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