(svn r28004) -Update from Eints:
[openttd.git] / src / depend / depend.cpp
blobd4b102ddcfa29fd01f0b77e85ef2b0de610a8404
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /**
11 * @file depend/depend.cpp Custom implementation of Makedepend.
13 * We previously used makedepend, but that could not handle the amount of
14 * files we have and does not handle conditional includes in a sane manner.
15 * This caused many link problems because not enough files were recompiled.
16 * This has lead to the development of our own dependency generator. It is
17 * meant to be a substitute to the (relatively slow) dependency generation
18 * via gcc. It thus helps speeding up compilation. It will also ignore
19 * system headers making it less error prone when system headers are moved
20 * or renamed.
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <limits.h>
28 #include <unistd.h>
29 #include <map>
30 #include <set>
31 #include <stack>
32 #include <cassert>
34 /**
35 * Return the length of an fixed size array.
36 * Unlike sizeof this function returns the number of elements
37 * of the given type.
39 * @param x The pointer to the first element of the array
40 * @return The number of elements
42 #define lengthof(x) (sizeof(x) / sizeof(x[0]))
44 /**
45 * Get the last element of an fixed size array.
47 * @param x The pointer to the first element of the array
48 * @return The pointer to the last element of the array
50 #define lastof(x) (&x[lengthof(x) - 1])
52 /**
53 * Copies characters from one buffer to another.
55 * Copies the source string to the destination buffer with respect of the
56 * terminating null-character and the last pointer to the last element in
57 * the destination buffer. If the last pointer is set to NULL no boundary
58 * check is performed.
60 * @note usage: strecpy(dst, src, lastof(dst));
61 * @note lastof() applies only to fixed size arrays
63 * @param dst The destination buffer
64 * @param src The buffer containing the string to copy
65 * @param last The pointer to the last element of the destination buffer
66 * @return The pointer to the terminating null-character in the destination buffer
68 char *strecpy(char *dst, const char *src, const char *last)
70 assert(dst <= last);
71 while (dst != last && *src != '\0') {
72 *dst++ = *src++;
74 *dst = '\0';
76 if (dst == last && *src != '\0') {
77 fprintf(stderr, "String too long for destination buffer\n");
78 exit(-3);
80 return dst;
83 /**
84 * Appends characters from one string to another.
86 * Appends the source string to the destination string with respect of the
87 * terminating null-character and and the last pointer to the last element
88 * in the destination buffer. If the last pointer is set to NULL no
89 * boundary check is performed.
91 * @note usage: strecat(dst, src, lastof(dst));
92 * @note lastof() applies only to fixed size arrays
94 * @param dst The buffer containing the target string
95 * @param src The buffer containing the string to append
96 * @param last The pointer to the last element of the destination buffer
97 * @return The pointer to the terminating null-character in the destination buffer
99 static char *strecat(char *dst, const char *src, const char *last)
101 assert(dst <= last);
102 while (*dst != '\0') {
103 if (dst == last) return dst;
104 dst++;
107 return strecpy(dst, src, last);
111 * Version of the standard free that accepts const pointers.
112 * @param ptr The data to free.
114 static inline void free(const void *ptr)
116 free(const_cast<void *>(ptr));
119 #ifndef PATH_MAX
120 /** The maximum length of paths, if we don't know it. */
121 # define PATH_MAX 260
122 #endif
124 /** Simple string comparator using strcmp as implementation */
125 struct StringCompare {
127 * Compare a to b using strcmp.
128 * @param a string to compare.
129 * @param b string to compare.
130 * @return whether a is less than b.
132 bool operator () (const char *a, const char *b) const
134 return strcmp(a, b) < 0;
137 /** Set of C-style strings. */
138 typedef std::set<const char*, StringCompare> StringSet;
139 /** Mapping of C-style string to a set of C-style strings. */
140 typedef std::map<const char*, StringSet*, StringCompare> StringMap;
141 /** Pair of C-style string and a set of C-style strings. */
142 typedef std::pair<const char*, StringSet*> StringMapItem;
144 /** Include directory to search in. */
145 static StringSet _include_dirs;
146 /** Files that have been parsed/handled with their dependencies. */
147 static StringMap _files;
148 /** Dependencies of headers. */
149 static StringMap _headers;
150 /** The current 'active' defines. */
151 static StringSet _defines;
154 * Helper class to read a file.
156 class File {
157 public:
159 * Create the helper by opening the given file.
160 * @param filename the file to open
161 * @post the file is open; otherwise the application is killed.
163 File(const char *filename)
165 this->fp = fopen(filename, "r");
166 if (this->fp == NULL) {
167 fprintf(stdout, "Could not open %s for reading\n", filename);
168 exit(1);
170 this->dirname = strdup(filename);
171 char *last = strrchr(this->dirname, '/');
172 if (last != NULL) {
173 *last = '\0';
174 } else {
175 *this->dirname = '\0';
179 /** Free everything we have allocated. */
180 ~File()
182 fclose(this->fp);
183 free(this->dirname);
187 * Get a single character from the file.
188 * If we are reading beyond the end of the file '\0' is returned.
189 * @return the read character.
191 char GetChar() const
193 int c = fgetc(this->fp);
194 return (c == EOF) ? '\0' : c;
198 * Get the directory name of the file.
199 * @return the directory name.
201 const char *GetDirname() const
203 return this->dirname;
206 private:
207 FILE *fp; ///< The currently opened file.
208 char *dirname; ///< The directory of the file.
211 /** A token returned by the tokenizer. */
212 enum Token {
213 TOKEN_UNKNOWN, ///< Unknown token
214 TOKEN_END, ///< End of document
215 TOKEN_EOL, ///< End of line
216 TOKEN_SHARP, ///< # character, usually telling something important comes.
217 TOKEN_LOCAL, ///< Read a local include
218 TOKEN_GLOBAL, ///< Read a global include
219 TOKEN_IDENTIFIER, ///< Identifier within the data.
220 TOKEN_DEFINE, ///< (#)define in code
221 TOKEN_IF, ///< (#)if in code
222 TOKEN_IFDEF, ///< (#)ifdef in code
223 TOKEN_IFNDEF, ///< (#)ifndef in code
224 TOKEN_ELIF, ///< (#)elif in code
225 TOKEN_ELSE, ///< (#)else in code
226 TOKEN_ENDIF, ///< (#)endif in code
227 TOKEN_UNDEF, ///< (#)undef in code
228 TOKEN_OR, ///< '||' within <tt>#if</tt> expression
229 TOKEN_AND, ///< '&&' within <tt>#if</tt> expression
230 TOKEN_DEFINED, ///< 'defined' within <tt>#if</tt> expression
231 TOKEN_OPEN, ///< '(' within <tt>#if</tt> expression
232 TOKEN_CLOSE, ///< ')' within <tt>#if</tt> expression
233 TOKEN_NOT, ///< '!' within <tt>#if</tt> expression
234 TOKEN_ZERO, ///< '0' within <tt>#if</tt> expression
235 TOKEN_INCLUDE, ///< (#)include in code
238 /** Mapping from a C-style keyword representation to a Token. */
239 typedef std::map<const char*, Token, StringCompare> KeywordList;
242 * Lexer of a file.
244 class Lexer {
245 public:
247 * Create the lexer and fill the keywords table.
248 * @param file the file to read from.
250 Lexer(const File *file) : file(file), current_char('\0'), string(NULL), token(TOKEN_UNKNOWN)
252 this->keywords["define"] = TOKEN_DEFINE;
253 this->keywords["defined"] = TOKEN_DEFINED;
254 this->keywords["if"] = TOKEN_IF;
255 this->keywords["ifdef"] = TOKEN_IFDEF;
256 this->keywords["ifndef"] = TOKEN_IFNDEF;
257 this->keywords["include"] = TOKEN_INCLUDE;
258 this->keywords["elif"] = TOKEN_ELIF;
259 this->keywords["else"] = TOKEN_ELSE;
260 this->keywords["endif"] = TOKEN_ENDIF;
261 this->keywords["undef"] = TOKEN_UNDEF;
263 /* Initialise currently read character. */
264 this->Next();
266 /* Allocate the buffer. */
267 this->buf_len = 32;
268 this->buf = (char*)malloc(sizeof(*this->buf) * this->buf_len);
271 /** Free everything */
272 ~Lexer()
274 free(this->buf);
278 * Read the next character into 'current_char'.
280 void Next()
282 this->current_char = this->file->GetChar();
286 * Get the current token.
287 * @return the token.
289 Token GetToken() const
291 return this->token;
295 * Read the currenty processed string.
296 * @return the string, can be NULL.
298 const char *GetString() const
300 return this->string;
304 * Perform the lexing/tokenizing of the file till we can return something
305 * that must be parsed.
307 void Lex()
309 for (;;) {
310 free(this->string);
311 this->string = NULL;
312 this->token = TOKEN_UNKNOWN;
314 switch (this->current_char) {
315 /* '\0' means End-Of-File */
316 case '\0': this->token = TOKEN_END; return;
318 /* Skip some chars, as they don't do anything */
319 case '\t': this->Next(); break;
320 case '\r': this->Next(); break;
321 case ' ': this->Next(); break;
323 case '\\':
324 this->Next();
325 if (this->current_char == '\n') this->Next();
326 break;
328 case '\n':
329 this->token = TOKEN_EOL;
330 this->Next();
331 return;
333 case '#':
334 this->token = TOKEN_SHARP;
335 this->Next();
336 return;
338 case '"':
339 this->ReadString('"', TOKEN_LOCAL);
340 this->Next();
341 return;
343 case '<':
344 this->ReadString('>', TOKEN_GLOBAL);
345 this->Next();
346 return;
348 case '&':
349 this->Next();
350 if (this->current_char == '&') {
351 this->Next();
352 this->token = TOKEN_AND;
353 return;
355 break;
357 case '|':
358 this->Next();
359 if (this->current_char == '|') {
360 this->Next();
361 this->token = TOKEN_OR;
362 return;
364 break;
366 case '(':
367 this->Next();
368 this->token = TOKEN_OPEN;
369 return;
371 case ')':
372 this->Next();
373 this->token = TOKEN_CLOSE;
374 return;
376 case '!':
377 this->Next();
378 if (this->current_char != '=') {
379 this->token = TOKEN_NOT;
380 return;
382 break;
384 /* Possible begin of comment */
385 case '/':
386 this->Next();
387 switch (this->current_char) {
388 case '*': {
389 this->Next();
390 char previous_char = '\0';
391 while ((this->current_char != '/' || previous_char != '*') && this->current_char != '\0') {
392 previous_char = this->current_char;
393 this->Next();
395 this->Next();
396 break;
398 case '/': while (this->current_char != '\n' && this->current_char != '\0') this->Next(); break;
399 default: break;
401 break;
403 default:
404 if (isalpha(this->current_char) || this->current_char == '_') {
405 /* If the name starts with a letter, it is an identifier */
406 this->ReadIdentifier();
407 return;
409 if (isdigit(this->current_char)) {
410 bool zero = this->current_char == '0';
411 this->Next();
412 if (this->current_char == 'x' || this->current_char == 'X') Next();
413 while (isdigit(this->current_char) || this->current_char == '.' || (this->current_char >= 'a' && this->current_char <= 'f') || (this->current_char >= 'A' && this->current_char <= 'F')) {
414 zero &= this->current_char == '0';
415 this->Next();
417 if (zero) this->token = TOKEN_ZERO;
418 return;
420 this->Next();
421 break;
426 private:
428 * The token based on keyword with a given name.
429 * @param name the actual keyword.
430 * @return the token of the keyword.
432 Token FindKeyword(const char *name) const
434 KeywordList::const_iterator it = this->keywords.find(name);
435 if (it == this->keywords.end()) return TOKEN_IDENTIFIER;
436 return (*it).second;
440 * Read an identifier.
442 void ReadIdentifier()
444 size_t count = 0;
446 /* Read the rest of the identifier */
447 do {
448 this->buf[count++] = this->current_char;
449 this->Next();
451 if (count >= buf_len) {
452 /* Scale the buffer if required */
453 this->buf_len *= 2;
454 this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
456 } while ((isalpha(this->current_char) || this->current_char == '_' || isdigit(this->current_char)));
457 this->buf[count] = '\0';
459 free(this->string);
460 this->string = strdup(this->buf);
461 this->token = FindKeyword(this->string);
465 * Read a string up to a given character, then set the given token.
466 * @param end the 'marker' for the end of the string.
467 * @param token the token to set after returning.
469 void ReadString(char end, Token token)
471 size_t count = 0;
472 this->Next();
473 while (this->current_char != end && this->current_char != ')' && this->current_char != '\n' && this->current_char != '\0') {
474 this->buf[count++] = this->current_char;
475 this->Next();
477 if (count >= this->buf_len) {
478 /* Scale the buffer if required */
479 this->buf_len *= 2;
480 this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
483 this->buf[count] = '\0';
484 free(this->string);
485 this->string = strdup(this->buf);
486 this->token = token;
489 const File *file; ///< The file to read from.
490 char current_char; ///< The current character to process.
491 char *string; ///< Currently processed string.
492 Token token; ///< The current token to process.
493 char *buf; ///< Temporary buffer.
494 size_t buf_len; ///< Length of the temporary buffer.
495 KeywordList keywords; ///< All keywords we know of.
499 * Generate a path from a directory name and a relative filename.
500 * If the file is not local the include directory names will be used instead
501 * of the passed parameter with directory name. If the file is local both will
502 * be queried where the parameter takes precedence.
503 * @param dirname the directory to look in.
504 * @param filename the file to look for.
505 * @param local whether to look locally (in dirname) for the file.
506 * @return the absolute path, or NULL if the file doesn't exist.
508 const char *GeneratePath(const char *dirname, const char *filename, bool local)
510 if (local) {
511 if (access(filename, R_OK) == 0) return strdup(filename);
513 char path[PATH_MAX];
514 strecpy(path, dirname, lastof(path));
515 const char *p = filename;
516 /* Remove '..' from the begin of the filename. */
517 while (*p == '.') {
518 if (*(++p) == '.') {
519 char *s = strrchr(path, '/');
520 if (s != NULL) *s = '\0';
521 p += 2;
524 strecat(path, "/", lastof(path));
525 strecat(path, p, lastof(path));
527 if (access(path, R_OK) == 0) return strdup(path);
530 for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
531 char path[PATH_MAX];
532 strecpy(path, *it, lastof(path));
533 const char *p = filename;
534 /* Remove '..' from the begin of the filename. */
535 while (*p == '.') {
536 if (*(++p) == '.') {
537 char *s = strrchr(path, '/');
538 if (s != NULL) *s = '\0';
539 p += 2;
542 strecat(path, "/", lastof(path));
543 strecat(path, p, lastof(path));
545 if (access(path, R_OK) == 0) return strdup(path);
548 return NULL;
552 * Try to parse a 'defined(expr)' expression.
553 * @param lexer the lexer to get tokens from.
554 * @param defines the set of known defines.
555 * @param verbose whether to give verbose debugging information.
556 * @return the value of the expression.
558 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose);
561 * Try to parse a 'expr || expr' expression.
562 * @param lexer the lexer to get tokens from.
563 * @param defines the set of known defines.
564 * @param verbose whether to give verbose debugging information.
565 * @return the value of the expression.
567 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose);
570 * Try to parse a '!expr' expression. Also parses the '(expr)', '0' and
571 * identifiers. Finally it also consumes any unknown tokens.
572 * @param lexer the lexer to get tokens from.
573 * @param defines the set of known defines.
574 * @param verbose whether to give verbose debugging information.
575 * @return the value of the expression.
577 bool ExpressionNot(Lexer *lexer, StringSet *defines, bool verbose)
579 if (lexer->GetToken() == TOKEN_NOT) {
580 if (verbose) fprintf(stderr, "!");
581 lexer->Lex();
582 bool value = !ExpressionDefined(lexer, defines, verbose);
583 if (verbose) fprintf(stderr, "[%d]", value);
584 return value;
587 if (lexer->GetToken() == TOKEN_OPEN) {
588 if (verbose) fprintf(stderr, "(");
589 lexer->Lex();
590 bool value = ExpressionOr(lexer, defines, verbose);
591 if (verbose) fprintf(stderr, ")[%d]", value);
592 lexer->Lex();
593 return value;
596 if (lexer->GetToken() == TOKEN_ZERO) {
597 if (verbose) fprintf(stderr, "0");
598 lexer->Lex();
599 if (verbose) fprintf(stderr, "[0]");
600 return false;
603 bool first = true;
604 while (lexer->GetToken() == TOKEN_UNKNOWN || lexer->GetToken() == TOKEN_IDENTIFIER) {
605 if (verbose && first) fprintf(stderr, "<assumed true>");
606 first = false;
607 lexer->Lex();
610 return true;
614 * Try to parse a 'defined(expr)' expression.
615 * @param lexer the lexer to get tokens from.
616 * @param defines the set of known defines.
617 * @param verbose whether to give verbose debugging information.
618 * @return the value of the expression.
620 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose)
622 bool value = ExpressionNot(lexer, defines, verbose);
624 if (lexer->GetToken() != TOKEN_DEFINED) return value;
625 lexer->Lex();
626 if (verbose) fprintf(stderr, "defined");
627 bool open = (lexer->GetToken() == TOKEN_OPEN);
628 if (open) lexer->Lex();
629 if (verbose) fprintf(stderr, open ? "(" : " ");
630 if (lexer->GetToken() == TOKEN_IDENTIFIER) {
631 if (verbose) fprintf(stderr, "%s", lexer->GetString());
632 value = defines->find(lexer->GetString()) != defines->end();
634 if (open) {
635 if (verbose) fprintf(stderr, ")");
636 lexer->Lex();
638 lexer->Lex();
639 if (verbose) fprintf(stderr, "[%d]", value);
640 return value;
644 * Try to parse a 'expr && expr' expression.
645 * @param lexer the lexer to get tokens from.
646 * @param defines the set of known defines.
647 * @param verbose whether to give verbose debugging information.
648 * @return the value of the expression.
650 bool ExpressionAnd(Lexer *lexer, StringSet *defines, bool verbose)
652 bool value = ExpressionDefined(lexer, defines, verbose);
654 for (;;) {
655 if (lexer->GetToken() != TOKEN_AND) return value;
656 if (verbose) fprintf(stderr, " && ");
657 lexer->Lex();
658 value = value && ExpressionDefined(lexer, defines, verbose);
663 * Try to parse a 'expr || expr' expression.
664 * @param lexer the lexer to get tokens from.
665 * @param defines the set of known defines.
666 * @param verbose whether to give verbose debugging information.
667 * @return the value of the expression.
669 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
671 bool value = ExpressionAnd(lexer, defines, verbose);
673 for (;;) {
674 if (lexer->GetToken() != TOKEN_OR) return value;
675 if (verbose) fprintf(stderr, " || ");
676 lexer->Lex();
677 value = value || ExpressionAnd(lexer, defines, verbose);
681 /** Enumerator to tell how long to ignore 'stuff'. */
682 enum Ignore {
683 NOT_IGNORE, ///< No ignoring.
684 IGNORE_UNTIL_ELSE, ///< Ignore till a #else is reached.
685 IGNORE_UNTIL_ENDIF, ///< Ignore till a #endif is reached.
689 * Scan a file for includes, defines and the lot.
690 * @param filename the name of the file to scan.
691 * @param ext the extension of the filename.
692 * @param header whether the file is a header or not.
693 * @param verbose whether to give verbose debugging information.
695 void ScanFile(const char *filename, const char *ext, bool header, bool verbose)
697 static StringSet defines;
698 static std::stack<Ignore> ignore;
699 /* Copy in the default defines (parameters of depend) */
700 if (!header) {
701 for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
702 defines.insert(strdup(*it));
706 File file(filename);
707 Lexer lexer(&file);
709 /* Start the lexing! */
710 lexer.Lex();
712 while (lexer.GetToken() != TOKEN_END) {
713 switch (lexer.GetToken()) {
714 /* We reached the end of the file... yay, we're done! */
715 case TOKEN_END: break;
717 /* The line started with a # (minus whitespace) */
718 case TOKEN_SHARP:
719 lexer.Lex();
720 switch (lexer.GetToken()) {
721 case TOKEN_INCLUDE:
722 if (verbose) fprintf(stderr, "%s #include ", filename);
723 lexer.Lex();
724 switch (lexer.GetToken()) {
725 case TOKEN_LOCAL:
726 case TOKEN_GLOBAL: {
727 if (verbose) fprintf(stderr, "%s", lexer.GetString());
728 if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
729 if (verbose) fprintf(stderr, " (ignored)");
730 break;
732 const char *h = GeneratePath(file.GetDirname(), lexer.GetString(), lexer.GetToken() == TOKEN_LOCAL);
733 if (h != NULL) {
734 StringMap::iterator it = _headers.find(h);
735 if (it == _headers.end()) {
736 it = (_headers.insert(StringMapItem(strdup(h), new StringSet()))).first;
737 if (verbose) fprintf(stderr, "\n");
738 ScanFile(h, ext, true, verbose);
740 StringMap::iterator curfile;
741 if (header) {
742 curfile = _headers.find(filename);
743 } else {
744 /* Replace the extension with the provided extension of '.o'. */
745 char path[PATH_MAX];
746 strecpy(path, filename, lastof(path));
747 *(strrchr(path, '.')) = '\0';
748 strecat(path, ext != NULL ? ext : ".o", lastof(path));
749 curfile = _files.find(path);
750 if (curfile == _files.end()) {
751 curfile = (_files.insert(StringMapItem(strdup(path), new StringSet()))).first;
754 if (it != _headers.end()) {
755 for (StringSet::iterator header = it->second->begin(); header != it->second->end(); header++) {
756 if (curfile->second->find(*header) == curfile->second->end()) curfile->second->insert(strdup(*header));
759 if (curfile->second->find(h) == curfile->second->end()) curfile->second->insert(strdup(h));
760 free(h);
763 /* FALL THROUGH */
764 default: break;
766 break;
768 case TOKEN_DEFINE:
769 if (verbose) fprintf(stderr, "%s #define ", filename);
770 lexer.Lex();
771 if (lexer.GetToken() == TOKEN_IDENTIFIER) {
772 if (verbose) fprintf(stderr, "%s", lexer.GetString());
773 if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
774 if (verbose) fprintf(stderr, " (ignored)");
775 break;
777 if (defines.find(lexer.GetString()) == defines.end()) defines.insert(strdup(lexer.GetString()));
778 lexer.Lex();
780 break;
782 case TOKEN_UNDEF:
783 if (verbose) fprintf(stderr, "%s #undef ", filename);
784 lexer.Lex();
785 if (lexer.GetToken() == TOKEN_IDENTIFIER) {
786 if (verbose) fprintf(stderr, "%s", lexer.GetString());
787 if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
788 if (verbose) fprintf(stderr, " (ignored)");
789 break;
791 StringSet::iterator it = defines.find(lexer.GetString());
792 if (it != defines.end()) {
793 free(*it);
794 defines.erase(it);
796 lexer.Lex();
798 break;
800 case TOKEN_ENDIF:
801 if (verbose) fprintf(stderr, "%s #endif", filename);
802 lexer.Lex();
803 if (!ignore.empty()) ignore.pop();
804 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
805 break;
807 case TOKEN_ELSE: {
808 if (verbose) fprintf(stderr, "%s #else", filename);
809 lexer.Lex();
810 Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
811 if (!ignore.empty()) ignore.pop();
812 if (ignore.empty() || ignore.top() == NOT_IGNORE) {
813 ignore.push(last == IGNORE_UNTIL_ELSE ? NOT_IGNORE : IGNORE_UNTIL_ENDIF);
814 } else {
815 ignore.push(IGNORE_UNTIL_ENDIF);
817 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
818 break;
821 case TOKEN_ELIF: {
822 if (verbose) fprintf(stderr, "%s #elif ", filename);
823 lexer.Lex();
824 Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
825 if (!ignore.empty()) ignore.pop();
826 if (ignore.empty() || ignore.top() == NOT_IGNORE) {
827 bool value = ExpressionOr(&lexer, &defines, verbose);
828 ignore.push(last == IGNORE_UNTIL_ELSE ? (value ? NOT_IGNORE : IGNORE_UNTIL_ELSE) : IGNORE_UNTIL_ENDIF);
829 } else {
830 ignore.push(IGNORE_UNTIL_ENDIF);
832 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
833 break;
836 case TOKEN_IF: {
837 if (verbose) fprintf(stderr, "%s #if ", filename);
838 lexer.Lex();
839 if (ignore.empty() || ignore.top() == NOT_IGNORE) {
840 bool value = ExpressionOr(&lexer, &defines, verbose);
841 ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
842 } else {
843 ignore.push(IGNORE_UNTIL_ENDIF);
845 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
846 break;
849 case TOKEN_IFDEF:
850 if (verbose) fprintf(stderr, "%s #ifdef ", filename);
851 lexer.Lex();
852 if (lexer.GetToken() == TOKEN_IDENTIFIER) {
853 bool value = defines.find(lexer.GetString()) != defines.end();
854 if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
855 if (ignore.empty() || ignore.top() == NOT_IGNORE) {
856 ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
857 } else {
858 ignore.push(IGNORE_UNTIL_ENDIF);
861 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
862 break;
864 case TOKEN_IFNDEF:
865 if (verbose) fprintf(stderr, "%s #ifndef ", filename);
866 lexer.Lex();
867 if (lexer.GetToken() == TOKEN_IDENTIFIER) {
868 bool value = defines.find(lexer.GetString()) != defines.end();
869 if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
870 if (ignore.empty() || ignore.top() == NOT_IGNORE) {
871 ignore.push(!value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
872 } else {
873 ignore.push(IGNORE_UNTIL_ENDIF);
876 if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
877 break;
879 default:
880 if (verbose) fprintf(stderr, "%s #<unknown>", filename);
881 lexer.Lex();
882 break;
884 if (verbose) fprintf(stderr, "\n");
885 /* FALL THROUGH */
886 default:
887 /* Ignore the rest of the garbage on this line */
888 while (lexer.GetToken() != TOKEN_EOL && lexer.GetToken() != TOKEN_END) lexer.Lex();
889 lexer.Lex();
890 break;
894 if (!header) {
895 for (StringSet::iterator it = defines.begin(); it != defines.end(); it++) {
896 free(*it);
898 defines.clear();
899 while (!ignore.empty()) ignore.pop();
904 * Entry point. Arguably the most common function in all applications.
905 * @param argc the number of arguments.
906 * @param argv the actual arguments.
907 * @return return value for the caller to tell we succeed or not.
909 int main(int argc, char *argv[])
911 bool ignorenext = true;
912 char *filename = NULL;
913 char *ext = NULL;
914 char *delimiter = NULL;
915 bool append = false;
916 bool verbose = false;
918 for (int i = 0; i < argc; i++) {
919 if (ignorenext) {
920 ignorenext = false;
921 continue;
923 if (argv[i][0] == '-') {
924 /* Append */
925 if (strncmp(argv[i], "-a", 2) == 0) append = true;
926 /* Include dir */
927 if (strncmp(argv[i], "-I", 2) == 0) {
928 if (argv[i][2] == '\0') {
929 i++;
930 _include_dirs.insert(strdup(argv[i]));
931 } else {
932 _include_dirs.insert(strdup(&argv[i][2]));
934 continue;
936 /* Define */
937 if (strncmp(argv[i], "-D", 2) == 0) {
938 char *p = strchr(argv[i], '=');
939 if (p != NULL) *p = '\0';
940 _defines.insert(strdup(&argv[i][2]));
941 continue;
943 /* Output file */
944 if (strncmp(argv[i], "-f", 2) == 0) {
945 if (filename != NULL) continue;
946 filename = strdup(&argv[i][2]);
947 continue;
949 /* Object file extension */
950 if (strncmp(argv[i], "-o", 2) == 0) {
951 if (ext != NULL) continue;
952 ext = strdup(&argv[i][2]);
953 continue;
955 /* Starting string delimiter */
956 if (strncmp(argv[i], "-s", 2) == 0) {
957 if (delimiter != NULL) continue;
958 delimiter = strdup(&argv[i][2]);
959 continue;
961 /* Verbose */
962 if (strncmp(argv[i], "-v", 2) == 0) verbose = true;
963 continue;
965 ScanFile(argv[i], ext, false, verbose);
968 /* Default output file is Makefile */
969 if (filename == NULL) filename = strdup("Makefile");
971 /* Default delimiter string */
972 if (delimiter == NULL) delimiter = strdup("# DO NOT DELETE");
974 char backup[PATH_MAX];
975 strecpy(backup, filename, lastof(backup));
976 strecat(backup, ".bak", lastof(backup));
978 char *content = NULL;
979 long size = 0;
981 /* Read in the current file; so we can overwrite everything from the
982 * end of non-depend data marker down till the end. */
983 FILE *src = fopen(filename, "rb");
984 if (src != NULL) {
985 fseek(src, 0, SEEK_END);
986 if ((size = ftell(src)) < 0) {
987 fprintf(stderr, "Could not read %s\n", filename);
988 exit(-2);
990 rewind(src);
991 content = (char*)malloc(size * sizeof(*content));
992 if (fread(content, 1, size, src) != (size_t)size) {
993 fprintf(stderr, "Could not read %s\n", filename);
994 exit(-2);
996 fclose(src);
999 FILE *dst = fopen(filename, "w");
1000 bool found_delimiter = false;
1002 if (size != 0) {
1003 src = fopen(backup, "wb");
1004 if (fwrite(content, 1, size, src) != (size_t)size) {
1005 fprintf(stderr, "Could not write %s\n", filename);
1006 exit(-2);
1008 fclose(src);
1010 /* Then append it to the real file. */
1011 src = fopen(backup, "rb");
1012 while (fgets(content, size, src) != NULL) {
1013 fputs(content, dst);
1014 if (!strncmp(content, delimiter, strlen(delimiter))) found_delimiter = true;
1015 if (!append && found_delimiter) break;
1017 fclose(src);
1019 if (!found_delimiter) fprintf(dst, "\n%s\n", delimiter);
1021 for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
1022 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1023 fprintf(dst, "%s: %s\n", it->first, *h);
1027 /* Clean up our mess. */
1028 fclose(dst);
1030 free(delimiter);
1031 free(filename);
1032 free(ext);
1033 free(content);
1035 for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
1036 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1037 free(*h);
1039 it->second->clear();
1040 delete it->second;
1041 free(it->first);
1043 _files.clear();
1045 for (StringMap::iterator it = _headers.begin(); it != _headers.end(); it++) {
1046 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1047 free(*h);
1049 it->second->clear();
1050 delete it->second;
1051 free(it->first);
1053 _headers.clear();
1055 for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
1056 free(*it);
1058 _defines.clear();
1060 for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
1061 free(*it);
1063 _include_dirs.clear();
1065 return 0;