packaging: bumped version, updated changelog.
[profile/ivi/murphy.git] / utils / collect-symbols.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdarg.h>
34 #include <errno.h>
35 #define _GNU_SOURCE
36 #include <getopt.h>
37 #include <unistd.h>
38 #include <regex.h>
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43
44 #define RD 0
45 #define WR 1
46
47 typedef enum {
48     TOKEN_ERROR = -1,
49     TOKEN_LINEMARKER,                    /* a preprocessor line marker */
50     TOKEN_BLOCK,                         /* a block enclosed in {}/()/[] */
51     TOKEN_WORD,                          /* a word */
52     TOKEN_DQUOTED,                       /* a double-quoted sequence */
53     TOKEN_SQUOTED,                       /* a single-quoted sequence */
54     TOKEN_ASSIGN,                        /* '=' */
55     TOKEN_SEMICOLON,                     /* ';' */
56     TOKEN_COLON,                         /* ',' */
57     TOKEN_OTHER,                         /* any other token */
58 } token_type_t;
59
60
61 typedef struct {
62     token_type_t  type;                  /* token type */
63     char         *value;                 /* token value */
64 } token_t;
65
66
67 #define READBUF_SIZE ( 8 * 1024)
68 #define RINGBUF_SIZE (16 * 1024)
69 #define MAX_TOKEN    (512)
70 #define MAX_TOKENS   (64)
71
72 typedef struct {
73     int  fd;                             /* file descriptor to read */
74     char buf[READBUF_SIZE];              /* data buffer */
75     int  len;                            /* amount of data in buffer */
76     int  rd;                             /* data buffer read offset */
77     int  nxt;                            /* pushed back data if non-zero */
78 } input_t;
79
80 typedef struct {
81     char buf[RINGBUF_SIZE];              /* data buffer */
82     int  wr;                             /* write offset */
83 } ringbuf_t;
84
85 typedef struct {
86     char    *preproc;                    /* preprocessor to use */
87     char    *pattern;                    /* symbol pattern */
88     char   **files;                      /* files to parse for symbols */
89     int      nfile;                      /* number of files */
90     char    *cflags;                     /* compiler flags */
91     char    *output;                     /* output path */
92     int      gnuld;                      /* generate GNU ld script */
93     int      verbose;                    /* verbosity */
94 } config_t;
95
96 typedef struct {
97     char **syms;
98     int    nsym;
99 } symtab_t;
100
101
102 static int verbosity = 1;
103
104
105 static void fatal_error(const char *fmt, ...)
106 {
107     va_list ap;
108
109     va_start(ap, fmt);
110     vfprintf(stderr, fmt, ap);
111     va_end(ap);
112
113     exit(1);
114 }
115
116
117 static void verbose_message(int level, const char *fmt, ...)
118 {
119     va_list ap;
120
121     if (verbosity >= level) {
122         va_start(ap, fmt);
123         vfprintf(stderr, fmt, ap);
124         va_end(ap);
125     }
126 }
127
128
129 static char *unshave(char *cmd)
130 {
131 #define SHAVE "shave cc "
132     char *shave;
133
134     shave = strstr(cmd, SHAVE);
135
136     if (shave == NULL)
137         return cmd;
138     else
139         return shave + sizeof(SHAVE) - 1;
140 #undef SHAVE
141 }
142
143
144 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
145 {
146     va_list ap;
147
148     if (fmt && *fmt) {
149         va_start(ap, fmt);
150         vprintf(fmt, ap);
151         va_end(ap);
152     }
153
154     printf("usage: %s [options]\n\n"
155            "The possible options are:\n"
156            "  -P  --preproc <preprocessor> preprocessor to use [gcc]\n"
157            "  -c, --compiler-flags <flags> flags to pass to compiler\n"
158            "  -p, --pattern <pattern>      symbol regexp pattern\n"
159            "  -o, --output <path>          write output to the given file\n"
160            "  -g, --gnu-ld <script>        generate GNU ld linker script\n"
161            "  -v, --verbose                increase verbosity\n"
162            "  -q, --quiet                  decrease verbosity\n"
163            "  -h, --help                   show this help on usage\n",
164            argv0);
165
166     if (exit_code < 0)
167         return;
168     else
169         exit(exit_code);
170 }
171
172
173 static void set_defaults(config_t *c)
174 {
175     memset(c, 0, sizeof(*c));
176     c->preproc = "gcc";
177     c->pattern = "^mrp_|^_mrp";
178 }
179
180
181 static void parse_cmdline(config_t *cfg, int argc, char **argv)
182 {
183 #   define OPTIONS "P:c:p:o:gvqh"
184     struct option options[] = {
185         { "preprocessor"  , required_argument, NULL, 'P' },
186         { "compiler-flags", required_argument, NULL, 'c' },
187         { "pattern"       , required_argument, NULL, 'p' },
188         { "output"        , required_argument, NULL, 'o' },
189         { "gnu-ld"        , no_argument      , NULL, 'g' },
190         { "verbose"       , no_argument      , NULL, 'v' },
191         { "quiet"         , no_argument      , NULL, 'q' },
192         { "help"          , no_argument      , NULL, 'h' },
193         { NULL, 0, NULL, 0 }
194     };
195
196     int opt;
197
198     set_defaults(cfg);
199
200     while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
201         switch (opt) {
202         case 'P':
203             cfg->preproc = unshave(optarg);
204             break;
205
206         case 'c':
207             cfg->cflags = optarg;
208             break;
209
210         case 'p':
211             cfg->pattern = optarg;
212             break;
213
214         case 'o':
215             cfg->output = optarg;
216             break;
217
218         case 'g':
219             cfg->gnuld = 1;
220             break;
221
222         case 'v':
223             verbosity++;
224             break;
225
226         case 'q':
227             verbosity--;
228             break;
229
230         case 'h':
231             print_usage(argv[0], -1, "");
232             exit(0);
233             break;
234
235         default:
236             print_usage(argv[0], EINVAL, "invalid option '%s'\n",
237                         argv[optind]);
238         }
239     }
240
241     cfg->files = argv + optind;
242     cfg->nfile = argc - optind;
243 }
244
245
246 static int preprocess_file(const char *preproc, const char *file,
247                            const char *cflags, pid_t *pid)
248 {
249     char cmd[4096], *argv[32];
250     int  fd[2], argc, i;
251
252     /*
253      * preprocess the given file
254      *
255      * Fork off a process for preprocessing the given file with the
256      * configured compiler flags. Return the reading end of the pipe
257      * the preprocessor is writing to.
258      */
259
260     if (pipe(fd) != 0)
261         fatal_error("failed to create pipe (%d: %s).", errno, strerror(errno));
262
263     *pid = fork();
264
265     switch (*pid) {
266     case -1:
267         fatal_error("failed to for preprocessor (%d: %s).",
268                     errno, strerror(errno));
269         break;
270
271     case 0: /* child: exec preprocessor */
272         close(fd[RD]);
273
274         /*
275          * Notes:
276          *     Currently we execute the preprocessor by starting a shell
277          *     and feeding it our constructed preprocessor command using
278          *     the '-c' option. If we need to pass options to the pre-
279          *     processor we need to protect those from expansion by the
280          *     intermediate shell. This causes some level of pain if we
281          *     also have a script that gets its arguments somewhere else,
282          *     eg. from a Makefile, and passes those forward to us. This
283          *     is exactly how we are executed during Murphy builds.
284          *
285          *     To reduce the pain perhaps we should leave the shell out,
286          *     search $PATH ourselves for the preprocessor and just exec
287          *     it directly here.
288          */
289
290         argc         = 0;
291         argv[argc++] = "/bin/sh";
292         argv[argc++] = "-c";
293
294         if (cflags != NULL)
295             snprintf(cmd, sizeof(cmd), "%s %s -E %s", preproc, cflags, file);
296         else
297             snprintf(cmd, sizeof(cmd), "%s -E %s", preproc, file);
298
299         argv[argc++] = cmd;
300         argv[argc]   = NULL;
301
302         for (i = 0; i < argc; i++) {
303             verbose_message(3, "shell arg #%d: '%s'\n", i, argv[i]);
304         }
305
306         if (dup2(fd[WR], fileno(stdout)) < 0)
307             fatal_error("failed to redirect stdout (%d: %s)",
308                         errno, strerror(errno));
309
310         if (execv("/bin/sh", argv) != 0)
311             fatal_error("failed to exec command '%s' (%d: %s)", cmd,
312                         errno, strerror(errno));
313         break;
314
315     default: /* parent: return fd to read preprocessed data from */
316         close(fd[WR]);
317         return fd[RD];
318     }
319
320     return -1;  /* never reached */
321 }
322
323
324 static void input_init(input_t *in, int fd)
325 {
326     memset(in, 0, sizeof(*in));
327
328     in->fd = fd;
329 }
330
331
332 static char input_read(input_t *in)
333 {
334     char ch;
335
336     /*
337      * read the next input character
338      *
339      * If there is an pushed back character deliver (and clear) than one.
340      * Otherwise refill the input buffer if needed and return the next
341      * character from it.
342      */
343
344     if (in->nxt != 0) {
345         ch = in->nxt;
346         in->nxt = 0;
347     }
348     else {
349         if (in->len <= in->rd) {
350             in->len = read(in->fd, in->buf, sizeof(in->buf));
351
352             if (in->len > 0) {
353                 in->rd = 1;
354                 ch = in->buf[0];
355             }
356             else
357                 ch = 0;
358         }
359         else
360             return ch = in->buf[in->rd++];
361     }
362
363     return ch;
364 }
365
366
367 static int input_pushback(input_t *in, char ch)
368 {
369     /*
370      * push back a character to the input stream
371      *
372      * Note that you can only push back a single character. Trying to
373      * push back more than one will fail with an error.
374      */
375
376     if (in->nxt == 0) {
377         in->nxt = ch;
378
379         return 0;
380     }
381     else {
382         errno = EBUSY;
383
384         return -1;
385     }
386 }
387
388
389 static void input_discard_whitespace(input_t *in)
390 {
391     char ch;
392
393     /*
394      * discard consecutive whitespace (including newline)
395      */
396
397     while ((ch = input_read(in)) == ' ' || ch == '\t' || ch == '\n')
398         ;
399
400     input_pushback(in, ch);
401 }
402
403
404 #if 0
405 static void input_discard_line(input_t *in)
406 {
407     int ch;
408
409     /*
410      * discard input till a newline
411      */
412
413     while ((ch = input_read(in)) != '\n' && ch != 0)
414         ;
415 }
416 #endif
417
418
419 static int input_discard_quoted(input_t *in, char quote)
420 {
421     char ch;
422
423     /*
424      * discard a block of quoted input
425      */
426
427     while ((ch = input_read(in)) != quote && ch != 0) {
428         if (ch == '\\')
429             input_read(in);
430     }
431
432     if (ch != quote) {
433         errno = EINVAL;
434         return -1;
435     }
436     else
437         return 0;
438 }
439
440
441 static int input_discard_block(input_t *in, char beg)
442 {
443     char end, ch, quote;
444     int  level;
445
446     /*
447      * discard a block enclosed in {}, [], or ()
448      */
449
450     switch (beg) {
451     case '{': end = '}'; break;
452     case '[': end = ']'; break;
453     case '(': end = ')'; break;
454     default:             return 0;
455     }
456
457     level = 1;
458     while (level > 0) {
459         switch ((ch = input_read(in))) {
460         case '"':
461         case '\'':
462             quote = ch;
463             if (input_discard_quoted(in, quote) != 0)
464                 return -1;
465             break;
466
467         default:
468             if (ch == end)
469                 level--;
470             else if (ch == beg)
471                 level++;
472         }
473     }
474
475     if (level == 0)
476         return 0;
477     else {
478         errno = EINVAL;
479         return -1;
480     }
481 }
482
483
484 static void ringbuf_init(ringbuf_t *rb)
485 {
486     memset(rb->buf, 0, sizeof(rb->buf));
487     rb->wr = 0;
488 }
489
490
491 static char *ringbuf_save(ringbuf_t *rb, char *token, int len)
492 {
493     char *t, *s, *d;
494     int   n, o, i;
495
496     /*
497      * save the given token in the token ring buffer
498      */
499
500     verbose_message(2, "saving '%s'...\n", token);
501
502     if (len < 0)
503         len = strlen(token);
504
505     n = sizeof(rb->buf) - 1 - rb->wr;
506
507     if (n < len + 1) {
508         t = rb->buf;
509         n = sizeof(rb->buf) - 1;
510         o = 0;
511     }
512     else {
513         t = rb->buf + rb->wr;
514         o = rb->wr;
515     }
516
517     if (n >= len + 1) {
518         s = token;
519         d = t;
520
521         for (i = 0; i < len; i++, o++)
522             *d++ = *s++;
523
524         *d = '\0';
525         rb->wr = o + 1;
526
527         return t;
528     }
529     else {
530         errno = ENOSPC;
531         return NULL;
532     }
533 }
534
535
536 static char *input_collect_word(input_t *in, ringbuf_t *rb)
537 {
538 #define WORD_CHAR(c)                            \
539     (('a' <= (c) && (c) <= 'z') ||              \
540      ('A' <= (c) && (c) <= 'Z') ||              \
541      ('0' <= (c) && (c) <= '9') ||              \
542      ((c) == '_' || (c) == '$'))
543
544     char buf[MAX_TOKEN], ch;
545     int  n;
546
547     /*
548      * collect and save the next word (consecutive sequence) of input
549      */
550
551     for (n = 0; n < (int)sizeof(buf) - 1; n++) {
552         ch = input_read(in);
553
554         if (WORD_CHAR(ch))
555             buf[n] = ch;
556         else {
557             buf[n] = '\0';
558             input_pushback(in, ch);
559
560             return ringbuf_save(rb, buf, n);
561         }
562     }
563
564     errno = ENOSPC;
565     return NULL;
566 }
567
568
569 static char *input_parse_linemarker(input_t *in, char *buf, size_t size)
570 {
571     char ch;
572     int  i;
573
574     while((ch = input_read(in)) != '"' && ch != '\n' && ch)
575         ;
576
577     if (ch != '"')
578         return NULL;
579
580     for (i = 0; i < (int)size - 1; i++) {
581         buf[i] = ch = input_read(in);
582
583         if (ch == '"') {
584             buf[i] = '\0';
585
586             while ((ch = input_read(in)) != '\n' && ch)
587                 ;
588
589             return buf;
590         }
591     }
592
593     return NULL;
594 }
595
596
597 static int same_file(const char *path1, const char *path2)
598 {
599     struct stat st1, st2;
600
601     if (stat(path1, &st1) != 0 || stat(path2, &st2) != 0)
602         return 0;
603     else
604         return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
605 }
606
607
608 static int collect_tokens(input_t *in, ringbuf_t *rb, token_t *tokens,
609                           int ntoken)
610 {
611     char ch, *v, path[1024];
612     int  n, has_paren;
613
614     /*
615      * collect a sequence of tokens that forms (or looks like) a logical unit
616      */
617
618     n = 0;
619     has_paren = 0;
620     while (n < ntoken) {
621         switch ((ch = input_read(in))) {
622             /* always treat a semicolon here as a sequence terminator */
623         case ';':
624             tokens[n].type  = TOKEN_SEMICOLON;
625             tokens[n].value = ringbuf_save(rb, ";", 1);
626             return n + 1;
627
628             /* extract path name from preprocessor line-markers */
629         case '#':
630             v = input_parse_linemarker(in, path, sizeof(path));
631             if (v != NULL) {
632                 tokens[n].type  = TOKEN_LINEMARKER;
633                 tokens[n].value = ringbuf_save(rb, v, -1);
634                 if (n == 0)
635                     return n + 1;
636                 else
637                     return -1;
638             }
639             break;
640
641             /* discard whitespace (including trailing newlines) */
642         case ' ':
643         case '\t':
644             input_discard_whitespace(in);
645             break;
646
647             /* ignore newlines */
648         case '\n':
649             break;
650
651             /* collate/collapse blocks to a block indicator token */
652         case '{':
653         case '(':
654         case '[':
655             if (input_discard_block(in, ch) != 0)
656                 return -1;
657             else {
658                 /* filter out __attribute__ ((.*)) token pairs */
659                 if (ch == '(' && n > 0 &&
660                     tokens[n-1].type == TOKEN_WORD &&
661                     !strcmp(tokens[n-1].value, "__attribute__")) {
662                     n--;
663                     verbose_message(2, "filtered __attribute__...\n");
664                     continue;
665                 }
666
667                 v = (ch == '{' ? "{" : (ch == '[' ? "[" : "("));
668                 tokens[n].type  = TOKEN_BLOCK;
669                 tokens[n].value = ringbuf_save(rb, v, 1);
670                 n++;
671
672                 if (v[0] == '(')
673                     has_paren = 1;
674                 else {
675                     /*
676                      * if this sequence includes both '(...)' and '{...}'
677                      * we assume this to be a function definition so we
678                      * don't wait for a semicolon but terminate sequence
679                      * here
680                      */
681                     if (v[0] == '{')
682                         if (has_paren)
683                             return n;
684                 }
685             }
686             break;
687
688             /* end of file terminates the current sequence */
689         case 0:
690             return n;
691
692             /* collect and save the next word */
693         case 'a'...'z':
694         case 'A'...'Z':
695         case '_':
696         case '$':
697         case '0'...'9':
698             input_pushback(in, ch);
699             v = input_collect_word(in, rb);
700
701             if (v != NULL) {
702                 if (!strcmp(v, "__extension__"))
703                     break;
704                 tokens[n].type  = TOKEN_WORD;
705                 tokens[n].value = v;
706                 n++;
707             }
708             else
709                 return -1;
710             break;
711
712         case '=':
713             tokens[n].type  = TOKEN_ASSIGN;
714             tokens[n].value = ringbuf_save(rb, "=", 1);
715             n++;
716             break;
717
718             /* ignore asterisks */
719         case '*':
720             break;
721
722             /* the rest we print for debugging */
723         default:
724             printf("%c", ch);
725         }
726     }
727
728     errno = EOVERFLOW;
729     return -1;
730 }
731
732
733 static char *symbol_from_tokens(token_t *tokens, int ntoken)
734 {
735 #define MATCHING_TOKEN(_n, _type, _val)                         \
736     (tokens[(_n)].type == TOKEN_##_type &&                      \
737      (!*_val || !strcmp(_val, tokens[(_n)].value)))
738
739     int last, has_paren, has_curly, has_bracket, has_assign;
740     int i;
741
742     /*
743      * extract the symbol from a sequence of tokens
744      */
745
746     if (verbosity > 2) {
747         for (i = 0; i < ntoken; i++)
748             verbose_message(3, "0x%x: '%s'\n", tokens[i].type, tokens[i].value);
749         verbose_message(3, "--\n");
750     }
751
752     has_paren = has_curly = has_bracket = has_assign = 0;
753     for (i = 0; i < ntoken; i++) {
754         if      (MATCHING_TOKEN(i, BLOCK , "(")) has_paren   = 1;
755         else if (MATCHING_TOKEN(i, BLOCK , "{")) has_curly   = 1;
756         else if (MATCHING_TOKEN(i, BLOCK , "[")) has_bracket = 1;
757         else if (MATCHING_TOKEN(i, ASSIGN, "" )) has_assign  = 1 + i;
758     }
759
760     last = ntoken - 1;
761
762     if (tokens[0].type != TOKEN_WORD) {
763         verbose_message(2, "ignoring sequence starting with non-word\n");
764         return NULL;
765     }
766
767     /* ignore typedefs and everything static */
768     if (MATCHING_TOKEN(0, WORD, "typedef") ||
769         MATCHING_TOKEN(0, WORD, "static")) {
770         verbose_message(2, "ignoring typedef or static sequence\n");
771         return NULL;
772     }
773
774     /* ignore forward declarations */
775     if (ntoken == 3 &&
776         (MATCHING_TOKEN(0, WORD, "struct") ||
777          MATCHING_TOKEN(0, WORD, "union" ) ||
778          MATCHING_TOKEN(0, WORD, "enum"  )) &&
779         MATCHING_TOKEN(1, WORD, "") &&
780         MATCHING_TOKEN(2, SEMICOLON, "")) {
781         verbose_message(2, "ignoring forward declaration sequence\n");
782         return NULL;
783     }
784
785     /* take care of function prototypes */
786     if (last > 2) {
787         if (MATCHING_TOKEN(last  , SEMICOLON, "" ) &&
788             MATCHING_TOKEN(last-1, BLOCK    , "(") &&
789             MATCHING_TOKEN(last-2, WORD     , "" ))
790             return tokens[last-2].value;
791     }
792
793     /* take care of global variables with assignments */
794     if (last > 1 && has_assign) {
795         i = has_assign - 1;
796         if (i > 0 && MATCHING_TOKEN(i-1, WORD, ""))
797             return tokens[i-1].value;
798         if (i > 1 &&
799             MATCHING_TOKEN(i-1, BLOCK, "[") &&
800             MATCHING_TOKEN(i-2, WORD , ""))
801             return tokens[i-2].value;
802     }
803
804     /* take care of global variables */
805     if (last > 1 && !has_paren && !has_curly) {
806         if (MATCHING_TOKEN(last  , SEMICOLON, "") &&
807             MATCHING_TOKEN(last-1, WORD     , ""))
808             return tokens[last-1].value;
809     }
810
811     verbose_message(2, "ignoring other non-matching token sequence\n");
812
813     return NULL;
814 }
815
816
817 static void symtab_init(symtab_t *st)
818 {
819     st->syms = NULL;
820     st->nsym = 0;
821 }
822
823
824 static void symtab_add(symtab_t *st, char *sym)
825 {
826     int i;
827
828     for (i = 0; i < st->nsym; i++)
829         if (!strcmp(st->syms[i], sym))
830             return;
831
832     st->syms = realloc(st->syms, (st->nsym + 1) * sizeof(st->syms[0]));
833
834     if (st->syms != NULL) {
835         st->syms[st->nsym] = strdup(sym);
836
837         if (st->syms[st->nsym] != NULL) {
838             st->nsym++;
839
840             return;
841         }
842
843         fatal_error("failed to save symbol '%s'", sym);
844     }
845
846     fatal_error("failed to allocate new symbol table entry");
847 }
848
849
850 static void symtab_reset(symtab_t *st)
851 {
852     int i;
853
854     for (i = 0; i < st->nsym; i++)
855         free(st->syms[i]);
856
857     free(st->syms);
858
859     st->syms = NULL;
860     st->nsym = 0;
861 }
862
863
864 static void symtab_dump(symtab_t *st, int gnuld, FILE *out)
865 {
866     int i;
867
868     if (!gnuld) {
869         for (i = 0; i < st->nsym; i++)
870             fprintf(out, "%s\n", st->syms[i]);
871     }
872     else {
873         fprintf(out, "{\n");
874         if (st->nsym > 0) {
875             fprintf(out, "    global:\n");
876             for (i = 0; i < st->nsym; i++)
877                 fprintf(out, "        %s;\n", st->syms[i]);
878         }
879         fprintf(out, "    local:\n");
880         fprintf(out, "        *;\n");
881         fprintf(out, "};\n");
882     }
883 }
884
885
886 static void extract_symbols(const char *preproc, const char *path,
887                             const char *cflags, symtab_t *st, regex_t *re)
888 {
889     input_t   in;
890     ringbuf_t rb;
891     int       fd;
892     pid_t     pp_pid;
893     token_t   tokens[MAX_TOKENS];
894     int       ntoken;
895     char     *sym;
896     int       pp_status, foreign;
897
898     fd = preprocess_file(preproc, path, cflags, &pp_pid);
899
900     input_init(&in, fd);
901     ringbuf_init(&rb);
902     foreign = 0;
903
904     while ((ntoken = collect_tokens(&in, &rb, tokens, MAX_TOKENS)) > 0) {
905         if (tokens[0].type == TOKEN_LINEMARKER) {
906             foreign = !same_file(path, tokens[0].value);
907
908             verbose_message(2, "input switched to %s file '%s'...\n",
909                             foreign ? "foreign" : "input", tokens[0].value);
910
911             continue;
912         }
913
914         if (foreign) {
915             verbose_message(2, "ignoring token stream from foreign file...\n");
916             continue;
917         }
918
919         sym = symbol_from_tokens(tokens, ntoken);
920
921         if (sym != NULL) {
922             if (re == NULL || regexec(re, sym, 0, NULL, 0) == 0)
923                 symtab_add(st, sym);
924             else
925                 verbose_message(2, "filtered non-matching '%s'...\n", sym);
926         }
927     }
928
929     close(fd);
930     waitpid(pp_pid, &pp_status, 0);
931
932     if (WIFEXITED(pp_status) && WEXITSTATUS(pp_status) != 0)
933         fatal_error("preprocessing of '%s' failed\n", path);
934 }
935
936
937 int main(int argc, char *argv[])
938 {
939     config_t  cfg;
940     symtab_t  st;
941     regex_t   rebuf, *re;
942     char      regerr[1024];
943     FILE     *out;
944     int       err, i;
945
946     if (getenv("__COLLECT_SYMBOLS_DEBUG") != NULL) {
947         verbosity = 3;
948         for (i = 0; i < argc; i++) {
949             verbose_message(0, "argv[%d]: '%s'\n", i, argv[i]);
950         }
951     }
952
953     symtab_init(&st);
954     parse_cmdline(&cfg, argc, argv);
955
956     verbose_message(1, "using preprocessor '%s', cflags '%s'\n", cfg.preproc,
957                     cfg.cflags ? cfg.cflags : "");
958
959     if (cfg.pattern != NULL) {
960         err = regcomp(&rebuf, cfg.pattern, REG_EXTENDED);
961
962         if (err != 0) {
963             regerror(err, &rebuf, regerr, sizeof(regerr));
964             fatal_error("invalid pattern '%s' (error: %s)\n", cfg.pattern,
965                         regerr);
966         }
967
968         re = &rebuf;
969     }
970     else
971         re = NULL;
972
973     for (i = 0; i < cfg.nfile; i++)
974         extract_symbols(cfg.preproc, cfg.files[i], cfg.cflags, &st, re);
975
976     if (cfg.output != NULL) {
977         out = fopen(cfg.output, "w");
978
979         if (out == NULL)
980             fatal_error("failed to open '%s' (%d: %s)", cfg.output,
981                         errno, strerror(errno));
982     }
983     else
984         out = stdout;
985
986     symtab_dump(&st, cfg.gnuld, out);
987
988     if (re != NULL)
989         regfree(re);
990
991     symtab_reset(&st);
992
993     if (out != stdout)
994         fclose(out);
995
996     return 0;
997 }