removed 'less' section from TODO
[oweals/busybox.git] / scripts / bb_mkdep.c
1 /*
2  * Another fast dependencies generator for Makefiles, Version 2.0
3  *
4  * Copyright (C) 2005 by Vladimir Oleynik <dzo@simtreas.ru>
5  *
6  * This program does:
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 skips scan of include/config/
10  * 4) find #include "*.h" and KEYs using, if not as #define and #undef
11  * 5) generate dependencies 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 does not generate dependencies for #include <...>
15  */
16
17 #define LOCAL_INCLUDE_PATH          "include"
18 #define INCLUDE_CONFIG_PATH         LOCAL_INCLUDE_PATH"/config"
19 #define INCLUDE_CONFIG_KEYS_PATH    LOCAL_INCLUDE_PATH"/config.h"
20
21 #define bb_mkdep_full_options \
22 "\nOptions:" \
23 "\n\t-I local_include_path  include paths, default: \"" LOCAL_INCLUDE_PATH "\"" \
24 "\n\t-d                     don't generate depend" \
25 "\n\t-w                     show warning if include files not found" \
26 "\n\t-k include/config      default: \"" INCLUDE_CONFIG_PATH "\"" \
27 "\n\t-c include/config.h    configs, default: \"" INCLUDE_CONFIG_KEYS_PATH "\"" \
28 "\n\tdirs_to_scan           default \".\""
29
30 #define bb_mkdep_terse_options "Usage: [-I local_include_paths] [-dw] " \
31                    "[-k path_for_stored_keys] [dirs]"
32
33
34
35 #define _GNU_SOURCE
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <getopt.h>
40 #include <dirent.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <fcntl.h>
48
49
50 typedef struct BB_KEYS {
51         char *keyname;
52         const char *value;
53         char *stored_path;
54         int  checked;
55         struct BB_KEYS *next;
56 } bb_key_t;
57
58 static bb_key_t *check_key(bb_key_t *k, const char *nk);
59 static bb_key_t *make_new_key(bb_key_t *k, const char *nk);
60
61 /* partial and simplified libbb routine */
62 static void bb_error_d(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
63 static char * bb_asprint(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
64
65 /* stolen from libbb as is */
66 typedef struct llist_s {
67         char *data;
68         struct llist_s *link;
69 } llist_t;
70 static void *xrealloc(void *p, size_t size);
71 static void *xmalloc(size_t size);
72 static char *bb_xstrdup(const char *s);
73 static char *bb_simplify_path(const char *path);
74 /* error messages */
75 static const char msg_enomem[] = "memory exhausted";
76
77 /* for lexical analyser */
78 static bb_key_t *key_top;
79 static llist_t *configs;
80
81 static int mode;
82 #define CONFIG_MODE  0
83 #define SOURCES_MODE 1
84
85 static void parse_inc(const char *include, const char *fname);
86 static void parse_conf_opt(char *opt, const char *val, size_t rsz);
87
88 /* for speed tricks */
89 static char first_chars[257];  /* + L_EOF */
90 /* trick for fast find "define", "include", "undef" */
91 static char first_chars_diu[256];
92
93 static int pagesizem1;
94 static size_t mema_id = 128;   /* first allocated for id */
95 static char *id_s;
96
97
98
99 #define yy_error_d(s) bb_error_d("%s:%d hmm, %s", fname, line, s)
100
101 /* state */
102 #define S      0        /* start state */
103 #define STR    '"'      /* string */
104 #define CHR    '\''     /* char */
105 #define REM    '/'      /* block comment */
106 #define BS     '\\'     /* back slash */
107 #define POUND  '#'      /* # */
108 #define I      'i'      /* #include preprocessor's directive */
109 #define D      'd'      /* #define preprocessor's directive */
110 #define U      'u'      /* #undef preprocessor's directive */
111 #define LI     'I'      /* #include "... */
112 #define DK     'K'      /* #define KEY... (config mode) */
113 #define DV     'V'      /* #define KEY "VALUE or #define KEY 'VALUE */
114 #define NLC    'n'      /* \ and \n */
115 #define ANY    '*'      /* any unparsed chars */
116
117 #define L_EOF  256
118 /* [A-Z_a-z] */
119 #define ID(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
120 /* [A-Z_a-z0-9] */
121 #define ISALNUM(c)  (ID(c) || (c >= '0' && c <= '9'))
122
123 #define getc1()     do { c = (optr >= oend) ? L_EOF : *optr++; } while(0)
124 #define ungetc1()   optr--
125
126 #define put_id(c)   do {    if(id_len == local_mema_id)                 \
127                                 id = xrealloc(id, local_mema_id += 16); \
128                             id[id_len++] = c; } while(0)
129
130
131
132 /* stupid C lexical analyser */
133 static void c_lex(const char *fname, long fsize)
134 {
135   int c = L_EOF;                     /* stupid initialize */
136   int prev_state = L_EOF;
137   int called;
138   int state;
139   int line;
140   char *id = id_s;
141   size_t local_mema_id = mema_id;
142   size_t id_len = 0;                /* stupid initialize */
143   char *val = NULL;
144   unsigned char *optr, *oend;
145   unsigned char *start = NULL;      /* stupid initialize */
146
147   int fd;
148   char *map;
149   int mapsize;
150
151   if(fsize == 0) {
152     fprintf(stderr, "Warning: %s is empty\n", fname);
153     return;
154   }
155   fd = open(fname, O_RDONLY);
156   if(fd < 0) {
157         perror(fname);
158         return;
159   }
160   mapsize = (fsize+pagesizem1) & ~pagesizem1;
161   map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
162   if ((long) map == -1)
163         bb_error_d("%s: mmap: %m", fname);
164
165   optr = (unsigned char *)map;
166   oend = optr + fsize;
167
168   line = 1;
169   called = state = S;
170
171   for(;;) {
172         if(state == LI || state == DV) {
173             /* store "include.h" or config mode #define KEY "|'..."|'  */
174             put_id(0);
175             if(state == LI) {
176                 parse_inc(id, fname);
177             } else {
178                 /* #define KEY "[VAL]" */
179                 parse_conf_opt(id, val, (optr - start));
180             }
181             state = S;
182         }
183         if(prev_state != state) {
184             prev_state = state;
185             getc1();
186         }
187
188         /* [ \t]+ eat first space */
189         while(c == ' ' || c == '\t')
190             getc1();
191
192         if(c == BS) {
193                 getc1();
194                 if(c == '\n') {
195                         /* \\\n eat continued */
196                         line++;
197                         prev_state = NLC;
198                         continue;
199                 }
200                 ungetc1();
201                 c = BS;
202         }
203
204         if(state == S) {
205                 while(first_chars[c] == ANY) {
206                     /* <S>unparsed */
207                     if(c == '\n')
208                         line++;
209                     getc1();
210                 }
211                 if(c == L_EOF) {
212                         /* <S><<EOF>> */
213                         id_s = id;
214                         mema_id = local_mema_id;
215                         munmap(map, mapsize);
216                         close(fd);
217                         return;
218                 }
219                 if(c == REM) {
220                         /* <S>/ */
221                         getc1();    /* eat <S>/ */
222                         if(c == REM) {
223                                 /* <S>"//"[^\n]* */
224                                 do getc1(); while(c != '\n' && c != L_EOF);
225                         } else if(c == '*') {
226                                 /* <S>[/][*] */
227                                 called = S;
228                                 state = REM;
229                         }
230                 } else if(c == POUND) {
231                         /* <S># */
232                         start = optr - 1;
233                         state = c;
234                 } else if(c == STR || c == CHR) {
235                         /* <S>\"|\' */
236                         val = NULL;
237                         called = S;
238                         state = c;
239                 } else if(c != BS) {
240                         /* <S>[A-Z_a-z0-9] */
241
242                         /* trick for fast drop id
243                            if key with this first char undefined */
244                         if(first_chars[c] == 0) {
245                             /* skip <S>[A-Z_a-z0-9]+ */
246                             do getc1(); while(ISALNUM(c));
247                         } else {
248                             id_len = 0;
249                             do {
250                                 /* <S>[A-Z_a-z0-9]+ */
251                                 put_id(c);
252                                 getc1();
253                             } while(ISALNUM(c));
254                             put_id(0);
255                             check_key(key_top, id);
256                         }
257                 } else {
258                     /* <S>\\ */
259                     prev_state = c;
260                 }
261                 continue;
262         }
263         if(state == REM) {
264           for(;;) {
265                 /* <REM>[^*]+ */
266                 while(c != '*') {
267                         if(c == '\n') {
268                                 /* <REM>\n */
269                                 if(called != S)
270                                     yy_error_d("unexpected newline");
271                                 line++;
272                         } else if(c == L_EOF)
273                                 yy_error_d("unexpected EOF");
274                         getc1();
275                 }
276                 /* <REM>[*] */
277                 getc1();
278                 if(c == REM) {
279                         /* <REM>[*][/] */
280                         state = called;
281                         break;
282                 }
283           }
284           continue;
285         }
286         if(state == STR || state == CHR) {
287             for(;;) {
288                 /* <STR,CHR>\n|<<EOF>> */
289                 if(c == '\n' || c == L_EOF)
290                         yy_error_d("unterminated");
291                 if(c == BS) {
292                         /* <STR,CHR>\\ */
293                         getc1();
294                         if(c != BS && c != '\n' && c != state) {
295                             /* another usage \ in str or char */
296                             if(c == L_EOF)
297                                 yy_error_d("unexpected EOF");
298                             if(val)
299                                 put_id(c);
300                             continue;
301                         }
302                         /* <STR,CHR>\\[\\\n] or <STR>\\\" or <CHR>\\\' */
303                         /* eat 2 char */
304                         if(c == '\n')
305                             line++;
306                         else if(val)
307                             put_id(c);
308                 } else if(c == state) {
309                         /* <STR>\" or <CHR>\' */
310                         if(called == DV)
311                             put_id(c);  /* #define KEY "VALUE"<- */
312                         state = called;
313                         break;
314                 } else if(val)
315                         put_id(c);
316                 /* <STR,CHR>. */
317                 getc1();
318             }
319             continue;
320         }
321
322         /* begin preprocessor states */
323         if(c == L_EOF)
324             yy_error_d("unexpected EOF");
325         if(c == REM) {
326                 /* <#.*>/ */
327                 getc1();
328                 if(c == REM)
329                         yy_error_d("detected // in preprocessor line");
330                 if(c == '*') {
331                         /* <#.*>[/][*] */
332                         called = state;
333                         state = REM;
334                         continue;
335                 }
336                 /* hmm, #.*[/] */
337                 yy_error_d("strange preprocessor line");
338         }
339         if(state == POUND) {
340             const unsigned char *p = optr - 1;
341             /* tricks */
342             static const char * const preproc[] = {
343                     /* 0-4 */
344                     "", "", "", "", "",
345                     /* 5 */   /* 6 */   /* 7 */
346                     "undef", "define", "include",
347             };
348             size_t readed = 0;
349             size_t diu = first_chars_diu[c];   /* strlen and preproc ptr */
350
351             while(ISALNUM(c)) {
352                 readed++;
353                 getc1();
354             }
355             /* have str begined with c, readed == strlen key and compared */
356             if(diu != S && diu == readed && !memcmp(p, preproc[diu], diu)) {
357                 state = *p;
358                 id_len = 0; /* common for save */
359             } else {
360                 state = S;
361             }
362             ungetc1();
363             continue;
364         }
365         if(state == I) {
366                 if(c == STR) {
367                         /* <I>\" */
368                         val = id;
369                         called = LI;
370                         state = STR;
371                 } else {
372                     /* another (may be wrong) #include ... */
373                     ungetc1();
374                     state = S;
375                 }
376                 continue;
377         }
378         if(state == D || state == U) {
379             if(mode == SOURCES_MODE) {
380                 /* ignore depend with #define or #undef KEY */
381                 while(ISALNUM(c))
382                     getc1();
383                 state = S;
384             } else {
385                 /* save KEY from #"define"|"undef" ... */
386                 while(ISALNUM(c)) {
387                     put_id(c);
388                     getc1();
389                 }
390                 if(!id_len)
391                     yy_error_d("expected identifier");
392                 put_id(0);
393                 if(state == U) {
394                     parse_conf_opt(id, NULL, (optr - start));
395                     state = S;
396                 } else {
397                     /* D -> DK */
398                     state = DK;
399                 }
400             }
401             ungetc1();
402             continue;
403         }
404         if(state == DK) {
405             /* #define KEY[ ] (config mode) */
406             val = id + id_len;
407             if(c == STR || c == CHR) {
408                 /* define KEY "... or define KEY '... */
409                 put_id(c);
410                 called = DV;
411                 state = c;
412                 continue;
413             }
414             while(ISALNUM(c)) {
415                 /* VALUE */
416                 put_id(c);
417                 getc1();
418             }
419             ungetc1();
420             state = DV;
421             continue;
422         }
423     }
424 }
425
426
427 static void show_usage(void) __attribute__ ((noreturn));
428 static void show_usage(void)
429 {
430         bb_error_d("%s\n%s\n", bb_mkdep_terse_options, bb_mkdep_full_options);
431 }
432
433 static const char *kp;
434 static size_t kp_len;
435 static llist_t *Iop;
436 static bb_key_t *Ifound;
437 static int noiwarning;
438
439 static bb_key_t *check_key(bb_key_t *k, const char *nk)
440 {
441     bb_key_t *cur;
442
443     for(cur = k; cur; cur = cur->next) {
444         if(strcmp(cur->keyname, nk) == 0) {
445             cur->checked = 1;
446             return cur;
447         }
448     }
449     return NULL;
450 }
451
452 static bb_key_t *make_new_key(bb_key_t *k, const char *nk)
453 {
454         bb_key_t *cur;
455         size_t nk_size;
456
457         nk_size = strlen(nk) + 1;
458         cur = xmalloc(sizeof(bb_key_t) + nk_size);
459         cur->keyname = memcpy(cur + 1, nk, nk_size);
460         cur->checked = 0;
461         cur->next = k;
462         return cur;
463 }
464
465 static inline char *store_include_fullpath(char *p_i, bb_key_t *li)
466 {
467     char *ok;
468
469     if(access(p_i, F_OK) == 0) {
470         ok = li->stored_path = bb_simplify_path(p_i);
471         li->checked = 1;
472     } else {
473         ok = NULL;
474     }
475     free(p_i);
476     return ok;
477 }
478
479 static void parse_inc(const char *include, const char *fname)
480 {
481         bb_key_t *li;
482         char *p_i;
483         llist_t *lo;
484
485         li = check_key(Ifound, include);
486         if(li)
487             return;
488         Ifound = li = make_new_key(Ifound, include);
489
490         if(include[0] != '/') {
491             /* relative */
492             int w;
493             const char *p;
494
495             p_i = strrchr(fname, '/');
496             if(p_i == NULL) {
497                 p = ".";
498                 w = 1;
499             } else {
500                 w = (p_i-fname);
501                 p = fname;
502             }
503             p_i = bb_asprint("%.*s/%s", w, p, include);
504             if(store_include_fullpath(p_i, li))
505                 return;
506             for(lo = Iop; lo; lo = lo->link) {
507                 p_i = bb_asprint("%s/%s", lo->data, include);
508                 if(store_include_fullpath(p_i, li))
509                     return;
510             }
511         } else {
512             /* absolute include pathname */
513             if(access(include, F_OK) == 0) {
514                 li->stored_path = bb_xstrdup(include);
515                 li->checked = 1;
516                 return;
517             }
518         }
519         li->stored_path = NULL;
520         if(noiwarning)
521             fprintf(stderr, "%s: Warning: #include \"%s\" not found in specified paths\n", fname, include);
522 }
523
524 static void parse_conf_opt(char *opt, const char *val, size_t recordsz)
525 {
526         bb_key_t *cur;
527         char *s, *p;
528         struct stat st;
529         int fd;
530         int cmp_ok = 0;
531         static char *record_buf;
532         static char *r_cmp;
533         static size_t r_sz;
534         ssize_t rw_ret;
535
536         cur = check_key(key_top, opt);
537         if(cur != NULL) {
538             /* present already */
539             cur->checked = 0;   /* store only */
540             if(cur->value == NULL && val == NULL)
541                 return;
542             if(cur->value != NULL && val != NULL && strcmp(cur->value, val) == 0)
543                 return;
544             fprintf(stderr, "Warning: redefined %s\n", opt);
545         }
546         /* new or redefined key, check old key if present after previous usage */
547         key_top = cur = make_new_key(key_top, opt);
548
549         recordsz += 2;  /* \n\0 */
550         if(recordsz > r_sz) {
551             record_buf = xrealloc(record_buf, r_sz=recordsz);
552             r_cmp = xrealloc(r_cmp, recordsz);
553         }
554         s = record_buf;
555         /* may be short count " " */
556         if(val) {
557             if(*val == '\0') {
558                 cur->value = "";
559                 recordsz = sprintf(s, "#define %s\n", opt);
560             } else {
561                 cur->value = bb_xstrdup(val);
562                 recordsz = sprintf(s, "#define %s %s\n", opt, val);
563             }
564         } else {
565             cur->value = NULL;
566             recordsz = sprintf(s, "#undef %s\n", opt);
567         }
568         /* size_t -> ssize_t :( */
569         rw_ret = (ssize_t)recordsz;
570         /* trick, save first char KEY for do fast identify id */
571         first_chars[(int)*opt] = *opt;
572
573         /* key converting [A-Z_] -> [a-z/] */
574         for(p = opt; *p; p++) {
575             if(*p >= 'A' && *p <= 'Z')
576                     *p = *p - 'A' + 'a';
577             else if(*p == '_')
578                     *p = '/';
579         }
580         p = bb_asprint("%s/%s.h", kp, opt);
581         cur->stored_path = opt = p;
582         if(stat(opt, &st)) {
583             p += kp_len;
584             while(*++p) {
585                 /* Auto-create directories. */
586                 if (*p == '/') {
587                     *p = '\0';
588                     if (access(opt, F_OK) != 0 && mkdir(opt, 0755) != 0)
589                         bb_error_d("mkdir(%s): %m", opt);
590                     *p = '/';
591                 }
592             }
593         } else {
594                 /* found */
595                 if(st.st_size == (off_t)recordsz) {
596                     fd = open(opt, O_RDONLY);
597                     if(fd < 0 || read(fd, r_cmp, recordsz) < rw_ret)
598                         bb_error_d("%s: %m", opt);
599                     close(fd);
600                     cmp_ok = memcmp(s, r_cmp, recordsz) == 0;
601                 }
602         }
603         if(!cmp_ok) {
604             fd = open(opt, O_WRONLY|O_CREAT|O_TRUNC, 0644);
605             if(fd < 0 || write(fd, s, recordsz) < rw_ret)
606                 bb_error_d("%s: %m", opt);
607             close(fd);
608         }
609 }
610
611 static int show_dep(int first, bb_key_t *k, const char *name)
612 {
613     bb_key_t *cur;
614
615     for(cur = k; cur; cur = cur->next) {
616         if(cur->checked && cur->stored_path) {
617             if(first) {
618                 printf("\n%s:", name);
619                 first = 0;
620             } else {
621                 printf(" \\\n  ");
622             }
623             printf(" %s", cur->stored_path);
624         }
625         cur->checked = 0;
626     }
627     return first;
628 }
629
630 static struct stat st_kp;
631 static int dontgenerate_dep;
632
633 static char *
634 parse_chd(const char *fe, const char *p, size_t dirlen)
635 {
636     struct stat st;
637     char *fp;
638     size_t df_sz;
639     static char *dir_and_entry;
640     static size_t dir_and_entry_sz;
641
642     if (*fe == '.')
643         return NULL;
644
645     df_sz = dirlen + strlen(fe) + 2;     /* dir/file\0 */
646     if(df_sz > dir_and_entry_sz)
647         dir_and_entry = xrealloc(dir_and_entry, dir_and_entry_sz = df_sz);
648     fp = dir_and_entry;
649     sprintf(fp, "%s/%s", p, fe);
650
651     if(stat(fp, &st)) {
652         fprintf(stderr, "Warning: stat(%s): %m", fp);
653         return NULL;
654     }
655     if(S_ISREG(st.st_mode)) {
656         llist_t *cfl;
657         char *e = fp + df_sz - 3;
658
659         if(*e++ != '.' || (*e != 'c' && *e != 'h')) {
660             /* direntry is regular file, but is not *.[ch] */
661             return NULL;
662         }
663         for(cfl = configs; cfl; cfl = cfl->link) {
664             struct stat *config = (struct stat *)cfl->data;
665
666             if (st.st_dev == config->st_dev && st.st_ino == config->st_ino) {
667                 /* skip already parsed configs.h */
668                 return NULL;
669             }
670         }
671         /* direntry is *.[ch] regular file and is not configs */
672         if(!dontgenerate_dep) {
673             int first;
674
675             c_lex(fp, st.st_size);
676             fp = bb_simplify_path(fp);
677             if(*e == 'c') {
678                 /* *.c -> *.o */
679                 e = strrchr(fp, '.') + 1;
680                 *e = 'o';
681             }
682             first = show_dep(1, Ifound, fp);
683             first = show_dep(first, key_top, fp);
684             if(first == 0)
685                 putchar('\n');
686             free(fp);
687         }
688         return NULL;
689     } else if(S_ISDIR(st.st_mode)) {
690         if (st.st_dev == st_kp.st_dev && st.st_ino == st_kp.st_ino)
691             return NULL;    /* drop scan kp/ directory */
692         /* direntry is directory. buff is returned, begin of zero allocate */
693         dir_and_entry = NULL;
694         dir_and_entry_sz = 0;
695         return fp;
696     }
697     /* hmm, direntry is device! */
698     return NULL;
699 }
700
701 /* from libbb but inline for fast */
702 static inline llist_t *llist_add_to(llist_t *old_head, char *new_item)
703 {
704         llist_t *new_head;
705
706         new_head = xmalloc(sizeof(llist_t));
707         new_head->data = new_item;
708         new_head->link = old_head;
709
710         return(new_head);
711 }
712
713 static void scan_dir_find_ch_files(char *p)
714 {
715     llist_t *dirs;
716     llist_t *d_add;
717     llist_t *d;
718     struct dirent *de;
719     DIR *dir;
720     size_t dirlen;
721
722     dirs = llist_add_to(NULL, p);
723     /* emulate recursive */
724     while(dirs) {
725         d_add = NULL;
726         while(dirs) {
727             dir = opendir(dirs->data);
728             if (dir == NULL)
729                 fprintf(stderr, "Warning: opendir(%s): %m", dirs->data);
730             dirlen = strlen(dirs->data);
731             while ((de = readdir(dir)) != NULL) {
732                 char *found_dir = parse_chd(de->d_name, dirs->data, dirlen);
733
734                 if(found_dir)
735                     d_add = llist_add_to(d_add, found_dir);
736             }
737             closedir(dir);
738             if(dirs->data != p)
739                 free(dirs->data);
740             d = dirs;
741             dirs = dirs->link;
742             free(d);
743         }
744         dirs = d_add;
745     }
746 }
747
748 static char *pwd;
749
750 int main(int argc, char **argv)
751 {
752         char *s;
753         int i;
754         llist_t *fl;
755
756         {
757             /* for bb_simplify_path */
758             /* libbb xgetcwd(), this program have not chdir() */
759             unsigned path_max = 512;
760
761             s = xmalloc (path_max);
762 #define PATH_INCR 32
763             while (getcwd (s, path_max) == NULL) {
764                 if(errno != ERANGE)
765                     bb_error_d("getcwd: %m");
766                 s = xrealloc (s, path_max += PATH_INCR);
767                 }
768             pwd = s;
769         }
770
771         while ((i = getopt(argc, argv, "I:c:dk:w")) > 0) {
772                 switch(i) {
773                     case 'I':
774                             Iop = llist_add_to(Iop, optarg);
775                             break;
776                     case 'c':
777                             s = bb_simplify_path(optarg);
778                             configs = llist_add_to(configs, s);
779                             break;
780                     case 'd':
781                             dontgenerate_dep = 1;
782                             break;
783                     case 'k':
784                             if(kp)
785                                 bb_error_d("Hmm, why multiple -k?");
786                             kp = bb_simplify_path(optarg);
787                             break;
788                     case 'w':
789                             noiwarning = 1;
790                             break;
791                     default:
792                             show_usage();
793                 }
794         }
795         /* default kp */
796         if(kp == NULL)
797             kp = bb_simplify_path(INCLUDE_CONFIG_PATH);
798         /* globals initialize */
799         kp_len = strlen(kp);
800         if(stat(kp, &st_kp))
801             bb_error_d("stat(%s): %m", kp);
802         if(!S_ISDIR(st_kp.st_mode))
803             bb_error_d("%s is not directory", kp);
804         /* defaults */
805         if(Iop == NULL)
806             Iop = llist_add_to(Iop, LOCAL_INCLUDE_PATH);
807         if(configs == NULL) {
808             s = bb_simplify_path(INCLUDE_CONFIG_KEYS_PATH);
809             configs = llist_add_to(configs, s);
810         }
811         /* for c_lex */
812         pagesizem1 = getpagesize() - 1;
813         id_s = xmalloc(mema_id);
814         for(i = 0; i < 256; i++) {
815             /* set unparsed chars for speed up of parser */
816             if(!ISALNUM(i) && i != CHR && i != STR &&
817                               i != POUND && i != REM && i != BS)
818                 first_chars[i] = ANY;
819         }
820         first_chars[i] = '-';   /* L_EOF */
821         /* trick for fast find "define", "include", "undef" */
822         first_chars_diu[(int)'d'] = (char)6;    /* strlen("define");  */
823         first_chars_diu[(int)'i'] = (char)7;    /* strlen("include"); */
824         first_chars_diu[(int)'u'] = (char)5;    /* strlen("undef");   */
825
826         /* parse configs */
827         for(fl = configs; fl; fl = fl->link) {
828             struct stat st;
829
830             if(stat(fl->data, &st))
831                 bb_error_d("stat(%s): %m", fl->data);
832             c_lex(fl->data, st.st_size);
833             /* trick for fast comparing found files with configs */
834             fl->data = xrealloc(fl->data, sizeof(struct stat));
835             memcpy(fl->data, &st, sizeof(struct stat));
836         }
837
838         /* main loop */
839         mode = SOURCES_MODE;
840         argv += optind;
841         if(*argv) {
842             while(*argv)
843                 scan_dir_find_ch_files(*argv++);
844         } else {
845                 scan_dir_find_ch_files(".");
846         }
847         return 0;
848 }
849
850 /* partial and simplified libbb routine */
851 static void bb_error_d(const char *s, ...)
852 {
853         va_list p;
854
855         va_start(p, s);
856         vfprintf(stderr, s, p);
857         va_end(p);
858         putc('\n', stderr);
859         exit(1);
860 }
861
862 static char *bb_asprint(const char *format, ...)
863 {
864         va_list p;
865         int r;
866         char *out;
867
868         va_start(p, format);
869         r = vasprintf(&out, format, p);
870         va_end(p);
871
872         if (r < 0)
873                 bb_error_d("bb_asprint: %m");
874         return out;
875 }
876
877 /* partial libbb routine as is */
878 static void *xmalloc(size_t size)
879 {
880         void *p = malloc(size);
881
882         if(p == NULL)
883                 bb_error_d(msg_enomem);
884         return p;
885 }
886
887 static void *xrealloc(void *p, size_t size) {
888         p = realloc(p, size);
889         if(p == NULL)
890                 bb_error_d(msg_enomem);
891         return p;
892 }
893
894 static char *bb_xstrdup(const char *s)
895 {
896         char *r = strdup(s);
897
898         if(r == NULL)
899             bb_error_d(msg_enomem);
900         return r;
901 }
902
903 static char *bb_simplify_path(const char *path)
904 {
905         char *s, *start, *p;
906
907         if (path[0] == '/')
908                 start = bb_xstrdup(path);
909         else {
910                 /* is not libbb, but this program have not chdir() */
911                 start = bb_asprint("%s/%s", pwd, path);
912         }
913         p = s = start;
914
915         do {
916             if (*p == '/') {
917                 if (*s == '/') {    /* skip duplicate (or initial) slash */
918                     continue;
919                 } else if (*s == '.') {
920                     if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
921                         continue;
922                     } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
923                         ++s;
924                         if (p > start) {
925                             while (*--p != '/');    /* omit previous dir */
926                         }
927                         continue;
928                     }
929                 }
930             }
931             *++p = *s;
932         } while (*++s);
933
934         if ((p == start) || (*p != '/')) {  /* not a trailing slash */
935             ++p;                            /* so keep last character */
936         }
937         *p = 0;
938
939         return start;
940 }