2 * Another dependences for Makefile mashine generator
4 * Copyright (C) 2005 by Vladimir Oleynik <dzo@simtreas.ru>
7 * 1) find #define KEY VALUE or #undef KEY from include/config.h
8 * 2) save include/config/key*.h if changed after previous usage
9 * 3) recursive scan from "./" *.[ch] files, but skip scan include/config/...
10 * 4) find #include "*.h" and KEYs using, if not as #define and #undef
11 * 5) generate depend to stdout
12 * path/file.o: include/config/key*.h found_include_*.h
13 * path/inc.h: include/config/key*.h found_included_include_*.h
14 * This programm do not generate dependences for #include <...>
17 * -I local_include_path (include`s paths, default: LOCAL_INCLUDE_PATH)
18 * -d (don`t generate depend)
19 * -w (show warning if include files not found)
20 * -k include/config (default: INCLUDE_CONFIG_PATH)
21 * -c include/config.h (configs, default: INCLUDE_CONFIG_KEYS_PATH)
24 #define LOCAL_INCLUDE_PATH "include"
25 #define INCLUDE_CONFIG_PATH LOCAL_INCLUDE_PATH"/config"
26 #define INCLUDE_CONFIG_KEYS_PATH LOCAL_INCLUDE_PATH"/config.h"
29 #include <sys/types.h>
42 typedef struct BB_KEYS {
51 /* partial and simplify libbb routine */
53 void bb_error_d(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
54 char * bb_asprint(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
56 /* stolen from libbb as is */
57 typedef struct llist_s {
61 llist_t *llist_add_to(llist_t *old_head, char *new_item);
62 void *xrealloc(void *p, size_t size);
63 void *xmalloc(size_t size);
64 char *bb_xstrdup(const char *s);
65 char *bb_simplify_path(const char *path);
67 /* for lexical analyzier */
68 static bb_key_t *key_top;
70 static void parse_inc(const char *include, const char *fname);
71 static void parse_conf_opt(char *opt, const char *val, size_t rsz);
75 static bb_key_t *find_already(bb_key_t *k, const char *nk, int flg_save_new);
77 #define yy_error_d(s) bb_error_d("%s:%d hmm, %s", fname, line, s)
80 #define S 0 /* start state */
81 #define STR '"' /* string */
82 #define CHR '\'' /* char */
83 #define REM '*' /* block comment */
84 #define POUND '#' /* # */
85 #define I 'i' /* #include preprocessor`s directive */
86 #define D 'd' /* #define preprocessor`s directive */
87 #define U 'u' /* #undef preprocessor`s directive */
88 #define LI 'I' /* #include "... */
89 #define DK 'K' /* #define KEY... (config mode) */
90 #define DV 'V' /* #define KEY "... or #define KEY '... */
91 #define NLC 'n' /* \+\n */
92 #define ANY '?' /* skip unparsed . */
95 #define ID(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
97 #define ISALNUM(c) (ID(c) || (c >= '0' && c <= '9'))
99 #define getc1() do { c = (optr >= oend) ? EOF : *optr++; } while(0)
100 #define ungetc1() optr--
102 #define put_id(c) do { if(id_len == mema_id) \
103 id = xrealloc(id, mema_id += 16); \
104 id[id_len++] = c; } while(0)
106 /* stupid C lexical analizator */
107 static void c_lex(const char *fname, int flg_config_include)
109 int c = EOF; /* stupid initialize */
110 int prev_state = EOF;
114 static size_t mema_id;
115 char *id = xmalloc(mema_id=128); /* fist allocate */
116 size_t id_len = 0; /* stupid initialize */
118 unsigned char *optr, *oend;
119 unsigned char *start = NULL; /* stupid initialize */
125 /* stolen from mkdep by Linus Torvalds */
126 int pagesizem1 = getpagesize() - 1;
129 fd = open(fname, O_RDONLY);
136 bb_error_d("%s is empty", fname);
137 mapsize = st.st_size;
138 mapsize = (mapsize+pagesizem1) & ~pagesizem1;
139 map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
140 if ((long) map == -1)
141 bb_error_d("%s: mmap: %m", fname);
143 /* hereinafter is my */
144 optr = (unsigned char *)map;
145 oend = optr + st.st_size;
152 if(state == LI || state == DV) {
153 /* store "include.h" or config mode #define KEY "|'..."|' */
156 parse_inc(id, fname);
160 yy_error_d("expected value");
162 parse_conf_opt(id, val, (optr - start));
166 if(prev_state != state) {
171 /* [ \t]+ eat first space */
172 while(c == ' ' || c == '\t')
178 /* \\\n eat continued */
188 while(c <= ' ' && c != EOF) {
196 munmap(map, mapsize);
205 do getc1(); while(c != '\n' && c != EOF);
206 } else if(c == '*') {
212 } else if(c == '#') {
216 } else if(c == STR || c == CHR) {
221 } else if(ISALNUM(c)) {
222 /* <S>[A-Z_a-z0-9] */
225 /* <S>[A-Z_a-z0-9]+ */
230 find_already(key_top, id, CHECK_ONLY);
244 yy_error_d("unexpected newline");
247 yy_error_d("unexpected EOF");
260 if(state == STR || state == CHR) {
262 /* <STR,CHR>\n|<<EOF>> */
263 if(c == '\n' || c == EOF)
264 yy_error_d("unterminating");
268 if(c != '\\' && c != '\n' && c != state) {
269 /* another usage \ in str or char */
271 yy_error_d("unexpected EOF");
276 /* <STR,CHR>\\[\\\n] or <STR>\\\" or <CHR>\\\' */
282 } else if(c == state) {
283 /* <STR>\" or <CHR>\' */
296 /* begin preprocessor states */
298 yy_error_d("unexpected EOF");
303 yy_error_d("detect // in preprocessor line");
311 yy_error_d("strange preprocessor line");
314 static const char * const preproc[] = {
315 "define", "undef", "include", ""
317 const char * const *str_type;
325 for(str_type = preproc; (state = **str_type); str_type++) {
326 if(*id == state && strcmp(id, *str_type) == 0)
329 /* to S if another #directive */
331 id_len = 0; /* common for save */
342 /* another (may be wrong) #include ... */
347 if(state == D || state == U) {
349 if(flg_config_include) {
350 /* save KEY from #"define"|"undef" ... */
355 if(!flg_config_include) {
359 yy_error_d("expected identificator");
362 parse_conf_opt(id, NULL, (optr - start));
373 /* #define (config mode) */
375 if(c == STR || c == CHR) {
376 /* define KEY "... or define KEY '... */
394 static void show_usage(void) __attribute__ ((noreturn));
395 static void show_usage(void)
397 bb_error_d("Usage: [-I local_include_path] [-dw] "
398 "[-k path_for_store_keys] [-s skip_file]");
401 static const char *kp;
403 static bb_key_t *Ifound;
404 static int noiwarning;
405 static llist_t *configs;
407 static bb_key_t *find_already(bb_key_t *k, const char *nk, int flg_save_new)
411 for(cur = k; cur; cur = cur->next) {
412 if(strcmp(cur->keyname, nk) == 0) {
417 if(flg_save_new == CHECK_ONLY)
419 cur = xmalloc(sizeof(bb_key_t));
420 cur->keyname = bb_xstrdup(nk);
426 static int store_include_fullpath(char *p_i, bb_key_t *li)
431 if(stat(p_i, &st) == 0) {
432 li->stored_path = bb_simplify_path(p_i);
439 static void parse_inc(const char *include, const char *fname)
445 if((li = find_already(Ifound, include, MAKE_NEW)) == NULL)
448 if(include[0] != '/') {
453 p_i = strrchr(fname, '/');
461 p_i = bb_asprint("%.*s/%s", w, p, include);
462 if(store_include_fullpath(p_i, li))
465 for(lo = Iop; lo; lo = lo->link) {
466 p_i = bb_asprint("%s/%s", lo->data, include);
467 if(store_include_fullpath(p_i, li))
470 li->stored_path = NULL;
472 fprintf(stderr, "%s: Warning: #include \"%s\" not found in specified paths\n", fname, include);
475 static void parse_conf_opt(char *opt, const char *val, size_t recordsz)
477 bb_key_t *cur = find_already(key_top, opt, MAKE_NEW);
480 /* new key, check old key if present after previous usage */
485 static char *record_buf;
489 recordsz += 2; /* \n\0 */
490 if(recordsz > r_sz) {
491 record_buf = xrealloc(record_buf, r_sz=recordsz);
492 r_cmp = xrealloc(r_cmp, recordsz);
496 sprintf(s, "#define %s%s%s\n", opt, (*val ? " " : ""), val);
498 sprintf(s, "#undef %s\n", opt);
499 /* may be short count " " */
500 recordsz = strlen(s);
501 /* key converting [A-Z] -> [a-z] */
502 for(p = opt; *p; p++) {
503 if(*p >= 'A' && *p <= 'Z')
508 p = bb_asprint("%s/%s.h", kp, opt);
509 cur->stored_path = opt = p;
511 /* Auto-create directories. */
514 if (stat(opt, &st) != 0 && mkdir(opt, 0755) != 0)
515 bb_error_d("mkdir(%s): %m", opt);
519 if(stat(opt, &st) == 0) {
521 if(st.st_size == recordsz) {
522 fd = open(opt, O_RDONLY);
523 if(fd < 0 || read(fd, r_cmp, recordsz) != recordsz)
524 bb_error_d("%s: %m", opt);
526 cmp_ok = memcmp(s, r_cmp, recordsz) == 0;
530 fd = open(opt, O_WRONLY|O_CREAT|O_TRUNC, 0644);
531 if(fd < 0 || write(fd, s, recordsz) != recordsz)
532 bb_error_d("%s: %m", opt);
541 cur->value = bb_xstrdup(val);
548 /* present already */
549 for(cur = key_top; cur; cur = cur->next) {
550 if(strcmp(cur->keyname, opt) == 0) {
552 if(cur->value == NULL && val == NULL)
554 if((cur->value == NULL && val != NULL) ||
555 (cur->value != NULL && val == NULL) ||
556 strcmp(cur->value, val))
557 fprintf(stderr, "Warning: redefined %s\n", opt);
564 static int show_dep(int first, bb_key_t *k, const char *a)
568 for(cur = k; cur; cur = cur->next) {
569 if(cur->checked && cur->stored_path) {
573 if(*a == '.' && a[1] == '/')
575 ext = strrchr(a, '.');
576 if(ext && ext[1] == 'c' && ext[2] == '\0') {
578 printf("\n%.*s.o:", (ext - a), a);
586 printf(" %s", cur->stored_path);
593 static llist_t *files;
595 static llist_t *filter_chd(const char *fe, const char *p, llist_t *pdirs)
605 fp = bb_asprint("%s/%s", p, fe);
607 fprintf(stderr, "Warning: stat(%s): %m", fp);
611 afp = bb_simplify_path(fp);
612 if(S_ISDIR(st.st_mode)) {
613 if(strcmp(kp, afp) == 0) {
614 /* is autogenerated to kp/key* by previous usage */
617 /* drop scan kp/ directory */
621 return llist_add_to(pdirs, fp);
623 if(!S_ISREG(st.st_mode)) {
624 /* hmm, is device! */
629 e = strrchr(fe, '.');
630 if(e == NULL || !((e[1]=='c' || e[1]=='h') && e[2]=='\0')) {
631 /* direntry is not directory or *.[ch] */
636 for(cfl = configs; cfl; cfl = cfl->link) {
637 if(cfl->data && strcmp(cfl->data, afp) == 0) {
638 /* parse configs.h */
648 /* direntry is *.[ch] regular file */
649 files = llist_add_to(files, afp);
653 static void scan_dir_find_ch_files(char *p)
661 dirs = llist_add_to(NULL, p);
662 /* emulate recursive */
666 dir = opendir(dirs->data);
668 fprintf(stderr, "Warning: opendir(%s): %m", dirs->data);
669 while ((de = readdir(dir)) != NULL) {
670 d = filter_chd(de->d_name, dirs->data, d_add);
685 int main(int argc, char **argv)
687 int generate_dep = 1;
692 while ((i = getopt(argc, argv, "I:c:dk:w")) > 0) {
695 Iop = llist_add_to(Iop, optarg);
698 s = bb_simplify_path(optarg);
699 configs = llist_add_to(configs, s);
706 bb_error_d("Hmm, why multiple -k?");
707 kp = bb_simplify_path(optarg);
721 kp = bb_simplify_path(INCLUDE_CONFIG_PATH);
723 Iop = llist_add_to(Iop, LOCAL_INCLUDE_PATH);
724 if(configs == NULL) {
725 s = bb_simplify_path(INCLUDE_CONFIG_KEYS_PATH);
726 configs = llist_add_to(configs, s);
728 scan_dir_find_ch_files(".");
730 for(fl = files; fl; fl = fl->link) {
733 i = show_dep(1, Ifound, fl->data);
734 i = show_dep(i, key_top, fl->data);
742 void bb_error_d(const char *s, ...)
747 vfprintf(stderr, s, p);
754 void *xmalloc(size_t size)
756 void *p = malloc(size);
759 bb_error_d("memory exhausted");
763 void *xrealloc(void *p, size_t size) {
764 p = realloc(p, size);
766 bb_error_d("memory exhausted");
770 char *bb_asprint(const char *format, ...)
777 r = vasprintf(&out, format, p);
781 bb_error_d("bb_asprint: %m");
785 llist_t *llist_add_to(llist_t *old_head, char *new_item)
789 new_head = xmalloc(sizeof(llist_t));
790 new_head->data = new_item;
791 new_head->link = old_head;
796 char *bb_xstrdup(const char *s)
800 bb_error_d("memory exhausted");
804 char *bb_simplify_path(const char *path)
809 start = bb_xstrdup(path);
814 /* is not libbb, but this program have not chdir() */
815 unsigned path_max = 512;
816 char *cwd = xmalloc (path_max);
818 while (getcwd (cwd, path_max) == NULL) {
820 bb_error_d("getcwd: %m");
821 path_max += PATH_INCR;
822 cwd = xrealloc (cwd, path_max);
826 start = bb_asprint("%s/%s", pwd, path);
832 if (*s == '/') { /* skip duplicate (or initial) slash */
834 } else if (*s == '.') {
835 if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
837 } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
840 while (*--p != '/'); /* omit previous dir */
849 if ((p == start) || (*p != '/')) { /* not a trailing slash */
850 ++p; /* so keep last character */