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