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