40 #define lengthof(x) (sizeof(x) / sizeof(x[0])) 48 #define lastof(x) (&x[lengthof(x) - 1]) 66 char *
strecpy(
char *dst,
const char *src,
const char *last)
69 while (dst != last && *src !=
'\0') {
74 if (dst == last && *src !=
'\0') {
75 fprintf(stderr,
"String too long for destination buffer\n");
97 static char *
strecat(
char *dst,
const char *src,
const char *last)
100 while (*dst !=
'\0') {
101 if (dst == last)
return dst;
105 return strecpy(dst, src, last);
108 #if defined(__CYGWIN__) 115 strdup (
const char *s)
117 size_t len = strlen(s) + 1;
118 void *n = malloc(len);
120 if (n == NULL)
return NULL;
121 return (
char *) memcpy(n, s, len);
129 static inline void free(
const void *ptr)
131 free(const_cast<void *>(ptr));
136 # define PATH_MAX 260 149 return strcmp(a, b) < 0;
155 typedef std::map<const char*, StringSet*, StringCompare>
StringMap;
180 this->fp = fopen(filename,
"r");
181 if (this->fp ==
nullptr) {
182 fprintf(stdout,
"Could not open %s for reading\n", filename);
185 this->dirname = strdup(filename);
186 char *last = strrchr(this->dirname,
'/');
187 if (last !=
nullptr) {
190 *this->dirname =
'\0';
208 int c = fgetc(this->fp);
209 return (c == EOF) ?
'\0' : c;
218 return this->dirname;
283 this->buf = (
char*)malloc(
sizeof(*this->buf) * this->buf_len);
297 this->current_char = this->file->GetChar();
326 this->
string =
nullptr;
329 switch (this->current_char) {
331 case '\0': this->token =
TOKEN_END;
return;
334 case '\t': this->Next();
break;
335 case '\r': this->Next();
break;
336 case ' ': this->Next();
break;
340 if (this->current_char ==
'\n') this->Next();
365 if (this->current_char ==
'&') {
374 if (this->current_char ==
'|') {
393 if (this->current_char !=
'=') {
402 switch (this->current_char) {
405 char previous_char =
'\0';
406 while ((this->current_char !=
'/' || previous_char !=
'*') && this->current_char !=
'\0') {
407 previous_char = this->current_char;
413 case '/':
while (this->current_char !=
'\n' && this->current_char !=
'\0') this->Next();
break;
419 if (isalpha(this->current_char) || this->current_char ==
'_') {
421 this->ReadIdentifier();
424 if (isdigit(this->current_char)) {
425 bool zero = this->current_char ==
'0';
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';
449 KeywordList::const_iterator it = this->keywords.find(name);
463 this->buf[count++] = this->current_char;
466 if (count >= buf_len) {
469 this->buf = (
char *)realloc(this->buf,
sizeof(*this->buf) * this->buf_len);
471 }
while ((isalpha(this->current_char) || this->current_char ==
'_' || isdigit(this->current_char)));
472 this->buf[count] =
'\0';
475 this->
string = strdup(this->buf);
476 this->token = FindKeyword(this->
string);
488 while (this->current_char != end && this->current_char !=
')' && this->current_char !=
'\n' && this->current_char !=
'\0') {
489 this->buf[count++] = this->current_char;
492 if (count >= this->buf_len) {
495 this->buf = (
char *)realloc(this->buf,
sizeof(*this->buf) * this->buf_len);
498 this->buf[count] =
'\0';
500 this->
string = strdup(this->buf);
523 const char *
GeneratePath(
const char *dirname,
const char *filename,
bool local)
526 if (strchr(filename,
'.') ==
nullptr)
return nullptr;
529 if (access(filename, R_OK) == 0)
return strdup(filename);
533 const char *p = filename;
537 char *s = strrchr(path,
'/');
538 if (s !=
nullptr) *s =
'\0';
545 if (access(path, R_OK) == 0)
return strdup(path);
551 const char *p = filename;
555 char *s = strrchr(path,
'/');
556 if (s !=
nullptr) *s =
'\0';
563 if (access(path, R_OK) == 0)
return strdup(path);
598 if (verbose) fprintf(stderr,
"!");
601 if (verbose) fprintf(stderr,
"[%d]", value);
606 if (verbose) fprintf(stderr,
"(");
609 if (verbose) fprintf(stderr,
")[%d]", value);
615 if (verbose) fprintf(stderr,
"0");
617 if (verbose) fprintf(stderr,
"[0]");
623 if (verbose && first) fprintf(stderr,
"<assumed true>");
644 if (verbose) fprintf(stderr,
"defined");
646 if (open) lexer->
Lex();
647 if (verbose) fprintf(stderr, open ?
"(" :
" ");
649 if (verbose) fprintf(stderr,
"%s", lexer->
GetString());
650 value = defines->find(lexer->
GetString()) != defines->end();
653 if (verbose) fprintf(stderr,
")");
657 if (verbose) fprintf(stderr,
"[%d]", value);
674 if (verbose) fprintf(stderr,
" && ");
693 if (verbose) fprintf(stderr,
" || ");
713 void ScanFile(
const char *filename,
const char *ext,
bool header,
bool verbose)
716 static std::stack<Ignore> ignore;
719 for (StringSet::iterator it =
_defines.begin(); it !=
_defines.end(); it++) {
720 defines.insert(strdup(*it));
740 if (verbose) fprintf(stderr,
"%s #include ", filename);
745 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
746 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
747 if (verbose) fprintf(stderr,
" (ignored)");
752 StringMap::iterator it =
_headers.find(h);
755 if (verbose) fprintf(stderr,
"\n");
758 StringMap::iterator curfile;
765 *(strrchr(path,
'.')) =
'\0';
767 curfile =
_files.find(path);
768 if (curfile ==
_files.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));
777 if (curfile->second->find(h) == curfile->second->end()) curfile->second->insert(strdup(h));
787 if (verbose) fprintf(stderr,
"%s #define ", filename);
790 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
791 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
792 if (verbose) fprintf(stderr,
" (ignored)");
795 if (defines.find(lexer.
GetString()) == defines.end()) defines.insert(strdup(lexer.
GetString()));
801 if (verbose) fprintf(stderr,
"%s #undef ", filename);
804 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
805 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
806 if (verbose) fprintf(stderr,
" (ignored)");
809 StringSet::iterator it = defines.find(lexer.
GetString());
810 if (it != defines.end()) {
819 if (verbose) fprintf(stderr,
"%s #endif", filename);
821 if (!ignore.empty()) ignore.pop();
822 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
826 if (verbose) fprintf(stderr,
"%s #else", filename);
829 if (!ignore.empty()) ignore.pop();
830 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
835 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
840 if (verbose) fprintf(stderr,
"%s #elif ", filename);
843 if (!ignore.empty()) ignore.pop();
844 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
850 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
855 if (verbose) fprintf(stderr,
"%s #if ", filename);
857 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
863 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
868 if (verbose) fprintf(stderr,
"%s #ifdef ", filename);
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) {
879 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
883 if (verbose) fprintf(stderr,
"%s #ifndef ", filename);
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) {
894 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
898 if (verbose) fprintf(stderr,
"%s #<unknown>", filename);
902 if (verbose) fprintf(stderr,
"\n");
913 for (StringSet::iterator it = defines.begin(); it != defines.end(); it++) {
917 while (!ignore.empty()) ignore.pop();
927 int main(
int argc,
char *argv[])
929 bool ignorenext =
true;
930 char *filename =
nullptr;
932 char *delimiter =
nullptr;
934 bool verbose =
false;
936 for (
int i = 0; i < argc; i++) {
941 if (argv[i][0] ==
'-') {
943 if (strncmp(argv[i],
"-a", 2) == 0) append =
true;
945 if (strncmp(argv[i],
"-I", 2) == 0) {
946 if (argv[i][2] ==
'\0') {
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]));
962 if (strncmp(argv[i],
"-f", 2) == 0) {
963 if (filename !=
nullptr)
continue;
964 filename = strdup(&argv[i][2]);
968 if (strncmp(argv[i],
"-o", 2) == 0) {
969 if (ext !=
nullptr)
continue;
970 ext = strdup(&argv[i][2]);
974 if (strncmp(argv[i],
"-s", 2) == 0) {
975 if (delimiter !=
nullptr)
continue;
976 delimiter = strdup(&argv[i][2]);
980 if (strncmp(argv[i],
"-v", 2) == 0) verbose =
true;
983 ScanFile(argv[i], ext,
false, verbose);
987 if (filename ==
nullptr) filename = strdup(
"Makefile");
990 if (delimiter ==
nullptr) delimiter = strdup(
"# DO NOT DELETE");
996 char *content =
nullptr;
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);
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);
1017 FILE *dst = fopen(filename,
"w");
1018 bool found_delimiter =
false;
1021 src = fopen(backup,
"wb");
1022 if (fwrite(content, 1, size, src) != (
size_t)size) {
1023 fprintf(stderr,
"Could not write %s\n", filename);
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;
1037 if (!found_delimiter) fprintf(dst,
"\n%s\n", delimiter);
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);
1053 for (StringMap::iterator it =
_files.begin(); it !=
_files.end(); it++) {
1054 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1057 it->second->clear();
1063 for (StringMap::iterator it =
_headers.begin(); it !=
_headers.end(); it++) {
1064 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1067 it->second->clear();
1073 for (StringSet::iterator it =
_defines.begin(); it !=
_defines.end(); it++) {
void Next()
Read the next character into 'current_char'.
bool ExpressionNot(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a '!expr' expression.
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
File(const char *filename)
Create the helper by opening the given file.
const File * file
The file to read from.
bool ExpressionDefined(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a 'defined(expr)' expression.
char * buf
Temporary buffer.
char * dirname
The directory of the file.
std::pair< const char *, StringSet * > StringMapItem
Pair of C-style string and a set of C-style strings.
bool ExpressionAnd(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a 'expr && expr' expression.
void ReadIdentifier()
Read an identifier.
size_t buf_len
Length of the temporary buffer.
'0' within #if expression
const char * GeneratePath(const char *dirname, const char *filename, bool local)
Generate a path from a directory name and a relative filename.
#define lastof(x)
Get the last element of an fixed size array.
'&&' within #if expression
static StringMap _headers
Dependencies of headers.
char current_char
The current character to process.
const char * GetDirname() const
Get the directory name of the file.
'(' within #if expression
Lexer(const File *file)
Create the lexer and fill the keywords table.
static StringSet _include_dirs
Include directory to search in.
int main(int argc, char *argv[])
Entry point.
bool operator()(const char *a, const char *b) const
Compare two strings.
char GetChar() const
Get a single character from the file.
char * string
Currently processed string.
bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
Try to parse a 'expr || expr' expression.
Ignore till a #endif is reached.
static StringSet _defines
The current 'active' defines.
Token
A token returned by the tokenizer.
')' within #if expression
static StringMap _files
Files that have been parsed/handled with their dependencies.
Ignore till a #else is reached.
'||' within #if expression
void Lex()
Perform the lexing/tokenizing of the file till we can return something that must be parsed...
std::set< const char *, StringCompare > StringSet
Set of C-style strings.
Token GetToken() const
Get the current token.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
#define PATH_MAX
The maximum length of paths, if we don't know it.
void ScanFile(const char *filename, const char *ext, bool header, bool verbose)
Scan a file for includes, defines and the lot.
'!' within #if expression
~File()
Free everything we have allocated.
'defined' within #if expression
const char * GetString() const
Read the currently processed string.
Token FindKeyword(const char *name) const
The token based on keyword with a given name.
Token token
The current token to process.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
std::map< const char *, Token, StringCompare > KeywordList
Mapping from a C-style keyword representation to a Token.
Ignore
Enumerator to tell how long to ignore 'stuff'.
Identifier within the data.
KeywordList keywords
All keywords we know of.
Helper class to read a file.
std::map< const char *, StringSet *, StringCompare > StringMap
Mapping of C-style string to a set of C-style strings.
FILE * fp
The currently opened file.
void ReadString(char end, Token token)
Read a string up to a given character, then set the given token.
character, usually telling something important comes.