depend.cpp

Go to the documentation of this file.
00001 /* $Id: depend.cpp 26060 2013-11-23 13:16:45Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <ctype.h>
00027 #include <limits.h>
00028 #include <unistd.h>
00029 #include <map>
00030 #include <set>
00031 #include <stack>
00032 #include <cassert>
00033 
00042 #define lengthof(x) (sizeof(x) / sizeof(x[0]))
00043 
00050 #define lastof(x) (&x[lengthof(x) - 1])
00051 
00068 char *strecpy(char *dst, const char *src, const char *last)
00069 {
00070   assert(dst <= last);
00071   while (dst != last && *src != '\0') {
00072     *dst++ = *src++;
00073   }
00074   *dst = '\0';
00075 
00076   if (dst == last && *src != '\0') {
00077     fprintf(stderr, "String too long for destination buffer\n");
00078     exit(-3);
00079   }
00080   return dst;
00081 }
00082 
00099 static char *strecat(char *dst, const char *src, const char *last)
00100 {
00101   assert(dst <= last);
00102   while (*dst != '\0') {
00103     if (dst == last) return dst;
00104     dst++;
00105   }
00106 
00107   return strecpy(dst, src, last);
00108 }
00109 
00114 static inline void free(const void *ptr)
00115 {
00116   free(const_cast<void *>(ptr));
00117 }
00118 
00119 #ifndef PATH_MAX
00120 
00121 # define PATH_MAX 260
00122 #endif
00123 
00125 struct StringCompare {
00132   bool operator () (const char *a, const char *b) const
00133   {
00134     return strcmp(a, b) < 0;
00135   }
00136 };
00138 typedef std::set<const char*, StringCompare> StringSet;
00140 typedef std::map<const char*, StringSet*, StringCompare> StringMap;
00142 typedef std::pair<const char*, StringSet*> StringMapItem;
00143 
00145 static StringSet _include_dirs;
00147 static StringMap _files;
00149 static StringMap _headers;
00151 static StringSet _defines;
00152 
00156 class File {
00157 public:
00163   File(const char *filename)
00164   {
00165     this->fp = fopen(filename, "r");
00166     if (this->fp == NULL) {
00167       fprintf(stdout, "Could not open %s for reading\n", filename);
00168       exit(1);
00169     }
00170     this->dirname = strdup(filename);
00171     char *last = strrchr(this->dirname, '/');
00172     if (last != NULL) {
00173       *last = '\0';
00174     } else {
00175       *this->dirname = '\0';
00176     }
00177   }
00178 
00180   ~File()
00181   {
00182     fclose(this->fp);
00183     free(this->dirname);
00184   }
00185 
00191   char GetChar() const
00192   {
00193     int c = fgetc(this->fp);
00194     return (c == EOF) ? '\0' : c;
00195   }
00196 
00201   const char *GetDirname() const
00202   {
00203     return this->dirname;
00204   }
00205 
00206 private:
00207   FILE *fp;             
00208   char *dirname;        
00209 };
00210 
00212 enum Token {
00213   TOKEN_UNKNOWN,    
00214   TOKEN_END,        
00215   TOKEN_EOL,        
00216   TOKEN_SHARP,      
00217   TOKEN_LOCAL,      
00218   TOKEN_GLOBAL,     
00219   TOKEN_IDENTIFIER, 
00220   TOKEN_DEFINE,     
00221   TOKEN_IF,         
00222   TOKEN_IFDEF,      
00223   TOKEN_IFNDEF,     
00224   TOKEN_ELIF,       
00225   TOKEN_ELSE,       
00226   TOKEN_ENDIF,      
00227   TOKEN_UNDEF,      
00228   TOKEN_OR,         
00229   TOKEN_AND,        
00230   TOKEN_DEFINED,    
00231   TOKEN_OPEN,       
00232   TOKEN_CLOSE,      
00233   TOKEN_NOT,        
00234   TOKEN_ZERO,       
00235   TOKEN_INCLUDE,    
00236 };
00237 
00239 typedef std::map<const char*, Token, StringCompare> KeywordList;
00240 
00244 class Lexer {
00245 public:
00250   Lexer(const File *file) : file(file), current_char('\0'), string(NULL), token(TOKEN_UNKNOWN)
00251   {
00252     this->keywords["define"]  = TOKEN_DEFINE;
00253     this->keywords["defined"] = TOKEN_DEFINED;
00254     this->keywords["if"]      = TOKEN_IF;
00255     this->keywords["ifdef"]   = TOKEN_IFDEF;
00256     this->keywords["ifndef"]  = TOKEN_IFNDEF;
00257     this->keywords["include"] = TOKEN_INCLUDE;
00258     this->keywords["elif"]    = TOKEN_ELIF;
00259     this->keywords["else"]    = TOKEN_ELSE;
00260     this->keywords["endif"]   = TOKEN_ENDIF;
00261     this->keywords["undef"]   = TOKEN_UNDEF;
00262 
00263     /* Initialise currently read character. */
00264     this->Next();
00265 
00266     /* Allocate the buffer. */
00267     this->buf_len = 32;
00268     this->buf = (char*)malloc(sizeof(*this->buf) * this->buf_len);
00269   }
00270 
00272   ~Lexer()
00273   {
00274     free(this->buf);
00275   }
00276 
00280   void Next()
00281   {
00282     this->current_char = this->file->GetChar();
00283   }
00284 
00289   Token GetToken() const
00290   {
00291     return this->token;
00292   }
00293 
00298   const char *GetString() const
00299   {
00300     return this->string;
00301   }
00302 
00307   void Lex()
00308   {
00309     for (;;) {
00310       free(this->string);
00311       this->string = NULL;
00312       this->token  = TOKEN_UNKNOWN;
00313 
00314       switch (this->current_char) {
00315         /* '\0' means End-Of-File */
00316         case '\0': this->token = TOKEN_END; return;
00317 
00318         /* Skip some chars, as they don't do anything */
00319         case '\t': this->Next(); break;
00320         case '\r': this->Next(); break;
00321         case ' ':  this->Next(); break;
00322 
00323         case '\\':
00324           this->Next();
00325           if (this->current_char == '\n') this->Next();
00326           break;
00327 
00328         case '\n':
00329           this->token = TOKEN_EOL;
00330           this->Next();
00331           return;
00332 
00333         case '#':
00334           this->token = TOKEN_SHARP;
00335           this->Next();
00336           return;
00337 
00338         case '"':
00339           this->ReadString('"', TOKEN_LOCAL);
00340           this->Next();
00341           return;
00342 
00343         case '<':
00344           this->ReadString('>', TOKEN_GLOBAL);
00345           this->Next();
00346           return;
00347 
00348         case '&':
00349           this->Next();
00350           if (this->current_char == '&') {
00351             this->Next();
00352             this->token = TOKEN_AND;
00353             return;
00354           }
00355           break;
00356 
00357         case '|':
00358           this->Next();
00359           if (this->current_char == '|') {
00360             this->Next();
00361             this->token = TOKEN_OR;
00362             return;
00363           }
00364           break;
00365 
00366         case '(':
00367           this->Next();
00368           this->token = TOKEN_OPEN;
00369           return;
00370 
00371         case ')':
00372           this->Next();
00373           this->token = TOKEN_CLOSE;
00374           return;
00375 
00376         case '!':
00377           this->Next();
00378           if (this->current_char != '=') {
00379             this->token = TOKEN_NOT;
00380             return;
00381           }
00382           break;
00383 
00384         /* Possible begin of comment */
00385         case '/':
00386           this->Next();
00387           switch (this->current_char) {
00388             case '*': {
00389               this->Next();
00390               char previous_char = '\0';
00391               while ((this->current_char != '/' || previous_char != '*') && this->current_char != '\0') {
00392                 previous_char = this->current_char;
00393                 this->Next();
00394               }
00395               this->Next();
00396               break;
00397             }
00398             case '/': while (this->current_char != '\n' && this->current_char != '\0') this->Next(); break;
00399             default: break;
00400           }
00401           break;
00402 
00403         default:
00404           if (isalpha(this->current_char) || this->current_char == '_') {
00405             /* If the name starts with a letter, it is an identifier */
00406             this->ReadIdentifier();
00407             return;
00408           }
00409           if (isdigit(this->current_char)) {
00410             bool zero = this->current_char == '0';
00411             this->Next();
00412             if (this->current_char == 'x' || this->current_char == 'X') Next();
00413             while (isdigit(this->current_char) || this->current_char == '.' || (this->current_char >= 'a' && this->current_char <= 'f') || (this->current_char >= 'A' && this->current_char <= 'F')) {
00414               zero &= this->current_char == '0';
00415               this->Next();
00416             }
00417             if (zero) this->token = TOKEN_ZERO;
00418             return;
00419           }
00420           this->Next();
00421           break;
00422       }
00423     }
00424   }
00425 
00426 private:
00432   Token FindKeyword(const char *name) const
00433   {
00434     KeywordList::const_iterator it = this->keywords.find(name);
00435     if (it == this->keywords.end()) return TOKEN_IDENTIFIER;
00436     return (*it).second;
00437   }
00438 
00442   void ReadIdentifier()
00443   {
00444     size_t count = 0;
00445 
00446     /* Read the rest of the identifier */
00447     do {
00448       this->buf[count++] = this->current_char;
00449       this->Next();
00450 
00451       if (count >= buf_len) {
00452         /* Scale the buffer if required */
00453         this->buf_len *= 2;
00454         this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
00455       }
00456     } while ((isalpha(this->current_char) || this->current_char == '_' || isdigit(this->current_char)));
00457     this->buf[count] = '\0';
00458 
00459     free(this->string);
00460     this->string = strdup(this->buf);
00461     this->token = FindKeyword(this->string);
00462   }
00463 
00469   void ReadString(char end, Token token)
00470   {
00471     size_t count = 0;
00472     this->Next();
00473     while (this->current_char != end && this->current_char != ')' && this->current_char != '\n' && this->current_char != '\0') {
00474       this->buf[count++] = this->current_char;
00475       this->Next();
00476 
00477       if (count >= this->buf_len) {
00478         /* Scale the buffer if required */
00479         this->buf_len *= 2;
00480         this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
00481       }
00482     }
00483     this->buf[count] = '\0';
00484     free(this->string);
00485     this->string = strdup(this->buf);
00486     this->token = token;
00487   }
00488 
00489   const File *file;     
00490   char current_char;    
00491   char *string;         
00492   Token token;          
00493   char *buf;            
00494   size_t buf_len;       
00495   KeywordList keywords; 
00496 };
00497 
00508 const char *GeneratePath(const char *dirname, const char *filename, bool local)
00509 {
00510   if (local) {
00511     if (access(filename, R_OK) == 0) return strdup(filename);
00512 
00513     char path[PATH_MAX];
00514     strecpy(path, dirname, lastof(path));
00515     const char *p = filename;
00516     /* Remove '..' from the begin of the filename. */
00517     while (*p == '.') {
00518       if (*(++p) == '.') {
00519         char *s = strrchr(path, '/');
00520         if (s != NULL) *s = '\0';
00521         p += 2;
00522       }
00523     }
00524     strecat(path, "/", lastof(path));
00525     strecat(path, p, lastof(path));
00526 
00527     if (access(path, R_OK) == 0) return strdup(path);
00528   }
00529 
00530   for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
00531     char path[PATH_MAX];
00532     strecpy(path, *it, lastof(path));
00533     const char *p = filename;
00534     /* Remove '..' from the begin of the filename. */
00535     while (*p == '.') {
00536       if (*(++p) == '.') {
00537         char *s = strrchr(path, '/');
00538         if (s != NULL) *s = '\0';
00539         p += 2;
00540       }
00541     }
00542     strecat(path, "/", lastof(path));
00543     strecat(path, p, lastof(path));
00544 
00545     if (access(path, R_OK) == 0) return strdup(path);
00546   }
00547 
00548   return NULL;
00549 }
00550 
00558 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose);
00559 
00567 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose);
00568 
00577 bool ExpressionNot(Lexer *lexer, StringSet *defines, bool verbose)
00578 {
00579   if (lexer->GetToken() == TOKEN_NOT) {
00580     if (verbose) fprintf(stderr, "!");
00581     lexer->Lex();
00582     bool value = !ExpressionDefined(lexer, defines, verbose);
00583     if (verbose) fprintf(stderr, "[%d]", value);
00584     return value;
00585   }
00586 
00587   if (lexer->GetToken() == TOKEN_OPEN) {
00588     if (verbose) fprintf(stderr, "(");
00589     lexer->Lex();
00590     bool value = ExpressionOr(lexer, defines, verbose);
00591     if (verbose) fprintf(stderr, ")[%d]", value);
00592     lexer->Lex();
00593     return value;
00594   }
00595 
00596   if (lexer->GetToken() == TOKEN_ZERO) {
00597     if (verbose) fprintf(stderr, "0");
00598     lexer->Lex();
00599     if (verbose) fprintf(stderr, "[0]");
00600     return false;
00601   }
00602 
00603   bool first = true;
00604   while (lexer->GetToken() == TOKEN_UNKNOWN || lexer->GetToken() == TOKEN_IDENTIFIER) {
00605     if (verbose && first) fprintf(stderr, "<assumed true>");
00606     first = false;
00607     lexer->Lex();
00608   }
00609 
00610   return true;
00611 }
00612 
00620 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose)
00621 {
00622   bool value = ExpressionNot(lexer, defines, verbose);
00623 
00624   if (lexer->GetToken() != TOKEN_DEFINED) return value;
00625   lexer->Lex();
00626   if (verbose) fprintf(stderr, "defined");
00627   bool open = (lexer->GetToken() == TOKEN_OPEN);
00628   if (open) lexer->Lex();
00629   if (verbose) fprintf(stderr, open ? "(" : " ");
00630   if (lexer->GetToken() == TOKEN_IDENTIFIER) {
00631     if (verbose) fprintf(stderr, "%s", lexer->GetString());
00632     value = defines->find(lexer->GetString()) != defines->end();
00633   }
00634   if (open) {
00635     if (verbose) fprintf(stderr, ")");
00636     lexer->Lex();
00637   }
00638   lexer->Lex();
00639   if (verbose) fprintf(stderr, "[%d]", value);
00640   return value;
00641 }
00642 
00650 bool ExpressionAnd(Lexer *lexer, StringSet *defines, bool verbose)
00651 {
00652   bool value = ExpressionDefined(lexer, defines, verbose);
00653 
00654   for (;;) {
00655     if (lexer->GetToken() != TOKEN_AND) return value;
00656     if (verbose) fprintf(stderr, " && ");
00657     lexer->Lex();
00658     value = value && ExpressionDefined(lexer, defines, verbose);
00659   }
00660 }
00661 
00669 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
00670 {
00671   bool value = ExpressionAnd(lexer, defines, verbose);
00672 
00673   for (;;) {
00674     if (lexer->GetToken() != TOKEN_OR) return value;
00675     if (verbose) fprintf(stderr, " || ");
00676     lexer->Lex();
00677     value = value || ExpressionAnd(lexer, defines, verbose);
00678   }
00679 }
00680 
00682 enum Ignore {
00683   NOT_IGNORE,         
00684   IGNORE_UNTIL_ELSE,  
00685   IGNORE_UNTIL_ENDIF, 
00686 };
00687 
00695 void ScanFile(const char *filename, const char *ext, bool header, bool verbose)
00696 {
00697   static StringSet defines;
00698   static std::stack<Ignore> ignore;
00699   /* Copy in the default defines (parameters of depend) */
00700   if (!header) {
00701     for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
00702       defines.insert(strdup(*it));
00703     }
00704   }
00705 
00706   File file(filename);
00707   Lexer lexer(&file);
00708 
00709   /* Start the lexing! */
00710   lexer.Lex();
00711 
00712   while (lexer.GetToken() != TOKEN_END) {
00713     switch (lexer.GetToken()) {
00714       /* We reached the end of the file... yay, we're done! */
00715       case TOKEN_END: break;
00716 
00717       /* The line started with a # (minus whitespace) */
00718       case TOKEN_SHARP:
00719         lexer.Lex();
00720         switch (lexer.GetToken()) {
00721           case TOKEN_INCLUDE:
00722             if (verbose) fprintf(stderr, "%s #include ", filename);
00723             lexer.Lex();
00724             switch (lexer.GetToken()) {
00725               case TOKEN_LOCAL:
00726               case TOKEN_GLOBAL: {
00727                 if (verbose) fprintf(stderr, "%s", lexer.GetString());
00728                 if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
00729                   if (verbose) fprintf(stderr, " (ignored)");
00730                   break;
00731                 }
00732                 const char *h = GeneratePath(file.GetDirname(), lexer.GetString(), lexer.GetToken() == TOKEN_LOCAL);
00733                 if (h != NULL) {
00734                   StringMap::iterator it = _headers.find(h);
00735                   if (it == _headers.end()) {
00736                     it = (_headers.insert(StringMapItem(strdup(h), new StringSet()))).first;
00737                     if (verbose) fprintf(stderr, "\n");
00738                     ScanFile(h, ext, true, verbose);
00739                   }
00740                   StringMap::iterator curfile;
00741                   if (header) {
00742                     curfile = _headers.find(filename);
00743                   } else {
00744                     /* Replace the extension with the provided extension of '.o'. */
00745                     char path[PATH_MAX];
00746                     strecpy(path, filename, lastof(path));
00747                     *(strrchr(path, '.')) = '\0';
00748                     strecat(path, ext != NULL ? ext : ".o", lastof(path));
00749                     curfile = _files.find(path);
00750                     if (curfile == _files.end()) {
00751                       curfile = (_files.insert(StringMapItem(strdup(path), new StringSet()))).first;
00752                     }
00753                   }
00754                   if (it != _headers.end()) {
00755                     for (StringSet::iterator header = it->second->begin(); header != it->second->end(); header++) {
00756                       if (curfile->second->find(*header) == curfile->second->end()) curfile->second->insert(strdup(*header));
00757                     }
00758                   }
00759                   if (curfile->second->find(h) == curfile->second->end()) curfile->second->insert(strdup(h));
00760                   free(h);
00761                 }
00762               }
00763               /* FALL THROUGH */
00764               default: break;
00765             }
00766             break;
00767 
00768           case TOKEN_DEFINE:
00769             if (verbose) fprintf(stderr, "%s #define ", filename);
00770             lexer.Lex();
00771             if (lexer.GetToken() == TOKEN_IDENTIFIER) {
00772               if (verbose) fprintf(stderr, "%s", lexer.GetString());
00773               if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
00774                 if (verbose) fprintf(stderr, " (ignored)");
00775                 break;
00776               }
00777               if (defines.find(lexer.GetString()) == defines.end()) defines.insert(strdup(lexer.GetString()));
00778               lexer.Lex();
00779             }
00780             break;
00781 
00782           case TOKEN_UNDEF:
00783             if (verbose) fprintf(stderr, "%s #undef ", filename);
00784             lexer.Lex();
00785             if (lexer.GetToken() == TOKEN_IDENTIFIER) {
00786               if (verbose) fprintf(stderr, "%s", lexer.GetString());
00787               if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
00788                 if (verbose) fprintf(stderr, " (ignored)");
00789                 break;
00790               }
00791               StringSet::iterator it = defines.find(lexer.GetString());
00792               if (it != defines.end()) {
00793                 free(*it);
00794                 defines.erase(it);
00795               }
00796               lexer.Lex();
00797             }
00798             break;
00799 
00800           case TOKEN_ENDIF:
00801             if (verbose) fprintf(stderr, "%s #endif", filename);
00802             lexer.Lex();
00803             if (!ignore.empty()) ignore.pop();
00804             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00805             break;
00806 
00807           case TOKEN_ELSE: {
00808             if (verbose) fprintf(stderr, "%s #else", filename);
00809             lexer.Lex();
00810             Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
00811             if (!ignore.empty()) ignore.pop();
00812             if (ignore.empty() || ignore.top() == NOT_IGNORE) {
00813               ignore.push(last == IGNORE_UNTIL_ELSE ? NOT_IGNORE : IGNORE_UNTIL_ENDIF);
00814             } else {
00815               ignore.push(IGNORE_UNTIL_ENDIF);
00816             }
00817             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00818             break;
00819           }
00820 
00821           case TOKEN_ELIF: {
00822             if (verbose) fprintf(stderr, "%s #elif ", filename);
00823             lexer.Lex();
00824             Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
00825             if (!ignore.empty()) ignore.pop();
00826             if (ignore.empty() || ignore.top() == NOT_IGNORE) {
00827               bool value = ExpressionOr(&lexer, &defines, verbose);
00828               ignore.push(last == IGNORE_UNTIL_ELSE ? (value ? NOT_IGNORE : IGNORE_UNTIL_ELSE) : IGNORE_UNTIL_ENDIF);
00829             } else {
00830               ignore.push(IGNORE_UNTIL_ENDIF);
00831             }
00832             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00833             break;
00834           }
00835 
00836           case TOKEN_IF: {
00837             if (verbose) fprintf(stderr, "%s #if ", filename);
00838             lexer.Lex();
00839             if (ignore.empty() || ignore.top() == NOT_IGNORE) {
00840               bool value = ExpressionOr(&lexer, &defines, verbose);
00841               ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
00842             } else {
00843               ignore.push(IGNORE_UNTIL_ENDIF);
00844             }
00845             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00846             break;
00847           }
00848 
00849           case TOKEN_IFDEF:
00850             if (verbose) fprintf(stderr, "%s #ifdef ", filename);
00851             lexer.Lex();
00852             if (lexer.GetToken() == TOKEN_IDENTIFIER) {
00853               bool value = defines.find(lexer.GetString()) != defines.end();
00854               if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
00855               if (ignore.empty() || ignore.top() == NOT_IGNORE) {
00856                 ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
00857               } else {
00858                 ignore.push(IGNORE_UNTIL_ENDIF);
00859               }
00860             }
00861             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00862             break;
00863 
00864           case TOKEN_IFNDEF:
00865             if (verbose) fprintf(stderr, "%s #ifndef ", filename);
00866             lexer.Lex();
00867             if (lexer.GetToken() == TOKEN_IDENTIFIER) {
00868               bool value = defines.find(lexer.GetString()) != defines.end();
00869               if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
00870               if (ignore.empty() || ignore.top() == NOT_IGNORE) {
00871                 ignore.push(!value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
00872               } else {
00873                 ignore.push(IGNORE_UNTIL_ENDIF);
00874               }
00875             }
00876             if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
00877             break;
00878 
00879           default:
00880             if (verbose) fprintf(stderr, "%s #<unknown>", filename);
00881             lexer.Lex();
00882             break;
00883         }
00884         if (verbose) fprintf(stderr, "\n");
00885         /* FALL THROUGH */
00886       default:
00887         /* Ignore the rest of the garbage on this line */
00888         while (lexer.GetToken() != TOKEN_EOL && lexer.GetToken() != TOKEN_END) lexer.Lex();
00889         lexer.Lex();
00890         break;
00891     }
00892   }
00893 
00894   if (!header) {
00895     for (StringSet::iterator it = defines.begin(); it != defines.end(); it++) {
00896       free(*it);
00897     }
00898     defines.clear();
00899     while (!ignore.empty()) ignore.pop();
00900   }
00901 }
00902 
00909 int main(int argc, char *argv[])
00910 {
00911   bool ignorenext = true;
00912   char *filename = NULL;
00913   char *ext = NULL;
00914   char *delimiter = NULL;
00915   bool append = false;
00916   bool verbose = false;
00917 
00918   for (int i = 0; i < argc; i++) {
00919     if (ignorenext) {
00920       ignorenext = false;
00921       continue;
00922     }
00923     if (argv[i][0] == '-') {
00924       /* Append */
00925       if (strncmp(argv[i], "-a", 2) == 0) append = true;
00926       /* Include dir */
00927       if (strncmp(argv[i], "-I", 2) == 0) {
00928         if (argv[i][2] == '\0') {
00929           i++;
00930           _include_dirs.insert(strdup(argv[i]));
00931         } else {
00932           _include_dirs.insert(strdup(&argv[i][2]));
00933         }
00934         continue;
00935       }
00936       /* Define */
00937       if (strncmp(argv[i], "-D", 2) == 0) {
00938         char *p = strchr(argv[i], '=');
00939         if (p != NULL) *p = '\0';
00940         _defines.insert(strdup(&argv[i][2]));
00941         continue;
00942       }
00943       /* Output file */
00944       if (strncmp(argv[i], "-f", 2) == 0) {
00945         if (filename != NULL) continue;
00946         filename = strdup(&argv[i][2]);
00947         continue;
00948       }
00949       /* Object file extension */
00950       if (strncmp(argv[i], "-o", 2) == 0) {
00951         if (ext != NULL) continue;
00952         ext = strdup(&argv[i][2]);
00953         continue;
00954       }
00955       /* Starting string delimiter */
00956       if (strncmp(argv[i], "-s", 2) == 0) {
00957         if (delimiter != NULL) continue;
00958         delimiter = strdup(&argv[i][2]);
00959         continue;
00960       }
00961       /* Verbose */
00962       if (strncmp(argv[i], "-v", 2) == 0) verbose = true;
00963       continue;
00964     }
00965     ScanFile(argv[i], ext, false, verbose);
00966   }
00967 
00968   /* Default output file is Makefile */
00969   if (filename == NULL) filename = strdup("Makefile");
00970 
00971   /* Default delimiter string */
00972   if (delimiter == NULL) delimiter = strdup("# DO NOT DELETE");
00973 
00974   char backup[PATH_MAX];
00975   strecpy(backup, filename, lastof(backup));
00976   strecat(backup, ".bak", lastof(backup));
00977 
00978   char *content = NULL;
00979   long size = 0;
00980 
00981   /* Read in the current file; so we can overwrite everything from the
00982    * end of non-depend data marker down till the end. */
00983   FILE *src = fopen(filename, "rb");
00984   if (src != NULL) {
00985     fseek(src, 0, SEEK_END);
00986     if ((size = ftell(src)) < 0) {
00987       fprintf(stderr, "Could not read %s\n", filename);
00988       exit(-2);
00989     }
00990     rewind(src);
00991     content = (char*)malloc(size * sizeof(*content));
00992     if (fread(content, 1, size, src) != (size_t)size) {
00993       fprintf(stderr, "Could not read %s\n", filename);
00994       exit(-2);
00995     }
00996     fclose(src);
00997   }
00998 
00999   FILE *dst = fopen(filename, "w");
01000   bool found_delimiter = false;
01001 
01002   if (size != 0) {
01003     src = fopen(backup, "wb");
01004     if (fwrite(content, 1, size, src) != (size_t)size) {
01005       fprintf(stderr, "Could not write %s\n", filename);
01006       exit(-2);
01007     }
01008     fclose(src);
01009 
01010     /* Then append it to the real file. */
01011     src = fopen(backup, "rb");
01012     while (fgets(content, size, src) != NULL) {
01013       fputs(content, dst);
01014       if (!strncmp(content, delimiter, strlen(delimiter))) found_delimiter = true;
01015       if (!append && found_delimiter) break;
01016     }
01017     fclose(src);
01018   }
01019   if (!found_delimiter) fprintf(dst, "\n%s\n", delimiter);
01020 
01021   for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
01022     for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
01023       fprintf(dst, "%s: %s\n", it->first, *h);
01024     }
01025   }
01026 
01027   /* Clean up our mess. */
01028   fclose(dst);
01029 
01030   free(delimiter);
01031   free(filename);
01032   free(ext);
01033   free(content);
01034 
01035   for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
01036     for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
01037       free(*h);
01038     }
01039     it->second->clear();
01040     delete it->second;
01041     free(it->first);
01042   }
01043   _files.clear();
01044 
01045   for (StringMap::iterator it = _headers.begin(); it != _headers.end(); it++) {
01046     for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
01047       free(*h);
01048     }
01049     it->second->clear();
01050     delete it->second;
01051     free(it->first);
01052   }
01053   _headers.clear();
01054 
01055   for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
01056     free(*it);
01057   }
01058   _defines.clear();
01059 
01060   for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
01061     free(*it);
01062   }
01063   _include_dirs.clear();
01064 
01065   return 0;
01066 }