42 #define lengthof(x) (sizeof(x) / sizeof(x[0])) 50 #define lastof(x) (&x[lengthof(x) - 1]) 68 char *
strecpy(
char *dst,
const char *src,
const char *last)
71 while (dst != last && *src !=
'\0') {
76 if (dst == last && *src !=
'\0') {
77 fprintf(stderr,
"String too long for destination buffer\n");
99 static char *
strecat(
char *dst,
const char *src,
const char *last)
102 while (*dst !=
'\0') {
103 if (dst == last)
return dst;
107 return strecpy(dst, src, last);
110 #if defined(__CYGWIN__) 117 strdup (
const char *s)
119 size_t len = strlen(s) + 1;
120 void *n = malloc(len);
122 if (n == NULL)
return NULL;
123 return (
char *) memcpy(n, s, len);
131 static inline void free(
const void *ptr)
133 free(const_cast<void *>(ptr));
138 # define PATH_MAX 260 151 return strcmp(a, b) < 0;
157 typedef std::map<const char*, StringSet*, StringCompare>
StringMap;
182 this->fp = fopen(filename,
"r");
183 if (this->fp ==
nullptr) {
184 fprintf(stdout,
"Could not open %s for reading\n", filename);
187 this->dirname = strdup(filename);
188 char *last = strrchr(this->dirname,
'/');
189 if (last !=
nullptr) {
192 *this->dirname =
'\0';
210 int c = fgetc(this->fp);
211 return (c == EOF) ?
'\0' : c;
220 return this->dirname;
285 this->buf = (
char*)malloc(
sizeof(*this->buf) * this->buf_len);
299 this->current_char = this->file->GetChar();
328 this->
string =
nullptr;
331 switch (this->current_char) {
333 case '\0': this->token =
TOKEN_END;
return;
336 case '\t': this->Next();
break;
337 case '\r': this->Next();
break;
338 case ' ': this->Next();
break;
342 if (this->current_char ==
'\n') this->Next();
367 if (this->current_char ==
'&') {
376 if (this->current_char ==
'|') {
395 if (this->current_char !=
'=') {
404 switch (this->current_char) {
407 char previous_char =
'\0';
408 while ((this->current_char !=
'/' || previous_char !=
'*') && this->current_char !=
'\0') {
409 previous_char = this->current_char;
415 case '/':
while (this->current_char !=
'\n' && this->current_char !=
'\0') this->Next();
break;
421 if (isalpha(this->current_char) || this->current_char ==
'_') {
423 this->ReadIdentifier();
426 if (isdigit(this->current_char)) {
427 bool zero = this->current_char ==
'0';
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';
451 KeywordList::const_iterator it = this->keywords.find(name);
465 this->buf[count++] = this->current_char;
468 if (count >= buf_len) {
471 this->buf = (
char *)realloc(this->buf,
sizeof(*this->buf) * this->buf_len);
473 }
while ((isalpha(this->current_char) || this->current_char ==
'_' || isdigit(this->current_char)));
474 this->buf[count] =
'\0';
477 this->
string = strdup(this->buf);
478 this->token = FindKeyword(this->
string);
490 while (this->current_char != end && this->current_char !=
')' && this->current_char !=
'\n' && this->current_char !=
'\0') {
491 this->buf[count++] = this->current_char;
494 if (count >= this->buf_len) {
497 this->buf = (
char *)realloc(this->buf,
sizeof(*this->buf) * this->buf_len);
500 this->buf[count] =
'\0';
502 this->
string = strdup(this->buf);
525 const char *
GeneratePath(
const char *dirname,
const char *filename,
bool local)
528 if (strchr(filename,
'.') ==
nullptr)
return nullptr;
531 if (access(filename, R_OK) == 0)
return strdup(filename);
535 const char *p = filename;
539 char *s = strrchr(path,
'/');
540 if (s !=
nullptr) *s =
'\0';
547 if (access(path, R_OK) == 0)
return strdup(path);
553 const char *p = filename;
557 char *s = strrchr(path,
'/');
558 if (s !=
nullptr) *s =
'\0';
565 if (access(path, R_OK) == 0)
return strdup(path);
600 if (verbose) fprintf(stderr,
"!");
603 if (verbose) fprintf(stderr,
"[%d]", value);
608 if (verbose) fprintf(stderr,
"(");
611 if (verbose) fprintf(stderr,
")[%d]", value);
617 if (verbose) fprintf(stderr,
"0");
619 if (verbose) fprintf(stderr,
"[0]");
625 if (verbose && first) fprintf(stderr,
"<assumed true>");
646 if (verbose) fprintf(stderr,
"defined");
648 if (open) lexer->
Lex();
649 if (verbose) fprintf(stderr, open ?
"(" :
" ");
651 if (verbose) fprintf(stderr,
"%s", lexer->
GetString());
652 value = defines->find(lexer->
GetString()) != defines->end();
655 if (verbose) fprintf(stderr,
")");
659 if (verbose) fprintf(stderr,
"[%d]", value);
676 if (verbose) fprintf(stderr,
" && ");
695 if (verbose) fprintf(stderr,
" || ");
715 void ScanFile(
const char *filename,
const char *ext,
bool header,
bool verbose)
718 static std::stack<Ignore> ignore;
721 for (StringSet::iterator it =
_defines.begin(); it !=
_defines.end(); it++) {
722 defines.insert(strdup(*it));
742 if (verbose) fprintf(stderr,
"%s #include ", filename);
747 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
748 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
749 if (verbose) fprintf(stderr,
" (ignored)");
754 StringMap::iterator it =
_headers.find(h);
757 if (verbose) fprintf(stderr,
"\n");
760 StringMap::iterator curfile;
767 *(strrchr(path,
'.')) =
'\0';
769 curfile =
_files.find(path);
770 if (curfile ==
_files.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));
779 if (curfile->second->find(h) == curfile->second->end()) curfile->second->insert(strdup(h));
789 if (verbose) fprintf(stderr,
"%s #define ", filename);
792 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
793 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
794 if (verbose) fprintf(stderr,
" (ignored)");
797 if (defines.find(lexer.
GetString()) == defines.end()) defines.insert(strdup(lexer.
GetString()));
803 if (verbose) fprintf(stderr,
"%s #undef ", filename);
806 if (verbose) fprintf(stderr,
"%s", lexer.
GetString());
807 if (!ignore.empty() && ignore.top() !=
NOT_IGNORE) {
808 if (verbose) fprintf(stderr,
" (ignored)");
811 StringSet::iterator it = defines.find(lexer.
GetString());
812 if (it != defines.end()) {
821 if (verbose) fprintf(stderr,
"%s #endif", filename);
823 if (!ignore.empty()) ignore.pop();
824 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
828 if (verbose) fprintf(stderr,
"%s #else", filename);
831 if (!ignore.empty()) ignore.pop();
832 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
837 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
842 if (verbose) fprintf(stderr,
"%s #elif ", filename);
845 if (!ignore.empty()) ignore.pop();
846 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
852 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
857 if (verbose) fprintf(stderr,
"%s #if ", filename);
859 if (ignore.empty() || ignore.top() ==
NOT_IGNORE) {
865 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
870 if (verbose) fprintf(stderr,
"%s #ifdef ", filename);
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) {
881 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
885 if (verbose) fprintf(stderr,
"%s #ifndef ", filename);
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) {
896 if (verbose) fprintf(stderr,
" -> %signore", (!ignore.empty() && ignore.top() !=
NOT_IGNORE) ?
"" :
"not ");
900 if (verbose) fprintf(stderr,
"%s #<unknown>", filename);
904 if (verbose) fprintf(stderr,
"\n");
915 for (StringSet::iterator it = defines.begin(); it != defines.end(); it++) {
919 while (!ignore.empty()) ignore.pop();
929 int main(
int argc,
char *argv[])
931 bool ignorenext =
true;
932 char *filename =
nullptr;
934 char *delimiter =
nullptr;
936 bool verbose =
false;
938 for (
int i = 0; i < argc; i++) {
943 if (argv[i][0] ==
'-') {
945 if (strncmp(argv[i],
"-a", 2) == 0) append =
true;
947 if (strncmp(argv[i],
"-I", 2) == 0) {
948 if (argv[i][2] ==
'\0') {
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]));
964 if (strncmp(argv[i],
"-f", 2) == 0) {
965 if (filename !=
nullptr)
continue;
966 filename = strdup(&argv[i][2]);
970 if (strncmp(argv[i],
"-o", 2) == 0) {
971 if (ext !=
nullptr)
continue;
972 ext = strdup(&argv[i][2]);
976 if (strncmp(argv[i],
"-s", 2) == 0) {
977 if (delimiter !=
nullptr)
continue;
978 delimiter = strdup(&argv[i][2]);
982 if (strncmp(argv[i],
"-v", 2) == 0) verbose =
true;
985 ScanFile(argv[i], ext,
false, verbose);
989 if (filename ==
nullptr) filename = strdup(
"Makefile");
992 if (delimiter ==
nullptr) delimiter = strdup(
"# DO NOT DELETE");
998 char *content =
nullptr;
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);
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);
1019 FILE *dst = fopen(filename,
"w");
1020 bool found_delimiter =
false;
1023 src = fopen(backup,
"wb");
1024 if (fwrite(content, 1, size, src) != (
size_t)size) {
1025 fprintf(stderr,
"Could not write %s\n", filename);
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;
1039 if (!found_delimiter) fprintf(dst,
"\n%s\n", delimiter);
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);
1055 for (StringMap::iterator it =
_files.begin(); it !=
_files.end(); it++) {
1056 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1059 it->second->clear();
1065 for (StringMap::iterator it =
_headers.begin(); it !=
_headers.end(); it++) {
1066 for (StringSet::iterator h = it->second->begin(); h != it->second->end(); h++) {
1069 it->second->clear();
1075 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.