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