OpenTTD
depend.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <limits.h>
26 #include <unistd.h>
27 #include <map>
28 #include <set>
29 #include <stack>
30 #include <cassert>
31 
40 #define lengthof(x) (sizeof(x) / sizeof(x[0]))
41 
48 #define lastof(x) (&x[lengthof(x) - 1])
49 
66 char *strecpy(char *dst, const char *src, const char *last)
67 {
68  assert(dst <= last);
69  while (dst != last && *src != '\0') {
70  *dst++ = *src++;
71  }
72  *dst = '\0';
73 
74  if (dst == last && *src != '\0') {
75  fprintf(stderr, "String too long for destination buffer\n");
76  exit(-3);
77  }
78  return dst;
79 }
80 
97 static char *strecat(char *dst, const char *src, const char *last)
98 {
99  assert(dst <= last);
100  while (*dst != '\0') {
101  if (dst == last) return dst;
102  dst++;
103  }
104 
105  return strecpy(dst, src, last);
106 }
107 
108 #if defined(__CYGWIN__)
109 
114 char *
115 strdup (const char *s)
116 {
117  size_t len = strlen(s) + 1;
118  void *n = malloc(len);
119 
120  if (n == NULL) return NULL;
121  return (char *) memcpy(n, s, len);
122 }
123 #endif
124 
129 static inline void free(const void *ptr)
130 {
131  free(const_cast<void *>(ptr));
132 }
133 
134 #ifndef PATH_MAX
135 
136 # define PATH_MAX 260
137 #endif
138 
140 struct StringCompare {
147  bool operator () (const char *a, const char *b) const
148  {
149  return strcmp(a, b) < 0;
150  }
151 };
153 typedef std::set<const char*, StringCompare> StringSet;
155 typedef std::map<const char*, StringSet*, StringCompare> StringMap;
157 typedef std::pair<const char*, StringSet*> StringMapItem;
158 
167 
171 class File {
172 public:
178  File(const char *filename)
179  {
180  this->fp = fopen(filename, "r");
181  if (this->fp == nullptr) {
182  fprintf(stdout, "Could not open %s for reading\n", filename);
183  exit(1);
184  }
185  this->dirname = strdup(filename);
186  char *last = strrchr(this->dirname, '/');
187  if (last != nullptr) {
188  *last = '\0';
189  } else {
190  *this->dirname = '\0';
191  }
192  }
193 
196  {
197  fclose(this->fp);
198  free(this->dirname);
199  }
200 
206  char GetChar() const
207  {
208  int c = fgetc(this->fp);
209  return (c == EOF) ? '\0' : c;
210  }
211 
216  const char *GetDirname() const
217  {
218  return this->dirname;
219  }
220 
221 private:
222  FILE *fp;
223  char *dirname;
224 };
225 
227 enum Token {
251 };
252 
254 typedef std::map<const char*, Token, StringCompare> KeywordList;
255 
259 class Lexer {
260 public:
265  Lexer(const File *file) : file(file), current_char('\0'), string(nullptr), token(TOKEN_UNKNOWN)
266  {
267  this->keywords["define"] = TOKEN_DEFINE;
268  this->keywords["defined"] = TOKEN_DEFINED;
269  this->keywords["if"] = TOKEN_IF;
270  this->keywords["ifdef"] = TOKEN_IFDEF;
271  this->keywords["ifndef"] = TOKEN_IFNDEF;
272  this->keywords["include"] = TOKEN_INCLUDE;
273  this->keywords["elif"] = TOKEN_ELIF;
274  this->keywords["else"] = TOKEN_ELSE;
275  this->keywords["endif"] = TOKEN_ENDIF;
276  this->keywords["undef"] = TOKEN_UNDEF;
277 
278  /* Initialise currently read character. */
279  this->Next();
280 
281  /* Allocate the buffer. */
282  this->buf_len = 32;
283  this->buf = (char*)malloc(sizeof(*this->buf) * this->buf_len);
284  }
285 
288  {
289  free(this->buf);
290  }
291 
295  void Next()
296  {
297  this->current_char = this->file->GetChar();
298  }
299 
304  Token GetToken() const
305  {
306  return this->token;
307  }
308 
313  const char *GetString() const
314  {
315  return this->string;
316  }
317 
322  void Lex()
323  {
324  for (;;) {
325  free(this->string);
326  this->string = nullptr;
327  this->token = TOKEN_UNKNOWN;
328 
329  switch (this->current_char) {
330  /* '\0' means End-Of-File */
331  case '\0': this->token = TOKEN_END; return;
332 
333  /* Skip some chars, as they don't do anything */
334  case '\t': this->Next(); break;
335  case '\r': this->Next(); break;
336  case ' ': this->Next(); break;
337 
338  case '\\':
339  this->Next();
340  if (this->current_char == '\n') this->Next();
341  break;
342 
343  case '\n':
344  this->token = TOKEN_EOL;
345  this->Next();
346  return;
347 
348  case '#':
349  this->token = TOKEN_SHARP;
350  this->Next();
351  return;
352 
353  case '"':
354  this->ReadString('"', TOKEN_LOCAL);
355  this->Next();
356  return;
357 
358  case '<':
359  this->ReadString('>', TOKEN_GLOBAL);
360  this->Next();
361  return;
362 
363  case '&':
364  this->Next();
365  if (this->current_char == '&') {
366  this->Next();
367  this->token = TOKEN_AND;
368  return;
369  }
370  break;
371 
372  case '|':
373  this->Next();
374  if (this->current_char == '|') {
375  this->Next();
376  this->token = TOKEN_OR;
377  return;
378  }
379  break;
380 
381  case '(':
382  this->Next();
383  this->token = TOKEN_OPEN;
384  return;
385 
386  case ')':
387  this->Next();
388  this->token = TOKEN_CLOSE;
389  return;
390 
391  case '!':
392  this->Next();
393  if (this->current_char != '=') {
394  this->token = TOKEN_NOT;
395  return;
396  }
397  break;
398 
399  /* Possible begin of comment */
400  case '/':
401  this->Next();
402  switch (this->current_char) {
403  case '*': {
404  this->Next();
405  char previous_char = '\0';
406  while ((this->current_char != '/' || previous_char != '*') && this->current_char != '\0') {
407  previous_char = this->current_char;
408  this->Next();
409  }
410  this->Next();
411  break;
412  }
413  case '/': while (this->current_char != '\n' && this->current_char != '\0') this->Next(); break;
414  default: break;
415  }
416  break;
417 
418  default:
419  if (isalpha(this->current_char) || this->current_char == '_') {
420  /* If the name starts with a letter, it is an identifier */
421  this->ReadIdentifier();
422  return;
423  }
424  if (isdigit(this->current_char)) {
425  bool zero = this->current_char == '0';
426  this->Next();
427  if (this->current_char == 'x' || this->current_char == 'X') Next();
428  while (isdigit(this->current_char) || this->current_char == '.' || (this->current_char >= 'a' && this->current_char <= 'f') || (this->current_char >= 'A' && this->current_char <= 'F')) {
429  zero &= this->current_char == '0';
430  this->Next();
431  }
432  if (zero) this->token = TOKEN_ZERO;
433  return;
434  }
435  this->Next();
436  break;
437  }
438  }
439  }
440 
441 private:
447  Token FindKeyword(const char *name) const
448  {
449  KeywordList::const_iterator it = this->keywords.find(name);
450  if (it == this->keywords.end()) return TOKEN_IDENTIFIER;
451  return (*it).second;
452  }
453 
458  {
459  size_t count = 0;
460 
461  /* Read the rest of the identifier */
462  do {
463  this->buf[count++] = this->current_char;
464  this->Next();
465 
466  if (count >= buf_len) {
467  /* Scale the buffer if required */
468  this->buf_len *= 2;
469  this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
470  }
471  } while ((isalpha(this->current_char) || this->current_char == '_' || isdigit(this->current_char)));
472  this->buf[count] = '\0';
473 
474  free(this->string);
475  this->string = strdup(this->buf);
476  this->token = FindKeyword(this->string);
477  }
478 
484  void ReadString(char end, Token token)
485  {
486  size_t count = 0;
487  this->Next();
488  while (this->current_char != end && this->current_char != ')' && this->current_char != '\n' && this->current_char != '\0') {
489  this->buf[count++] = this->current_char;
490  this->Next();
491 
492  if (count >= this->buf_len) {
493  /* Scale the buffer if required */
494  this->buf_len *= 2;
495  this->buf = (char *)realloc(this->buf, sizeof(*this->buf) * this->buf_len);
496  }
497  }
498  this->buf[count] = '\0';
499  free(this->string);
500  this->string = strdup(this->buf);
501  this->token = token;
502  }
503 
504  const File *file;
506  char *string;
508  char *buf;
509  size_t buf_len;
511 };
512 
523 const char *GeneratePath(const char *dirname, const char *filename, bool local)
524 {
525  /* Ignore C++ standard library headers. */
526  if (strchr(filename, '.') == nullptr) return nullptr;
527 
528  if (local) {
529  if (access(filename, R_OK) == 0) return strdup(filename);
530 
531  char path[PATH_MAX];
532  strecpy(path, dirname, 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 != nullptr) *s = '\0';
539  p += 2;
540  }
541  }
542  strecat(path, "/", lastof(path));
543  strecat(path, p, lastof(path));
544 
545  if (access(path, R_OK) == 0) return strdup(path);
546  }
547 
548  for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
549  char path[PATH_MAX];
550  strecpy(path, *it, lastof(path));
551  const char *p = filename;
552  /* Remove '..' from the begin of the filename. */
553  while (*p == '.') {
554  if (*(++p) == '.') {
555  char *s = strrchr(path, '/');
556  if (s != nullptr) *s = '\0';
557  p += 2;
558  }
559  }
560  strecat(path, "/", lastof(path));
561  strecat(path, p, lastof(path));
562 
563  if (access(path, R_OK) == 0) return strdup(path);
564  }
565 
566  return nullptr;
567 }
568 
576 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose);
577 
585 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose);
586 
595 bool ExpressionNot(Lexer *lexer, StringSet *defines, bool verbose)
596 {
597  if (lexer->GetToken() == TOKEN_NOT) {
598  if (verbose) fprintf(stderr, "!");
599  lexer->Lex();
600  bool value = !ExpressionDefined(lexer, defines, verbose);
601  if (verbose) fprintf(stderr, "[%d]", value);
602  return value;
603  }
604 
605  if (lexer->GetToken() == TOKEN_OPEN) {
606  if (verbose) fprintf(stderr, "(");
607  lexer->Lex();
608  bool value = ExpressionOr(lexer, defines, verbose);
609  if (verbose) fprintf(stderr, ")[%d]", value);
610  lexer->Lex();
611  return value;
612  }
613 
614  if (lexer->GetToken() == TOKEN_ZERO) {
615  if (verbose) fprintf(stderr, "0");
616  lexer->Lex();
617  if (verbose) fprintf(stderr, "[0]");
618  return false;
619  }
620 
621  bool first = true;
622  while (lexer->GetToken() == TOKEN_UNKNOWN || lexer->GetToken() == TOKEN_IDENTIFIER) {
623  if (verbose && first) fprintf(stderr, "<assumed true>");
624  first = false;
625  lexer->Lex();
626  }
627 
628  return true;
629 }
630 
638 bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose)
639 {
640  bool value = ExpressionNot(lexer, defines, verbose);
641 
642  if (lexer->GetToken() != TOKEN_DEFINED) return value;
643  lexer->Lex();
644  if (verbose) fprintf(stderr, "defined");
645  bool open = (lexer->GetToken() == TOKEN_OPEN);
646  if (open) lexer->Lex();
647  if (verbose) fprintf(stderr, open ? "(" : " ");
648  if (lexer->GetToken() == TOKEN_IDENTIFIER) {
649  if (verbose) fprintf(stderr, "%s", lexer->GetString());
650  value = defines->find(lexer->GetString()) != defines->end();
651  }
652  if (open) {
653  if (verbose) fprintf(stderr, ")");
654  lexer->Lex();
655  }
656  lexer->Lex();
657  if (verbose) fprintf(stderr, "[%d]", value);
658  return value;
659 }
660 
668 bool ExpressionAnd(Lexer *lexer, StringSet *defines, bool verbose)
669 {
670  bool value = ExpressionDefined(lexer, defines, verbose);
671 
672  for (;;) {
673  if (lexer->GetToken() != TOKEN_AND) return value;
674  if (verbose) fprintf(stderr, " && ");
675  lexer->Lex();
676  value = value && ExpressionDefined(lexer, defines, verbose);
677  }
678 }
679 
687 bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
688 {
689  bool value = ExpressionAnd(lexer, defines, verbose);
690 
691  for (;;) {
692  if (lexer->GetToken() != TOKEN_OR) return value;
693  if (verbose) fprintf(stderr, " || ");
694  lexer->Lex();
695  value = value || ExpressionAnd(lexer, defines, verbose);
696  }
697 }
698 
700 enum Ignore {
704 };
705 
713 void ScanFile(const char *filename, const char *ext, bool header, bool verbose)
714 {
715  static StringSet defines;
716  static std::stack<Ignore> ignore;
717  /* Copy in the default defines (parameters of depend) */
718  if (!header) {
719  for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
720  defines.insert(strdup(*it));
721  }
722  }
723 
724  File file(filename);
725  Lexer lexer(&file);
726 
727  /* Start the lexing! */
728  lexer.Lex();
729 
730  while (lexer.GetToken() != TOKEN_END) {
731  switch (lexer.GetToken()) {
732  /* We reached the end of the file... yay, we're done! */
733  case TOKEN_END: break;
734 
735  /* The line started with a # (minus whitespace) */
736  case TOKEN_SHARP:
737  lexer.Lex();
738  switch (lexer.GetToken()) {
739  case TOKEN_INCLUDE:
740  if (verbose) fprintf(stderr, "%s #include ", filename);
741  lexer.Lex();
742  switch (lexer.GetToken()) {
743  case TOKEN_LOCAL:
744  case TOKEN_GLOBAL: {
745  if (verbose) fprintf(stderr, "%s", lexer.GetString());
746  if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
747  if (verbose) fprintf(stderr, " (ignored)");
748  break;
749  }
750  const char *h = GeneratePath(file.GetDirname(), lexer.GetString(), lexer.GetToken() == TOKEN_LOCAL);
751  if (h != nullptr) {
752  StringMap::iterator it = _headers.find(h);
753  if (it == _headers.end()) {
754  it = (_headers.insert(StringMapItem(strdup(h), new StringSet()))).first;
755  if (verbose) fprintf(stderr, "\n");
756  ScanFile(h, ext, true, verbose);
757  }
758  StringMap::iterator curfile;
759  if (header) {
760  curfile = _headers.find(filename);
761  } else {
762  /* Replace the extension with the provided extension of '.o'. */
763  char path[PATH_MAX];
764  strecpy(path, filename, lastof(path));
765  *(strrchr(path, '.')) = '\0';
766  strecat(path, ext != nullptr ? ext : ".o", lastof(path));
767  curfile = _files.find(path);
768  if (curfile == _files.end()) {
769  curfile = (_files.insert(StringMapItem(strdup(path), new StringSet()))).first;
770  }
771  }
772  if (it != _headers.end()) {
773  for (StringSet::iterator header = it->second->begin(); header != it->second->end(); header++) {
774  if (curfile->second->find(*header) == curfile->second->end()) curfile->second->insert(strdup(*header));
775  }
776  }
777  if (curfile->second->find(h) == curfile->second->end()) curfile->second->insert(strdup(h));
778  free(h);
779  }
780  }
781  /* FALL THROUGH */
782  default: break;
783  }
784  break;
785 
786  case TOKEN_DEFINE:
787  if (verbose) fprintf(stderr, "%s #define ", filename);
788  lexer.Lex();
789  if (lexer.GetToken() == TOKEN_IDENTIFIER) {
790  if (verbose) fprintf(stderr, "%s", lexer.GetString());
791  if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
792  if (verbose) fprintf(stderr, " (ignored)");
793  break;
794  }
795  if (defines.find(lexer.GetString()) == defines.end()) defines.insert(strdup(lexer.GetString()));
796  lexer.Lex();
797  }
798  break;
799 
800  case TOKEN_UNDEF:
801  if (verbose) fprintf(stderr, "%s #undef ", filename);
802  lexer.Lex();
803  if (lexer.GetToken() == TOKEN_IDENTIFIER) {
804  if (verbose) fprintf(stderr, "%s", lexer.GetString());
805  if (!ignore.empty() && ignore.top() != NOT_IGNORE) {
806  if (verbose) fprintf(stderr, " (ignored)");
807  break;
808  }
809  StringSet::iterator it = defines.find(lexer.GetString());
810  if (it != defines.end()) {
811  free(*it);
812  defines.erase(it);
813  }
814  lexer.Lex();
815  }
816  break;
817 
818  case TOKEN_ENDIF:
819  if (verbose) fprintf(stderr, "%s #endif", filename);
820  lexer.Lex();
821  if (!ignore.empty()) ignore.pop();
822  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
823  break;
824 
825  case TOKEN_ELSE: {
826  if (verbose) fprintf(stderr, "%s #else", filename);
827  lexer.Lex();
828  Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
829  if (!ignore.empty()) ignore.pop();
830  if (ignore.empty() || ignore.top() == NOT_IGNORE) {
831  ignore.push(last == IGNORE_UNTIL_ELSE ? NOT_IGNORE : IGNORE_UNTIL_ENDIF);
832  } else {
833  ignore.push(IGNORE_UNTIL_ENDIF);
834  }
835  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
836  break;
837  }
838 
839  case TOKEN_ELIF: {
840  if (verbose) fprintf(stderr, "%s #elif ", filename);
841  lexer.Lex();
842  Ignore last = ignore.empty() ? NOT_IGNORE : ignore.top();
843  if (!ignore.empty()) ignore.pop();
844  if (ignore.empty() || ignore.top() == NOT_IGNORE) {
845  bool value = ExpressionOr(&lexer, &defines, verbose);
846  ignore.push(last == IGNORE_UNTIL_ELSE ? (value ? NOT_IGNORE : IGNORE_UNTIL_ELSE) : IGNORE_UNTIL_ENDIF);
847  } else {
848  ignore.push(IGNORE_UNTIL_ENDIF);
849  }
850  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
851  break;
852  }
853 
854  case TOKEN_IF: {
855  if (verbose) fprintf(stderr, "%s #if ", filename);
856  lexer.Lex();
857  if (ignore.empty() || ignore.top() == NOT_IGNORE) {
858  bool value = ExpressionOr(&lexer, &defines, verbose);
859  ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
860  } else {
861  ignore.push(IGNORE_UNTIL_ENDIF);
862  }
863  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
864  break;
865  }
866 
867  case TOKEN_IFDEF:
868  if (verbose) fprintf(stderr, "%s #ifdef ", filename);
869  lexer.Lex();
870  if (lexer.GetToken() == TOKEN_IDENTIFIER) {
871  bool value = defines.find(lexer.GetString()) != defines.end();
872  if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
873  if (ignore.empty() || ignore.top() == NOT_IGNORE) {
874  ignore.push(value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
875  } else {
876  ignore.push(IGNORE_UNTIL_ENDIF);
877  }
878  }
879  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
880  break;
881 
882  case TOKEN_IFNDEF:
883  if (verbose) fprintf(stderr, "%s #ifndef ", filename);
884  lexer.Lex();
885  if (lexer.GetToken() == TOKEN_IDENTIFIER) {
886  bool value = defines.find(lexer.GetString()) != defines.end();
887  if (verbose) fprintf(stderr, "%s[%d]", lexer.GetString(), value);
888  if (ignore.empty() || ignore.top() == NOT_IGNORE) {
889  ignore.push(!value ? NOT_IGNORE : IGNORE_UNTIL_ELSE);
890  } else {
891  ignore.push(IGNORE_UNTIL_ENDIF);
892  }
893  }
894  if (verbose) fprintf(stderr, " -> %signore", (!ignore.empty() && ignore.top() != NOT_IGNORE) ? "" : "not ");
895  break;
896 
897  default:
898  if (verbose) fprintf(stderr, "%s #<unknown>", filename);
899  lexer.Lex();
900  break;
901  }
902  if (verbose) fprintf(stderr, "\n");
903  /* FALL THROUGH */
904  default:
905  /* Ignore the rest of the garbage on this line */
906  while (lexer.GetToken() != TOKEN_EOL && lexer.GetToken() != TOKEN_END) lexer.Lex();
907  lexer.Lex();
908  break;
909  }
910  }
911 
912  if (!header) {
913  for (StringSet::iterator it = defines.begin(); it != defines.end(); it++) {
914  free(*it);
915  }
916  defines.clear();
917  while (!ignore.empty()) ignore.pop();
918  }
919 }
920 
927 int main(int argc, char *argv[])
928 {
929  bool ignorenext = true;
930  char *filename = nullptr;
931  char *ext = nullptr;
932  char *delimiter = nullptr;
933  bool append = false;
934  bool verbose = false;
935 
936  for (int i = 0; i < argc; i++) {
937  if (ignorenext) {
938  ignorenext = false;
939  continue;
940  }
941  if (argv[i][0] == '-') {
942  /* Append */
943  if (strncmp(argv[i], "-a", 2) == 0) append = true;
944  /* Include dir */
945  if (strncmp(argv[i], "-I", 2) == 0) {
946  if (argv[i][2] == '\0') {
947  i++;
948  _include_dirs.insert(strdup(argv[i]));
949  } else {
950  _include_dirs.insert(strdup(&argv[i][2]));
951  }
952  continue;
953  }
954  /* Define */
955  if (strncmp(argv[i], "-D", 2) == 0) {
956  char *p = strchr(argv[i], '=');
957  if (p != nullptr) *p = '\0';
958  _defines.insert(strdup(&argv[i][2]));
959  continue;
960  }
961  /* Output file */
962  if (strncmp(argv[i], "-f", 2) == 0) {
963  if (filename != nullptr) continue;
964  filename = strdup(&argv[i][2]);
965  continue;
966  }
967  /* Object file extension */
968  if (strncmp(argv[i], "-o", 2) == 0) {
969  if (ext != nullptr) continue;
970  ext = strdup(&argv[i][2]);
971  continue;
972  }
973  /* Starting string delimiter */
974  if (strncmp(argv[i], "-s", 2) == 0) {
975  if (delimiter != nullptr) continue;
976  delimiter = strdup(&argv[i][2]);
977  continue;
978  }
979  /* Verbose */
980  if (strncmp(argv[i], "-v", 2) == 0) verbose = true;
981  continue;
982  }
983  ScanFile(argv[i], ext, false, verbose);
984  }
985 
986  /* Default output file is Makefile */
987  if (filename == nullptr) filename = strdup("Makefile");
988 
989  /* Default delimiter string */
990  if (delimiter == nullptr) delimiter = strdup("# DO NOT DELETE");
991 
992  char backup[PATH_MAX];
993  strecpy(backup, filename, lastof(backup));
994  strecat(backup, ".bak", lastof(backup));
995 
996  char *content = nullptr;
997  long size = 0;
998 
999  /* Read in the current file; so we can overwrite everything from the
1000  * end of non-depend data marker down till the end. */
1001  FILE *src = fopen(filename, "rb");
1002  if (src != nullptr) {
1003  fseek(src, 0, SEEK_END);
1004  if ((size = ftell(src)) < 0) {
1005  fprintf(stderr, "Could not read %s\n", filename);
1006  exit(-2);
1007  }
1008  rewind(src);
1009  content = (char*)malloc(size * sizeof(*content));
1010  if (fread(content, 1, size, src) != (size_t)size) {
1011  fprintf(stderr, "Could not read %s\n", filename);
1012  exit(-2);
1013  }
1014  fclose(src);
1015  }
1016 
1017  FILE *dst = fopen(filename, "w");
1018  bool found_delimiter = false;
1019 
1020  if (size != 0) {
1021  src = fopen(backup, "wb");
1022  if (fwrite(content, 1, size, src) != (size_t)size) {
1023  fprintf(stderr, "Could not write %s\n", filename);
1024  exit(-2);
1025  }
1026  fclose(src);
1027 
1028  /* Then append it to the real file. */
1029  src = fopen(backup, "r");
1030  while (fgets(content, size, src) != nullptr) {
1031  fputs(content, dst);
1032  if (!strncmp(content, delimiter, strlen(delimiter))) found_delimiter = true;
1033  if (!append && found_delimiter) break;
1034  }
1035  fclose(src);
1036  }
1037  if (!found_delimiter) fprintf(dst, "\n%s\n", delimiter);
1038 
1039  for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
1040  for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1041  fprintf(dst, "%s: %s\n", it->first, *h);
1042  }
1043  }
1044 
1045  /* Clean up our mess. */
1046  fclose(dst);
1047 
1048  free(delimiter);
1049  free(filename);
1050  free(ext);
1051  free(content);
1052 
1053  for (StringMap::iterator it = _files.begin(); it != _files.end(); it++) {
1054  for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1055  free(*h);
1056  }
1057  it->second->clear();
1058  delete it->second;
1059  free(it->first);
1060  }
1061  _files.clear();
1062 
1063  for (StringMap::iterator it = _headers.begin(); it != _headers.end(); it++) {
1064  for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1065  free(*h);
1066  }
1067  it->second->clear();
1068  delete it->second;
1069  free(it->first);
1070  }
1071  _headers.clear();
1072 
1073  for (StringSet::iterator it = _defines.begin(); it != _defines.end(); it++) {
1074  free(*it);
1075  }
1076  _defines.clear();
1077 
1078  for (StringSet::iterator it = _include_dirs.begin(); it != _include_dirs.end(); it++) {
1079  free(*it);
1080  }
1081  _include_dirs.clear();
1082 
1083  return 0;
1084 }
void Next()
Read the next character into &#39;current_char&#39;.
Definition: depend.cpp:295
Read a local include.
Definition: depend.cpp:232
bool ExpressionNot(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a &#39;!expr&#39; expression.
Definition: depend.cpp:595
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: depend.cpp:97
File(const char *filename)
Create the helper by opening the given file.
Definition: depend.cpp:178
const File * file
The file to read from.
Definition: depend.cpp:504
bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a &#39;defined(expr)&#39; expression.
Definition: depend.cpp:638
char * buf
Temporary buffer.
Definition: depend.cpp:508
End of document.
Definition: depend.cpp:229
char * dirname
The directory of the file.
Definition: depend.cpp:223
std::pair< const char *, StringSet * > StringMapItem
Pair of C-style string and a set of C-style strings.
Definition: depend.cpp:157
bool ExpressionAnd(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a &#39;expr && expr&#39; expression.
Definition: depend.cpp:668
#ifdef in code
Definition: depend.cpp:237
void ReadIdentifier()
Read an identifier.
Definition: depend.cpp:457
Lexer of a file.
Definition: depend.cpp:259
size_t buf_len
Length of the temporary buffer.
Definition: depend.cpp:509
No ignoring.
Definition: depend.cpp:701
&#39;0&#39; within #if expression
Definition: depend.cpp:249
const char * GeneratePath(const char *dirname, const char *filename, bool local)
Generate a path from a directory name and a relative filename.
Definition: depend.cpp:523
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:48
Read a global include.
Definition: depend.cpp:233
&#39;&&&#39; within #if expression
Definition: depend.cpp:244
static StringMap _headers
Dependencies of headers.
Definition: depend.cpp:164
char current_char
The current character to process.
Definition: depend.cpp:505
const char * GetDirname() const
Get the directory name of the file.
Definition: depend.cpp:216
&#39;(&#39; within #if expression
Definition: depend.cpp:246
Lexer(const File *file)
Create the lexer and fill the keywords table.
Definition: depend.cpp:265
static StringSet _include_dirs
Include directory to search in.
Definition: depend.cpp:160
int main(int argc, char *argv[])
Entry point.
Definition: depend.cpp:927
~Lexer()
Free everything.
Definition: depend.cpp:287
#include in code
Definition: depend.cpp:250
Unknown token.
Definition: depend.cpp:228
bool operator()(const char *a, const char *b) const
Compare two strings.
#if in code
Definition: depend.cpp:236
char GetChar() const
Get a single character from the file.
Definition: depend.cpp:206
char * string
Currently processed string.
Definition: depend.cpp:506
bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a &#39;expr || expr&#39; expression.
Definition: depend.cpp:687
Ignore till a #endif is reached.
Definition: depend.cpp:703
End of line.
Definition: depend.cpp:230
#ifndef in code
Definition: depend.cpp:238
static StringSet _defines
The current &#39;active&#39; defines.
Definition: depend.cpp:166
#define in code
Definition: depend.cpp:235
Token
A token returned by the tokenizer.
Definition: depend.cpp:227
&#39;)&#39; within #if expression
Definition: depend.cpp:247
static StringMap _files
Files that have been parsed/handled with their dependencies.
Definition: depend.cpp:162
Ignore till a #else is reached.
Definition: depend.cpp:702
&#39;||&#39; within #if expression
Definition: depend.cpp:243
void Lex()
Perform the lexing/tokenizing of the file till we can return something that must be parsed...
Definition: depend.cpp:322
#else in code
Definition: depend.cpp:240
#elif in code
Definition: depend.cpp:239
std::set< const char *, StringCompare > StringSet
Set of C-style strings.
Definition: depend.cpp:153
Token GetToken() const
Get the current token.
Definition: depend.cpp:304
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:66
#define PATH_MAX
The maximum length of paths, if we don&#39;t know it.
Definition: depend.cpp:136
void ScanFile(const char *filename, const char *ext, bool header, bool verbose)
Scan a file for includes, defines and the lot.
Definition: depend.cpp:713
&#39;!&#39; within #if expression
Definition: depend.cpp:248
~File()
Free everything we have allocated.
Definition: depend.cpp:195
&#39;defined&#39; within #if expression
Definition: depend.cpp:245
const char * GetString() const
Read the currently processed string.
Definition: depend.cpp:313
Token FindKeyword(const char *name) const
The token based on keyword with a given name.
Definition: depend.cpp:447
Token token
The current token to process.
Definition: depend.cpp:507
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
std::map< const char *, Token, StringCompare > KeywordList
Mapping from a C-style keyword representation to a Token.
Definition: depend.cpp:254
Ignore
Enumerator to tell how long to ignore &#39;stuff&#39;.
Definition: depend.cpp:700
Identifier within the data.
Definition: depend.cpp:234
KeywordList keywords
All keywords we know of.
Definition: depend.cpp:510
Helper class to read a file.
Definition: depend.cpp:171
std::map< const char *, StringSet *, StringCompare > StringMap
Mapping of C-style string to a set of C-style strings.
Definition: depend.cpp:155
Comparator for strings.
#endif in code
Definition: depend.cpp:241
#undef in code
Definition: depend.cpp:242
FILE * fp
The currently opened file.
Definition: depend.cpp:222
void ReadString(char end, Token token)
Read a string up to a given character, then set the given token.
Definition: depend.cpp:484
character, usually telling something important comes.
Definition: depend.cpp:231