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/>.
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
35 * Return the length of an fixed size array.
36 * Unlike sizeof this function returns the number of elements
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]))
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])
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
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
)
71 while (dst
!= last
&& *src
!= '\0') {
76 if (dst
== last
&& *src
!= '\0') {
77 fprintf(stderr
, "String too long for destination buffer\n");
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
)
102 while (*dst
!= '\0') {
103 if (dst
== last
) return 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
));
120 /** The maximum length of paths, if we don't know it. */
121 # define PATH_MAX 260
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.
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
);
170 this->dirname
= strdup(filename
);
171 char *last
= strrchr(this->dirname
, '/');
175 *this->dirname
= '\0';
179 /** Free everything we have allocated. */
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.
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
;
207 FILE *fp
; ///< The currently opened file.
208 char *dirname
; ///< The directory of the file.
211 /** A token returned by the tokenizer. */
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
;
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. */
266 /* Allocate the buffer. */
268 this->buf
= (char*)malloc(sizeof(*this->buf
) * this->buf_len
);
271 /** Free everything */
278 * Read the next character into 'current_char'.
282 this->current_char
= this->file
->GetChar();
286 * Get the current token.
289 Token
GetToken() const
295 * Read the currenty processed string.
296 * @return the string, can be NULL.
298 const char *GetString() const
304 * Perform the lexing/tokenizing of the file till we can return something
305 * that must be parsed.
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;
325 if (this->current_char
== '\n') this->Next();
329 this->token
= TOKEN_EOL
;
334 this->token
= TOKEN_SHARP
;
339 this->ReadString('"', TOKEN_LOCAL
);
344 this->ReadString('>', TOKEN_GLOBAL
);
350 if (this->current_char
== '&') {
352 this->token
= TOKEN_AND
;
359 if (this->current_char
== '|') {
361 this->token
= TOKEN_OR
;
368 this->token
= TOKEN_OPEN
;
373 this->token
= TOKEN_CLOSE
;
378 if (this->current_char
!= '=') {
379 this->token
= TOKEN_NOT
;
384 /* Possible begin of comment */
387 switch (this->current_char
) {
390 char previous_char
= '\0';
391 while ((this->current_char
!= '/' || previous_char
!= '*') && this->current_char
!= '\0') {
392 previous_char
= this->current_char
;
398 case '/': while (this->current_char
!= '\n' && this->current_char
!= '\0') this->Next(); break;
404 if (isalpha(this->current_char
) || this->current_char
== '_') {
405 /* If the name starts with a letter, it is an identifier */
406 this->ReadIdentifier();
409 if (isdigit(this->current_char
)) {
410 bool zero
= this->current_char
== '0';
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';
417 if (zero
) this->token
= TOKEN_ZERO
;
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
;
440 * Read an identifier.
442 void ReadIdentifier()
446 /* Read the rest of the identifier */
448 this->buf
[count
++] = this->current_char
;
451 if (count
>= buf_len
) {
452 /* Scale the buffer if required */
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';
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
)
473 while (this->current_char
!= end
&& this->current_char
!= ')' && this->current_char
!= '\n' && this->current_char
!= '\0') {
474 this->buf
[count
++] = this->current_char
;
477 if (count
>= this->buf_len
) {
478 /* Scale the buffer if required */
480 this->buf
= (char *)realloc(this->buf
, sizeof(*this->buf
) * this->buf_len
);
483 this->buf
[count
] = '\0';
485 this->string
= strdup(this->buf
);
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
)
511 if (access(filename
, R_OK
) == 0) return strdup(filename
);
514 strecpy(path
, dirname
, lastof(path
));
515 const char *p
= filename
;
516 /* Remove '..' from the begin of the filename. */
519 char *s
= strrchr(path
, '/');
520 if (s
!= NULL
) *s
= '\0';
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
++) {
532 strecpy(path
, *it
, lastof(path
));
533 const char *p
= filename
;
534 /* Remove '..' from the begin of the filename. */
537 char *s
= strrchr(path
, '/');
538 if (s
!= NULL
) *s
= '\0';
542 strecat(path
, "/", lastof(path
));
543 strecat(path
, p
, lastof(path
));
545 if (access(path
, R_OK
) == 0) return strdup(path
);
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
, "!");
582 bool value
= !ExpressionDefined(lexer
, defines
, verbose
);
583 if (verbose
) fprintf(stderr
, "[%d]", value
);
587 if (lexer
->GetToken() == TOKEN_OPEN
) {
588 if (verbose
) fprintf(stderr
, "(");
590 bool value
= ExpressionOr(lexer
, defines
, verbose
);
591 if (verbose
) fprintf(stderr
, ")[%d]", value
);
596 if (lexer
->GetToken() == TOKEN_ZERO
) {
597 if (verbose
) fprintf(stderr
, "0");
599 if (verbose
) fprintf(stderr
, "[0]");
604 while (lexer
->GetToken() == TOKEN_UNKNOWN
|| lexer
->GetToken() == TOKEN_IDENTIFIER
) {
605 if (verbose
&& first
) fprintf(stderr
, "<assumed 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
;
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();
635 if (verbose
) fprintf(stderr
, ")");
639 if (verbose
) fprintf(stderr
, "[%d]", 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
);
655 if (lexer
->GetToken() != TOKEN_AND
) return value
;
656 if (verbose
) fprintf(stderr
, " && ");
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
);
674 if (lexer
->GetToken() != TOKEN_OR
) return value
;
675 if (verbose
) fprintf(stderr
, " || ");
677 value
= value
|| ExpressionAnd(lexer
, defines
, verbose
);
681 /** Enumerator to tell how long to ignore 'stuff'. */
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) */
701 for (StringSet::iterator it
= _defines
.begin(); it
!= _defines
.end(); it
++) {
702 defines
.insert(strdup(*it
));
709 /* Start the lexing! */
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) */
720 switch (lexer
.GetToken()) {
722 if (verbose
) fprintf(stderr
, "%s #include ", filename
);
724 switch (lexer
.GetToken()) {
727 if (verbose
) fprintf(stderr
, "%s", lexer
.GetString());
728 if (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) {
729 if (verbose
) fprintf(stderr
, " (ignored)");
732 const char *h
= GeneratePath(file
.GetDirname(), lexer
.GetString(), lexer
.GetToken() == TOKEN_LOCAL
);
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
;
742 curfile
= _headers
.find(filename
);
744 /* Replace the extension with the provided extension of '.o'. */
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
));
769 if (verbose
) fprintf(stderr
, "%s #define ", filename
);
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)");
777 if (defines
.find(lexer
.GetString()) == defines
.end()) defines
.insert(strdup(lexer
.GetString()));
783 if (verbose
) fprintf(stderr
, "%s #undef ", filename
);
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)");
791 StringSet::iterator it
= defines
.find(lexer
.GetString());
792 if (it
!= defines
.end()) {
801 if (verbose
) fprintf(stderr
, "%s #endif", filename
);
803 if (!ignore
.empty()) ignore
.pop();
804 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
808 if (verbose
) fprintf(stderr
, "%s #else", filename
);
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
);
815 ignore
.push(IGNORE_UNTIL_ENDIF
);
817 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
822 if (verbose
) fprintf(stderr
, "%s #elif ", filename
);
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
);
830 ignore
.push(IGNORE_UNTIL_ENDIF
);
832 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
837 if (verbose
) fprintf(stderr
, "%s #if ", filename
);
839 if (ignore
.empty() || ignore
.top() == NOT_IGNORE
) {
840 bool value
= ExpressionOr(&lexer
, &defines
, verbose
);
841 ignore
.push(value
? NOT_IGNORE
: IGNORE_UNTIL_ELSE
);
843 ignore
.push(IGNORE_UNTIL_ENDIF
);
845 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
850 if (verbose
) fprintf(stderr
, "%s #ifdef ", filename
);
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
);
858 ignore
.push(IGNORE_UNTIL_ENDIF
);
861 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
865 if (verbose
) fprintf(stderr
, "%s #ifndef ", filename
);
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
);
873 ignore
.push(IGNORE_UNTIL_ENDIF
);
876 if (verbose
) fprintf(stderr
, " -> %signore", (!ignore
.empty() && ignore
.top() != NOT_IGNORE
) ? "" : "not ");
880 if (verbose
) fprintf(stderr
, "%s #<unknown>", filename
);
884 if (verbose
) fprintf(stderr
, "\n");
887 /* Ignore the rest of the garbage on this line */
888 while (lexer
.GetToken() != TOKEN_EOL
&& lexer
.GetToken() != TOKEN_END
) lexer
.Lex();
895 for (StringSet::iterator it
= defines
.begin(); it
!= defines
.end(); it
++) {
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
;
914 char *delimiter
= NULL
;
916 bool verbose
= false;
918 for (int i
= 0; i
< argc
; i
++) {
923 if (argv
[i
][0] == '-') {
925 if (strncmp(argv
[i
], "-a", 2) == 0) append
= true;
927 if (strncmp(argv
[i
], "-I", 2) == 0) {
928 if (argv
[i
][2] == '\0') {
930 _include_dirs
.insert(strdup(argv
[i
]));
932 _include_dirs
.insert(strdup(&argv
[i
][2]));
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]));
944 if (strncmp(argv
[i
], "-f", 2) == 0) {
945 if (filename
!= NULL
) continue;
946 filename
= strdup(&argv
[i
][2]);
949 /* Object file extension */
950 if (strncmp(argv
[i
], "-o", 2) == 0) {
951 if (ext
!= NULL
) continue;
952 ext
= strdup(&argv
[i
][2]);
955 /* Starting string delimiter */
956 if (strncmp(argv
[i
], "-s", 2) == 0) {
957 if (delimiter
!= NULL
) continue;
958 delimiter
= strdup(&argv
[i
][2]);
962 if (strncmp(argv
[i
], "-v", 2) == 0) verbose
= true;
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
;
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");
985 fseek(src
, 0, SEEK_END
);
986 if ((size
= ftell(src
)) < 0) {
987 fprintf(stderr
, "Could not read %s\n", filename
);
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
);
999 FILE *dst
= fopen(filename
, "w");
1000 bool found_delimiter
= false;
1003 src
= fopen(backup
, "wb");
1004 if (fwrite(content
, 1, size
, src
) != (size_t)size
) {
1005 fprintf(stderr
, "Could not write %s\n", filename
);
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;
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. */
1035 for (StringMap::iterator it
= _files
.begin(); it
!= _files
.end(); it
++) {
1036 for (StringSet::iterator h
= it
->second
->begin(); h
!= it
->second
->end(); h
++) {
1039 it
->second
->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
++) {
1049 it
->second
->clear();
1055 for (StringSet::iterator it
= _defines
.begin(); it
!= _defines
.end(); it
++) {
1060 for (StringSet::iterator it
= _include_dirs
.begin(); it
!= _include_dirs
.end(); it
++) {
1063 _include_dirs
.clear();