/* * Another fast dependencies generator for Makefiles, Version 4.2 * * Copyright (C) 2005,2006 by Vladimir Oleynik * * mmaping file may be originally by Linus Torvalds. * * infix parser/evaluator for #if expression * Copyright (c) 2001 Aaron Lehmann * Copyright (c) 2001 Manuel Novoa III * Copyright (c) 2003 Vladimir Oleynik * * bb_simplify_path() * Copyright (C) 2005 Manuel Novoa III * * xmalloc() bb_xstrdup() bb_error_d(): * Copyright (C) 1999-2004 by Erik Andersen * * llist routine * Copyright (C) 2003 Glenn McGrath * Copyright (C) Vladimir Oleynik * * (c) 2005,2006 Bernhard Fischer: * - commentary typos, * - move "memory exhausted" into msg_enomem, * - more verbose --help output. * * This program does: * 1) find #define KEY VALUE or #undef KEY from include/bb_config.h * 2) recursive find and scan *.[ch] files, but skips scan of include/config/ * 3) find #include "*.h" and KEYs using, if not as #define and #undef * 4) generate dependencies to stdout * pwd/file.o: include/config/key*.h found_include_*.h * path/inc.h: include/config/key*.h found_included_include_*.h * 5) save include/config/key*.h if changed after previous usage * This program does not generate dependencies for #include <...> * Config file can have #if #elif #else #ifdef #ifndef #endif lines */ #define LOCAL_INCLUDE_PATH "include" #define INCLUDE_CONFIG_PATH LOCAL_INCLUDE_PATH"/config" #define INCLUDE_CONFIG_KEYS_PATH LOCAL_INCLUDE_PATH"/bb_config.h" #define bb_mkdep_full_options \ "\nOptions:" \ "\n\t-I local_include_path include paths, default: \"" LOCAL_INCLUDE_PATH "\"" \ "\n\t-d don't generate depend" \ "\n\t-w show warning if include files not found" \ "\n\t-k include/config default: \"" INCLUDE_CONFIG_PATH "\"" \ "\n\t-c include/config.h configs, default: \"" INCLUDE_CONFIG_KEYS_PATH "\"" \ "\n\tdirs_to_scan default \".\"" #define bb_mkdep_terse_options "Usage: [-I local_include_paths] [-dw] " \ "[-k path_for_stored_keys] [dirs]" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ #define ATTRIBUTE __attribute__ #else #define ATTRIBUTE(a) /* nothing */ #endif #if !(defined __USE_ISOC99 || (defined __GLIBC_HAVE_LONG_LONG && defined __USE_MISC)) #define strtoll strtol #endif /* partial and simplified libbb routine */ static void bb_error_d(const char *s, ...) ATTRIBUTE ((noreturn, format (printf, 1, 2))); static char * bb_asprint(const char *format, ...) ATTRIBUTE ((format (printf, 1, 2))); static char *bb_simplify_path(const char *path); /* stolen from libbb as is */ typedef struct llist_s { char *data; struct llist_s *link; } llist_t; static const char msg_enomem[] = "memory exhausted"; /* inline realization for fast works */ static inline void *xmalloc(size_t size) { void *p = malloc(size); if(p == NULL) bb_error_d(msg_enomem); return p; } static inline char *bb_xstrdup(const char *s) { char *r = strdup(s); if(r == NULL) bb_error_d(msg_enomem); return r; } static int dontgenerate_dep; /* flag -d usaged */ static int noiwarning; /* flag -w usaged */ static llist_t *configs; /* list of -c usaged and them stat() after parsed */ static llist_t *Iop; /* list of -I include usaged */ static char *pwd; /* current work directory */ static size_t replace; /* replace current work directory with build dir */ static const char *kp; /* KEY path, argument of -k used */ static size_t kp_len; static struct stat st_kp; /* stat(kp) */ typedef struct BB_KEYS { char *keyname; size_t key_sz; const char *value; const char *checked; char *stored_path; const char *src_have_this_key; struct BB_KEYS *next; } bb_key_t; static bb_key_t *key_top; /* list of found KEYs */ static bb_key_t *Ifound; /* list of parsed includes */ static void parse_conf_opt(const char *opt, const char *val, size_t key_sz); static void parse_inc(const char *include, const char *fname); static inline bb_key_t *check_key(bb_key_t *k, const char *nk, size_t key_sz) { bb_key_t *cur; for(cur = k; cur; cur = cur->next) { if(key_sz == cur->key_sz && memcmp(cur->keyname, nk, key_sz) == 0) { cur->checked = cur->stored_path; return cur; } } return NULL; } static inline const char *lookup_key(const char *nk, size_t key_sz) { bb_key_t *cur; for(cur = key_top; cur; cur = cur->next) { if(key_sz == cur->key_sz && memcmp(cur->keyname, nk, key_sz) == 0) { return cur->value; } } return NULL; } /* for lexical analyser */ static int pagesizem1; /* padding mask = getpagesize() - 1 */ /* for speed tricks */ static char first_chars[1+UCHAR_MAX]; /* + L_EOF */ static char isalnums[1+UCHAR_MAX]; /* + L_EOF */ /* trick for fast find "define", "include", "undef", "if((n)def)" "else", "endif" */ static const char * const preproc[] = { /* 0 1 2 3 4 5 6 7 8 9 */ "", "efine", "lif", "lse", "ndif", "f", "fdef", "fndef", "nclude", "ndef" }; static const unsigned char first_chars_deiu[UCHAR_MAX] = { [(int)'d'] = (unsigned char)(1|0x10), /* define */ [(int)'e'] = (unsigned char)(2|0x40), /* elif, else, endif */ [(int)'i'] = (unsigned char)(5|0x80), /* if ifdef ifndef include */ [(int)'u'] = (unsigned char)(9|0x90), /* undef */ }; #define CONFIG_MODE 0 #define IF0_MODE 1 /* #if 0 */ #define IF1_MODE 2 /* #if 1 */ #define ELSE0_MODE 4 /* #else found after #if 0 */ #define ELSE1_MODE 8 /* #else found after #if 1 */ #define ELIF1_MODE 16 /* #elif found after #if 1 */ #define FALSE_MODES (IF0_MODE|ELSE1_MODE|ELIF1_MODE) #define yy_error_d(s) bb_error_d("%s:%d hmm, %s", fname, line, s) /* state */ #define S 0 /* start state */ #define STR '"' /* string */ #define CHR '\'' /* char */ #define REM '/' /* block comment */ #define BS '\\' /* back slash */ #define POUND '#' /* # */ #define D '1' /* #define preprocessor's directive */ #define EI '2' /* #elif preprocessor's directive */ #define E '3' /* #else preprocessor's directive */ #define EF '4' /* #endif preprocessor's directive */ #define F '5' /* #if preprocessor's directive */ #define IFD '6' /* #ifdef preprocessor's directive */ #define IFND '7' /* #ifndef preprocessor's directive */ #define I '8' /* #include preprocessor's directive */ #define U '9' /* #undef preprocessor's directive */ #define DK 'K' /* #define KEY... (config mode) */ #define ANY '*' /* any unparsed chars */ /* [A-Z_a-z] */ #define ID(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') /* [A-Z_a-z0-9] */ #define ISALNUM(c) (ID(c) || (c >= '0' && c <= '9')) #define L_EOF (1+UCHAR_MAX) #define getc0() do { c = (optr >= oend) ? L_EOF : *optr++; } while(0) #define getc1() do { getc0(); if(c == BS) { getc0(); \ if(c == '\n') { line++; continue; } \ else { optr--; c = BS; } \ } break; } while(1) static char id_s[4096]; #define put_id(ic) do { if(id_len == sizeof(id_s)) goto too_long; \ id[id_len++] = ic; } while(0) static char ifcpp_stack[1024]; static int ptr_ifcpp_stack; #define push_mode() do { \ if(ptr_ifcpp_stack == (int)sizeof(ifcpp_stack)) \ yy_error_d("#if* stack overflow"); \ ifcpp_stack[ptr_ifcpp_stack++] = (char)mode; \ } while(0) #define pop_mode() do { \ if(ptr_ifcpp_stack == 0) \ yy_error_d("unexpected #endif"); \ mode = ifcpp_stack[--ptr_ifcpp_stack]; \ } while(0) /* #if expression */ typedef long long arith_t; static arith_t arith (const char *expr, int *perrcode); /* The code uses a simple two-stack algorithm. See * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html * for a detailed explanation of the infix-to-postfix algorithm on which * this is based (this code differs in that it applies operators immediately * to the stack instead of adding them to a queue to end up with an * expression). */ #define arith_isspace(arithval) \ (arithval == ' ' || arithval == '\v' || arithval == '\t' || arithval == '\f') typedef unsigned char operator; /* An operator's token id is a bit of a bitfield. The lower 5 bits are the * precedence, and 3 high bits are an ID unique across operators of that * precedence. The ID portion is so that multiple operators can have the * same precedence, ensuring that the leftmost one is evaluated first. * Consider * and /. */ #define tok_decl(prec,id) (((id)<<5)|(prec)) #define PREC(op) ((op) & 0x1F) #define TOK_LPAREN tok_decl(0,0) #define TOK_COMMA tok_decl(1,0) /* conditional is right associativity too */ #define TOK_CONDITIONAL tok_decl(2,0) #define TOK_CONDITIONAL_SEP tok_decl(2,1) #define TOK_OR tok_decl(3,0) #define TOK_AND tok_decl(4,0) #define TOK_BOR tok_decl(5,0) #define TOK_BXOR tok_decl(6,0) #define TOK_BAND tok_decl(7,0) #define TOK_EQ tok_decl(8,0) #define TOK_NE tok_decl(8,1) #define TOK_LT tok_decl(9,0) #define TOK_GT tok_decl(9,1) #define TOK_GE tok_decl(9,2) #define TOK_LE tok_decl(9,3) #define TOK_LSHIFT tok_decl(10,0) #define TOK_RSHIFT tok_decl(10,1) #define TOK_ADD tok_decl(11,0) #define TOK_SUB tok_decl(11,1) #define TOK_MUL tok_decl(12,0) #define TOK_DIV tok_decl(12,1) #define TOK_REM tok_decl(12,2) /* For now unary operators. */ #define UNARYPREC 13 #define TOK_BNOT tok_decl(UNARYPREC,0) #define TOK_NOT tok_decl(UNARYPREC,1) #define TOK_UMINUS tok_decl(UNARYPREC+1,0) #define TOK_UPLUS tok_decl(UNARYPREC+1,1) #define SPEC_PREC (UNARYPREC+2) #define TOK_NUM tok_decl(SPEC_PREC, 0) #define TOK_RPAREN tok_decl(SPEC_PREC, 1) #define NUMPTR (*numstackptr) typedef struct ARITCH_VAR_NUM { arith_t val; arith_t contidional_second_val; char contidional_second_val_initialized; } v_n_t; typedef struct CHK_VAR_RECURSIVE_LOOPED { const char *var; size_t var_sz; struct CHK_VAR_RECURSIVE_LOOPED *next; } chk_var_recursive_looped_t; static chk_var_recursive_looped_t *prev_chk_var_recursive; static int arith_lookup_val(const char *var, size_t key_sz, arith_t *pval) { const char * p = lookup_key(var, key_sz); if(p) { int errcode; /* recursive try as expression */ chk_var_recursive_looped_t *cur; chk_var_recursive_looped_t cur_save; for(cur = prev_chk_var_recursive; cur; cur = cur->next) { if(cur->var_sz == key_sz && memcmp(cur->var, var, key_sz) == 0) { /* expression recursion loop detected */ return -5; } } /* save current lookuped var name */ cur = prev_chk_var_recursive; cur_save.var = var; cur_save.var_sz = key_sz; cur_save.next = cur; prev_chk_var_recursive = &cur_save; *pval = arith (p, &errcode); /* restore previous ptr after recursiving */ prev_chk_var_recursive = cur; return errcode; } else { /* disallow undefined var */ fprintf(stderr, "%.*s ", (int)key_sz, var); return -4; } } /* "applying" a token means performing it on the top elements on the integer * stack. For a unary operator it will only change the top element, but a * binary operator will pop two arguments and push a result */ static inline int arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) { v_n_t *numptr_m1; arith_t numptr_val, rez; if (NUMPTR == numstack) goto err; /* There is no operator that can work without arguments */ numptr_m1 = NUMPTR - 1; rez = numptr_m1->val; if (op == TOK_UMINUS) rez *= -1; else if (op == TOK_NOT) rez = !rez; else if (op == TOK_BNOT) rez = ~rez; else if (op != TOK_UPLUS) { /* Binary operators */ /* check and binary operators need two arguments */ if (numptr_m1 == numstack) goto err; /* ... and they pop one */ --NUMPTR; numptr_val = rez; if (op == TOK_CONDITIONAL) { if(! numptr_m1->contidional_second_val_initialized) { /* protect $((expr1 ? expr2)) without ": expr" */ goto err; } rez = numptr_m1->contidional_second_val; } else if(numptr_m1->contidional_second_val_initialized) { /* protect $((expr1 : expr2)) without "expr ? " */ goto err; } numptr_m1 = NUMPTR - 1; if (op == TOK_CONDITIONAL) { numptr_m1->contidional_second_val = rez; } rez = numptr_m1->val; if (op == TOK_BOR) rez |= numptr_val; else if (op == TOK_OR) rez = numptr_val || rez; else if (op == TOK_BAND) rez &= numptr_val; else if (op == TOK_BXOR) rez ^= numptr_val; else if (op == TOK_AND) rez = rez && numptr_val; else if (op == TOK_EQ) rez = (rez == numptr_val); else if (op == TOK_NE) rez = (rez != numptr_val); else if (op == TOK_GE) rez = (rez >= numptr_val); else if (op == TOK_RSHIFT) rez >>= numptr_val; else if (op == TOK_LSHIFT) rez <<= numptr_val; else if (op == TOK_GT) rez = (rez > numptr_val); else if (op == TOK_LT) rez = (rez < numptr_val); else if (op == TOK_LE) rez = (rez <= numptr_val); else if (op == TOK_MUL) rez *= numptr_val; else if (op == TOK_ADD) rez += numptr_val; else if (op == TOK_SUB) rez -= numptr_val; else if (op == TOK_COMMA) rez = numptr_val; else if (op == TOK_CONDITIONAL_SEP) { if (numptr_m1 == numstack) { /* protect $((expr : expr)) without "expr ? " */ goto err; } numptr_m1->contidional_second_val_initialized = op; numptr_m1->contidional_second_val = numptr_val; } else if (op == TOK_CONDITIONAL) { rez = rez ? numptr_val : numptr_m1->contidional_second_val; } else if(numptr_val==0) /* zero divisor check */ return -2; else if (op == TOK_DIV) rez /= numptr_val; else if (op == TOK_REM) rez %= numptr_val; } numptr_m1->val = rez; return 0; err: return(-1); } /* longest must first */ static const char op_tokens[] = { '<','<', 0, TOK_LSHIFT, '>','>', 0, TOK_RSHIFT, '|','|', 0, TOK_OR, '&','&', 0, TOK_AND, '!','=', 0, TOK_NE, '<','=', 0, TOK_LE, '>','=', 0, TOK_GE, '=','=', 0, TOK_EQ, '!', 0, TOK_NOT, '<', 0, TOK_LT, '>', 0, TOK_GT, '|', 0, TOK_BOR, '&', 0, TOK_BAND, '*', 0, TOK_MUL, '/', 0, TOK_DIV, '%', 0, TOK_REM, '+', 0, TOK_ADD, '-', 0, TOK_SUB, '^', 0, TOK_BXOR, '~', 0, TOK_BNOT, ',', 0, TOK_COMMA, '?', 0, TOK_CONDITIONAL, ':', 0, TOK_CONDITIONAL_SEP, ')', 0, TOK_RPAREN, '(', 0, TOK_LPAREN, 0 }; /* ptr to ")" */ #define endexpression &op_tokens[sizeof(op_tokens)-7] /* * Return of a legal variable name (a letter or underscore followed by zero or * more letters, underscores, and digits). */ static inline char * endofname(const char *name) { char *p; p = (char *) name; if (! ID(*p)) return p; while (*++p) { if (! ISALNUM(*p)) break; } return p; } static arith_t arith (const char *expr, int *perrcode) { char arithval; /* Current character under analysis */ operator lasttok, op; operator prec; const char *p = endexpression; int errcode; size_t datasizes = strlen(expr) + 2; /* Stack of integers */ /* The proof that there can be no more than strlen(startbuf)/2+1 integers * in any given correct or incorrect expression is left as an exercise to * the reader. */ v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)), *numstackptr = numstack; /* Stack of operator tokens */ operator *stack = alloca((datasizes) * sizeof(operator)), *stackptr = stack; *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ *perrcode = errcode = 0; while(1) { if ((arithval = *expr) == 0) { if (p == endexpression) { /* Null expression. */ err: return (*perrcode = -1); } /* This is only reached after all tokens have been extracted from the * input stream. If there are still tokens on the operator stack, they * are to be applied in order. At the end, there should be a final * result on the integer stack */ if (expr != endexpression + 1) { /* If we haven't done so already, */ /* append a closing right paren */ expr = endexpression; /* and let the loop process it. */ continue; } /* At this point, we're done with the expression. */ if (numstackptr != numstack+1) { /* ... but if there isn't, it's bad */ goto err; } ret: *perrcode = errcode; return numstack->val; } else { /* Continue processing the expression. */ if (arith_isspace(arithval)) { /* Skip whitespace */ goto prologue; } if((p = endofname(expr)) != expr) { size_t var_name_size = (p-expr); if(var_name_size == 7 && strncmp(expr, "defined", var_name_size) == 0) { int brace_form = 0; const char *v; while(arith_isspace(*p)) p++; if(*p == '(') { p++; while(arith_isspace(*p)) p++; brace_form = 1; } expr = p; if((p = endofname(expr)) == expr) goto err; var_name_size = (p-expr); while(arith_isspace(*p)) p++; if(brace_form && *p++ != ')') goto err; v = lookup_key(expr, var_name_size); numstackptr->val = (v != NULL) ? 1 : 0; } else { errcode = arith_lookup_val(expr, var_name_size, &(numstackptr->val)); if(errcode) goto ret; } expr = p; num: numstackptr->contidional_second_val_initialized = 0; numstackptr++; lasttok = TOK_NUM; continue; } else if (arithval >= '0' && arithval <= '9') { numstackptr->val = strtoll(expr, (char **) &expr, 0); while(*expr == 'l' || *expr == 'L' || *expr == 'u' || *expr == 'U') expr++; goto num; } for(p = op_tokens; ; p++) { const char *o; if(*p == 0) { /* strange operator not found */ goto err; } for(o = expr; *p && *o == *p; p++) o++; if(! *p) { /* found */ expr = o - 1; break; } /* skip tail uncompared token */ while(*p) p++; /* skip zero delim */ p++; } op = p[1]; /* Plus and minus are binary (not unary) _only_ if the last * token was as number, or a right paren (which pretends to be * a number, since it evaluates to one). Think about it. * It makes sense. */ if (lasttok != TOK_NUM) { if(op == TOK_ADD) op = TOK_UPLUS; else if(op == TOK_SUB) op = TOK_UMINUS; } /* We don't want a unary operator to cause recursive descent on the * stack, because there can be many in a row and it could cause an * operator to be evaluated before its argument is pushed onto the * integer stack. */ /* But for binary operators, "apply" everything on the operator * stack until we find an operator with a lesser priority than the * one we have just extracted. */ /* Left paren is given the lowest priority so it will never be * "applied" in this way. * if associativity is right and priority eq, applied also skip */ prec = PREC(op); if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { /* not left paren or unary */ if (lasttok != TOK_NUM) { /* binary op must be preceded by a num */ goto err; } while (stackptr != stack) { if (op == TOK_RPAREN) { /* The algorithm employed here is simple: while we don't * hit an open paren nor the bottom of the stack, pop * tokens and apply them */ if (stackptr[-1] == TOK_LPAREN) { --stackptr; /* Any operator directly after a */ lasttok = TOK_NUM; /* close paren should consider itself binary */ goto prologue; } } else { operator prev_prec = PREC(stackptr[-1]); if (prev_prec < prec) break; /* check right assoc */ if(prev_prec == prec && prec == PREC(TOK_CONDITIONAL)) break; } errcode = arith_apply(*--stackptr, numstack, &numstackptr); if(errcode) goto ret; } if (op == TOK_RPAREN) { goto err; } } /* Push this operator to the stack and remember it. */ *stackptr++ = lasttok = op; prologue: ++expr; } } } /* stupid C lexical analyser for configs.h */ static void c_lex_config(const char *fname, long fsize) { int c; int state; int line; char *id = id_s; size_t id_len = 0; /* stupid initialization */ unsigned char *optr, *oend; int mode = CONFIG_MODE; int fd; char *map; int mapsize; if(fsize == 0) { fprintf(stderr, "Warning: %s is empty\n", fname); return; } fd = open(fname, O_RDONLY); if(fd < 0) { perror(fname); return; } mapsize = (fsize+pagesizem1) & ~pagesizem1; map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0); if ((long) map == -1) bb_error_d("%s: mmap: %m", fname); optr = (unsigned char *)map; oend = optr + fsize; line = 1; state = S; for(;;) { getc1(); for(;;) { /* [ \t]+ eat first space */ while(c == ' ' || c == '\t') getc1(); if(state == S) { while(first_chars[c] == ANY) { /* unparsed */ if(c == '\n') line++; getc1(); } if(c == L_EOF) { /* <> */ munmap(map, mapsize); close(fd); if(mode != CONFIG_MODE) yy_error_d("expected #endif"); return; } if(c == REM) { /* / */ getc0(); if(c == REM) { /* "//"[^\n]* */ do getc0(); while(c != '\n' && c != L_EOF); } else if(c == '*') { /* [/][*] goto parse block comments */ break; } } else if(c == POUND) { /* # */ state = c; getc1(); } else if(c == STR || c == CHR) { /* \"|\' */ int qc = c; for(;;) { /* . */ getc1(); if(c == qc) { /* \" or \' */ break; } if(c == BS) { /* \\ but is not \\\n */ getc0(); } if(c == '\n' || c == L_EOF) yy_error_d("unterminated"); } getc1(); } else { /* [A-Z_a-z0-9] */ /* trick for fast drop id if key with this first char undefined */ if(first_chars[c] == 0 || (mode & FALSE_MODES) != 0) { /* skip [A-Z_a-z0-9]+ */ do getc1(); while(isalnums[c]); } else { id_len = 0; do { /* [A-Z_a-z0-9]+ */ put_id(c); getc1(); } while(isalnums[c]); check_key(key_top, id, id_len); } } continue; } /* begin preprocessor states */ if(c == L_EOF) yy_error_d("unexpected EOF"); if(c == REM) { /* <#.*>/ */ getc0(); if(c == REM) yy_error_d("detected // in preprocessor line"); if(c == '*') { /* <#.*>[/][*] goto parse block comments */ break; } /* hmm, #.*[/] */ yy_error_d("strange preprocessor line"); } if(state == POUND) { /* tricks */ int diu = (int)first_chars_deiu[c]; /* preproc ptr */ state = S; if(diu != S) { int p_num_str, p_num_max; getc1(); id_len = 0; while(isalnums[c]) { put_id(c); getc1(); } put_id(0); p_num_str = diu & 0xf; p_num_max = diu >> 4; for(diu = p_num_str; diu <= p_num_max; diu++) if(!strcmp(id, preproc[diu])) { state = (diu + '0'); /* common */ id_len = 0; break; } } else { while(isalnums[c]) getc1(); } } else if(state == EF) { /* #endif */ pop_mode(); state = S; } else if(state == I) { if(c == STR && (mode & FALSE_MODES) == 0) { /* \" */ for(;;) { getc1(); if(c == STR) break; if(c == L_EOF) yy_error_d("unexpected EOF"); put_id(c); } put_id(0); /* store "include.h" */ parse_inc(id, fname); getc1(); } /* else another (may be wrong) #include ... */ state = S; } else if(state == F) { arith_t t; int errcode; while(c != '\n' && c != L_EOF) { put_id(c); getc1(); } put_id(0); t = arith(id, &errcode); if (errcode < 0) { if (errcode == -2) yy_error_d("divide by zero"); else if (errcode == -4) yy_error_d("undefined"); else if (errcode == -5) yy_error_d("expression recursion loop detected"); else yy_error_d("syntax error"); } push_mode(); mode = t != 0 ? IF1_MODE : IF0_MODE; state = S; } else if(state == IFD || state == IFND) { /* save KEY from #if(n)def KEY ... */ const char *v; push_mode(); while(isalnums[c]) { put_id(c); getc1(); } if(!id_len) yy_error_d("expected identifier"); v = lookup_key(id, id_len); mode = IF1_MODE; if(state == IFD && v == NULL) mode = IF0_MODE; else if(state == IFND && v != NULL) mode = IF0_MODE; state = S; } else if(state == EI) { /* #elif */ if(mode == CONFIG_MODE || mode == ELSE0_MODE || mode == ELSE1_MODE) yy_error_d("unexpected #elif"); if(mode == IF0_MODE) { pop_mode(); state = F; } else { mode = ELIF1_MODE; state = S; } } else if(state == E) { if(mode == CONFIG_MODE || mode == ELSE0_MODE || mode == ELSE1_MODE) yy_error_d("unexpected #else"); if(mode == IF0_MODE) mode = ELSE0_MODE; else if(mode == IF1_MODE) mode = ELSE1_MODE; state = S; } else if(state == D || state == U) { /* save KEY from #"define"|"undef" ... */ while(isalnums[c]) { put_id(c); getc1(); } if(!id_len) yy_error_d("expected identifier"); if(state == U) { if((mode & FALSE_MODES) == 0) parse_conf_opt(id, NULL, id_len); state = S; } else { /* D -> DK */ state = DK; } } else { /* state== #define KEY[ ] */ size_t opt_len = id_len; char *val = id + opt_len; char *sp; for(;;) { if(c == L_EOF || c == '\n') break; put_id(c); getc1(); } sp = id + id_len; put_id(0); /* trim tail spaces */ while(--sp >= val && (*sp == ' ' || *sp == '\t' || *sp == '\f' || *sp == '\v')) *sp = '\0'; if((mode & FALSE_MODES) == 0) parse_conf_opt(id, val, opt_len); state = S; } } /* */ getc0(); for(;;) { /* [^*]+ */ while(c != '*') { if(c == '\n') { /* \n */ if(state != S) yy_error_d("unexpected newline"); line++; } else if(c == L_EOF) yy_error_d("unexpected EOF"); getc0(); } /* [*] */ getc0(); if(c == REM) { /* [*][/] */ break; } } } too_long: yy_error_d("phrase too long"); } /* trick for fast find "define", "include", "undef" */ static const char first_chars_diu[UCHAR_MAX] = { [(int)'d'] = (char)5, /* strlen("define") - 1; */ [(int)'i'] = (char)6, /* strlen("include") - 1; */ [(int)'u'] = (char)4, /* strlen("undef") - 1; */ }; #undef D #undef I #undef U #define D '5' /* #define preprocessor's directive */ #define I '6' /* #include preprocessor's directive */ #define U '4' /* #undef preprocessor's directive */ /* stupid C lexical analyser for sources */ static void c_lex_src(const char *fname, long fsize) { int c; int state; int line; char *id = id_s; size_t id_len = 0; /* stupid initialization */ unsigned char *optr, *oend; int fd; char *map; int mapsize; if(fsize == 0) { fprintf(stderr, "Warning: %s is empty\n", fname); return; } fd = open(fname, O_RDONLY); if(fd < 0) { perror(fname); return; } mapsize = (fsize+pagesizem1) & ~pagesizem1; map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0); if ((long) map == -1) bb_error_d("%s: mmap: %m", fname); optr = (unsigned char *)map; oend = optr + fsize; line = 1; state = S; for(;;) { getc1(); for(;;) { /* [ \t]+ eat first space */ while(c == ' ' || c == '\t') getc1(); if(state == S) { while(first_chars[c] == ANY) { /* unparsed */ if(c == '\n') line++; getc1(); } if(c == L_EOF) { /* <> */ munmap(map, mapsize); close(fd); return; } if(c == REM) { /* / */ getc0(); if(c == REM) { /* "//"[^\n]* */ do getc0(); while(c != '\n' && c != L_EOF); } else if(c == '*') { /* [/][*] goto parse block comments */ break; } } else if(c == POUND) { /* # */ state = c; getc1(); } else if(c == STR || c == CHR) { /* \"|\' */ int qc = c; for(;;) { /* . */ getc1(); if(c == qc) { /* \" or \' */ break; } if(c == BS) { /* \\ but is not \\\n */ getc0(); } if(c == '\n' || c == L_EOF) yy_error_d("unterminated"); } getc1(); } else { /* [A-Z_a-z0-9] */ /* trick for fast drop id if key with this first char undefined */ if(first_chars[c] == 0) { /* skip [A-Z_a-z0-9]+ */ do getc1(); while(isalnums[c]); } else { id_len = 0; do { /* [A-Z_a-z0-9]+ */ put_id(c); getc1(); } while(isalnums[c]); check_key(key_top, id, id_len); } } continue; } /* begin preprocessor states */ if(c == L_EOF) yy_error_d("unexpected EOF"); if(c == REM) { /* <#.*>/ */ getc0(); if(c == REM) yy_error_d("detected // in preprocessor line"); if(c == '*') { /* <#.*>[/][*] goto parse block comments */ break; } /* hmm, #.*[/] */ yy_error_d("strange preprocessor line"); } if(state == POUND) { /* tricks */ static const char * const p_preproc[] = { /* 0 1 2 3 4 5 6 */ "", "", "", "", "ndef", "efine", "nclude" }; size_t diu = first_chars_diu[c]; /* strlen and p_preproc ptr */ state = S; if(diu != S) { getc1(); id_len = 0; while(isalnums[c]) { put_id(c); getc1(); } /* str begins with c, read == strlen key and compared */ if(diu == id_len && !memcmp(id, p_preproc[diu], diu)) { state = diu + '0'; id_len = 0; /* common for save */ } } else { while(isalnums[c]) getc1(); } } else if(state == I) { if(c == STR) { /* \" */ for(;;) { getc1(); if(c == STR) break; if(c == L_EOF) yy_error_d("unexpected EOF"); put_id(c); } put_id(0); /* store "include.h" */ parse_inc(id, fname); getc1(); } /* else another (may be wrong) #include ... */ state = S; } else /* if(state == D || state == U) */ { /* ignore depend with #define or #undef KEY */ while(isalnums[c]) getc1(); state = S; } } /* */ getc0(); for(;;) { /* [^*]+ */ while(c != '*') { if(c == '\n') { /* \n */ if(state != S) yy_error_d("unexpected newline"); line++; } else if(c == L_EOF) yy_error_d("unexpected EOF"); getc0(); } /* [*] */ getc0(); if(c == REM) { /* [*][/] */ break; } } } too_long: yy_error_d("phrase too long"); } /* bb_simplify_path special variant for absolute pathname */ static size_t bb_qa_simplify_path(char *path) { char *s, *p; p = s = path; do { if (*p == '/') { if (*s == '/') { /* skip duplicate (or initial) slash */ continue; } else if (*s == '.') { if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */ continue; } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) { ++s; if (p > path) { while (*--p != '/'); /* omit previous dir */ } continue; } } } *++p = *s; } while (*++s); if ((p == path) || (*p != '/')) { /* not a trailing slash */ ++p; /* so keep last character */ } *p = 0; return (p - path); } static void parse_inc(const char *include, const char *fname) { bb_key_t *cur; const char *p_i; llist_t *lo; char *ap; size_t key_sz; struct stat st; if(*include == '/') { lo = NULL; ap = bb_xstrdup(include); } else { int w; const char *p; lo = Iop; p = strrchr(fname, '/'); /* fname has absolute pathname */ w = (p-fname); /* find from current directory of source file */ ap = bb_asprint("%.*s/%s", w, fname, include); } for(;;) { key_sz = bb_qa_simplify_path(ap); cur = check_key(Ifound, ap, key_sz); if(cur) { cur->checked = cur->value; free(ap); return; } if(stat(ap, &st) == 0) { /* found */ llist_t *cfl; for(cfl = configs; cfl; cfl = cfl->link) { struct stat *config = (struct stat *)cfl->data; if (st.st_dev == config->st_dev && st.st_ino == config->st_ino) { /* skip depend with bb_configs.h */ return; } } p_i = ap; break; } else if(lo == NULL) { p_i = NULL; break; } /* find from "-I include" specified directories */ free(ap); /* lo->data has absolute pathname */ ap = bb_asprint("%s/%s", lo->data, include); lo = lo->link; } cur = xmalloc(sizeof(bb_key_t)); cur->keyname = ap; cur->key_sz = key_sz; cur->stored_path = ap; cur->value = cur->checked = p_i; if(p_i == NULL && noiwarning) fprintf(stderr, "%s: Warning: #include \"%s\" not found\n", fname, include); cur->next = Ifound; Ifound = cur; } static size_t max_rec_sz; static void parse_conf_opt(const char *opt, const char *val, size_t key_sz) { bb_key_t *cur; char *k; char *p; size_t val_sz = 0; cur = check_key(key_top, opt, key_sz); if(cur != NULL) { /* already present */ cur->checked = NULL; /* store only */ if(cur->value == NULL && val == NULL) return; if(cur->value != NULL && val != NULL && !strcmp(cur->value, val)) return; k = cur->keyname; fprintf(stderr, "Warning: redefined %s\n", k); } else { size_t recordsz; if(val && *val) val_sz = strlen(val) + 1; recordsz = key_sz + val_sz + 1; if(max_rec_sz < recordsz) max_rec_sz = recordsz; cur = xmalloc(sizeof(bb_key_t) + recordsz); k = cur->keyname = memcpy(cur + 1, opt, key_sz); cur->keyname[key_sz] = '\0'; cur->key_sz = key_sz; cur->checked = NULL; cur->src_have_this_key = NULL; cur->next = key_top; key_top = cur; } /* save VAL */ if(val) { if(*val == '\0') { cur->value = ""; } else { cur->value = p = cur->keyname + key_sz + 1; memcpy(p, val, val_sz); } } else { cur->value = NULL; } /* trick, save first char KEY for do fast identify id */ first_chars[(int)*k] = *k; cur->stored_path = k = bb_asprint("%s/%s.h", kp, k); /* key conversion [A-Z_] -> [a-z/] */ for(p = k + kp_len + 1; *p; p++) { if(*p >= 'A' && *p <= 'Z') *p = *p - 'A' + 'a'; else if(*p == '_' && p[1] > '9') /* do not change A_1 to A/1 */ *p = '/'; } } static void store_keys(void) { bb_key_t *cur; char *k; struct stat st; int cmp_ok; ssize_t rw_ret; size_t recordsz = max_rec_sz * 2 + 10 * 2 + 16; /* buffer for double "#define KEY VAL\n" */ char *record_buf = xmalloc(recordsz); for(cur = key_top; cur; cur = cur->next) { if(cur->src_have_this_key) { /* do generate record */ k = cur->keyname; if(cur->value == NULL) { recordsz = sprintf(record_buf, "#undef %s\n", k); } else { const char *val = cur->value; if(*val == '\0') { recordsz = sprintf(record_buf, "#define %s\n", k); } else { if(val[0] != '(') recordsz = sprintf(record_buf, "#define %s %s\n", k, val); else recordsz = sprintf(record_buf, "#define %s%s\n", k, val); } } /* size_t -> ssize_t :( */ rw_ret = (ssize_t)recordsz; /* check kp/key.h, compare after previous use */ cmp_ok = 0; k = cur->stored_path; if(stat(k, &st)) { char *p; for(p = k + kp_len + 1; *p; p++) { /* Auto-create directories. */ if (*p == '/') { *p = '\0'; if (access(k, F_OK) != 0 && mkdir(k, 0755) != 0) bb_error_d("mkdir(%s): %m", k); *p = '/'; } } } else { /* found */ if(st.st_size == (off_t)recordsz) { char *r_cmp; int fd; size_t padded = recordsz; /* 16-byte padding for read(2) and memcmp(3) */ padded = (padded+15) & ~15; r_cmp = record_buf + padded; fd = open(k, O_RDONLY); if(fd < 0 || read(fd, r_cmp, recordsz) < rw_ret) bb_error_d("%s: %m", k); close(fd); cmp_ok = memcmp(record_buf, r_cmp, recordsz) == 0; } } if(!cmp_ok) { int fd = open(k, O_WRONLY|O_CREAT|O_TRUNC, 0644); if(fd < 0 || write(fd, record_buf, recordsz) < rw_ret) bb_error_d("%s: %m", k); close(fd); } } } } static int show_dep(int first, bb_key_t *k, const char *name, const char *f) { bb_key_t *cur; for(cur = k; cur; cur = cur->next) { if(cur->checked) { if(first >= 0) { if(first) { if(f == NULL) printf("\n%s:", name); else printf("\n%s/%s:", pwd, name); first = 0; } else { printf(" \\\n "); } printf(" %s", cur->checked); } cur->src_have_this_key = cur->checked; cur->checked = NULL; } } return first; } static char * parse_chd(const char *fe, const char *p, size_t dirlen) { struct stat st; char *fp; size_t df_sz; static char dir_and_entry[4096]; size_t fe_sz = strlen(fe) + 1; df_sz = dirlen + fe_sz + 1; /* dir/file\0 */ if(df_sz > sizeof(dir_and_entry)) bb_error_d("%s: file name too long", fe); fp = dir_and_entry; /* sprintf(fp, "%s/%s", p, fe); */ memcpy(fp, p, dirlen); fp[dirlen] = '/'; memcpy(fp + dirlen + 1, fe, fe_sz); if(stat(fp, &st)) { fprintf(stderr, "Warning: stat(%s): %m\n", fp); return NULL; } if(S_ISREG(st.st_mode)) { llist_t *cfl; char *e = fp + df_sz - 3; if(*e++ != '.' || (*e != 'c' && *e != 'h')) { /* direntry is regular file, but is not *.[ch] */ return NULL; } for(cfl = configs; cfl; cfl = cfl->link) { struct stat *config = (struct stat *)cfl->data; if (st.st_dev == config->st_dev && st.st_ino == config->st_ino) { /* skip already parsed bb_configs.h */ return NULL; } } /* direntry is *.[ch] regular file and is not configs */ c_lex_src(fp, st.st_size); if(!dontgenerate_dep) { int first; if(*e == 'c') { /* *.c -> *.o */ *e = 'o'; /* /src_dir/path/file.o to path/file.o */ fp += replace; if(*fp == '/') fp++; } else { e = NULL; } first = show_dep(1, Ifound, fp, e); first = show_dep(first, key_top, fp, e); if(first == 0) putchar('\n'); } else { show_dep(-1, key_top, NULL, NULL); } return NULL; } else if(S_ISDIR(st.st_mode)) { if (st.st_dev == st_kp.st_dev && st.st_ino == st_kp.st_ino) return NULL; /* drop scan kp/ directory */ /* direntry is directory. buff is returned */ return bb_xstrdup(fp); } /* hmm, direntry is device! */ return NULL; } /* from libbb but inlined for speed considerations */ static inline llist_t *llist_add_to(llist_t *old_head, char *new_item) { llist_t *new_head; new_head = xmalloc(sizeof(llist_t)); new_head->data = new_item; new_head->link = old_head; return(new_head); } static void scan_dir_find_ch_files(const char *p) { llist_t *dirs; llist_t *d_add; llist_t *d; struct dirent *de; DIR *dir; size_t dirlen; dirs = llist_add_to(NULL, bb_simplify_path(p)); replace = strlen(dirs->data); /* emulate recursion */ while(dirs) { d_add = NULL; while(dirs) { dir = opendir(dirs->data); if (dir == NULL) fprintf(stderr, "Warning: opendir(%s): %m\n", dirs->data); dirlen = strlen(dirs->data); while ((de = readdir(dir)) != NULL) { char *found_dir; if (de->d_name[0] == '.') continue; found_dir = parse_chd(de->d_name, dirs->data, dirlen); if(found_dir) d_add = llist_add_to(d_add, found_dir); } closedir(dir); free(dirs->data); d = dirs; dirs = dirs->link; free(d); } dirs = d_add; } } static void show_usage(void) ATTRIBUTE ((noreturn)); static void show_usage(void) { bb_error_d("%s\n%s\n", bb_mkdep_terse_options, bb_mkdep_full_options); } int main(int argc, char **argv) { char *s; int i; llist_t *fl; { /* for bb_simplify_path, this program has no chdir() */ /* libbb-like my xgetcwd() */ unsigned path_max = 512; s = xmalloc (path_max); while (getcwd (s, path_max) == NULL) { if(errno != ERANGE) bb_error_d("getcwd: %m"); free(s); s = xmalloc(path_max *= 2); } pwd = s; } while ((i = getopt(argc, argv, "I:c:dk:w")) > 0) { switch(i) { case 'I': s = bb_simplify_path(optarg); Iop = llist_add_to(Iop, s); break; case 'c': s = bb_simplify_path(optarg); configs = llist_add_to(configs, s); break; case 'd': dontgenerate_dep = 1; break; case 'k': if(kp) bb_error_d("Hmm, why multiple -k?"); kp = bb_simplify_path(optarg); break; case 'w': noiwarning = 1; break; default: show_usage(); } } /* default kp */ if(kp == NULL) kp = bb_simplify_path(INCLUDE_CONFIG_PATH); /* globals initialize */ kp_len = strlen(kp); if(stat(kp, &st_kp)) bb_error_d("stat(%s): %m", kp); if(!S_ISDIR(st_kp.st_mode)) bb_error_d("%s is not directory", kp); /* defaults */ if(Iop == NULL) Iop = llist_add_to(Iop, bb_simplify_path(LOCAL_INCLUDE_PATH)); if(configs == NULL) { s = bb_simplify_path(INCLUDE_CONFIG_KEYS_PATH); configs = llist_add_to(configs, s); } /* for c_lex */ pagesizem1 = getpagesize() - 1; for(i = 0; i < UCHAR_MAX; i++) { if(ISALNUM(i)) isalnums[i] = i; /* set unparsed chars to speed up the parser */ else if(i != CHR && i != STR && i != POUND && i != REM) first_chars[i] = ANY; } first_chars[i] = '-'; /* L_EOF */ /* parse configs */ for(fl = configs; fl; fl = fl->link) { struct stat st; if(stat(fl->data, &st)) bb_error_d("stat(%s): %m", fl->data); c_lex_config(fl->data, st.st_size); free(fl->data); /* trick for fast comparing found files with configs */ fl->data = xmalloc(sizeof(struct stat)); memcpy(fl->data, &st, sizeof(struct stat)); } /* main loop */ argv += optind; if(*argv) { while(*argv) scan_dir_find_ch_files(*argv++); } else { scan_dir_find_ch_files("."); } store_keys(); return 0; } /* partial and simplified libbb routine */ static void bb_error_d(const char *s, ...) { va_list p; va_start(p, s); vfprintf(stderr, s, p); va_end(p); putc('\n', stderr); exit(1); } static char *bb_asprint(const char *format, ...) { va_list p; int r; char *out; #ifdef __USE_GNU va_start(p, format); r = vasprintf(&out, format, p); va_end(p); #else out = xmalloc(BUFSIZ); va_start(p, format); r = vsprintf(out, format, p); va_end(p); #endif if (r < 0) bb_error_d("bb_asprint: %m"); return out; } /* partial libbb routine as is */ static char *bb_simplify_path(const char *path) { char *s, *start, *p; if (path[0] == '/') start = bb_xstrdup(path); else { /* is not libbb, but this program has no chdir() */ start = bb_asprint("%s/%s", pwd, path); } p = s = start; do { if (*p == '/') { if (*s == '/') { /* skip duplicate (or initial) slash */ continue; } else if (*s == '.') { if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */ continue; } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) { ++s; if (p > start) { while (*--p != '/'); /* omit previous dir */ } continue; } } } *++p = *s; } while (*++s); if ((p == start) || (*p != '/')) { /* not a trailing slash */ ++p; /* so keep last character */ } *p = 0; return start; }