source: branches/profile/AISC_MKPTPS/mkptypes.cxx

Last change on this file was 12783, checked in by westram, 10 years ago
  • publish TESTs for which nm 2.24 fails to export source location
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.0 KB
Line 
1/* Program to extract function declarations from C/C++ source code
2 *
3 * Written by Eric R. Smith and placed in the public domain
4 *
5 * Thanks to:
6 *
7 * Jwahar R. Bammi, for catching several bugs and providing the Unix makefiles
8 * Byron T. Jenings Jr. for cleaning up the space code, providing a Unix
9 *   manual page, and some ANSI and C++ improvements.
10 * Skip Gilbrech for code to skip parameter names in prototypes.
11 * ... and many others for helpful comments and suggestions.
12 *
13 * Many extension were made for use in ARB build process
14 * by Ralf Westram <ralf@arb-home.de>
15 */
16
17#include <cstddef>
18#include <cstdlib>
19#include <cassert>
20#include <unistd.h>
21#include <cstdio>
22#include <cctype>
23#include <cstring>
24#include <cstdarg>
25
26#include <arb_early_check.h>
27#include <attributes.h>
28
29#include <arb_simple_assert.h>
30#include <arbtools.h>
31
32#define mp_assert(cond) arb_assert(cond)
33
34static void Version();
35
36#if defined(DEBUG)
37// #define DEBUG_PRINTS
38#endif // DEBUG
39
40#ifdef DEBUG_PRINTS
41#define DEBUG_PRINT(s)                  fputs((s), stderr)
42#define DEBUG_PRINT_STRING(name, str)   fprintf(stderr, "%s'%s'\n", name, str)
43#else
44#define DEBUG_PRINT(s)
45#define DEBUG_PRINT_STRING(name, str)
46#endif
47
48#define PRINT(s) fputs((s), stdout)
49
50#define IS_CSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
51#define ABORTED    ((Word *) -1)
52#define MAXPARAM   20                               // max. number of parameters to a function
53#define NEWBUFSIZ  (20480*sizeof(char))             // new buffer size
54
55
56static int donum               = 0;                 // print line numbers?
57static int define_macro        = 0;                 // define macro for K&R prototypes?
58static int use_macro           = 0;                 // use a macro to support K&R prototypes?
59static int use_main            = 0;                 // add prototype for main?
60static int no_parm_names       = 0;                 // no parm names - only types
61static int print_extern        = 0;                 // use "extern" before function declarations
62static int dont_promote        = 0;                 // don't promote prototypes
63static int promote_lines       = 0;                 // promote 'AISC_MKPT_PROMOTE'-lines
64static int aisc                = 0;                 // aisc compatible output
65static int cansibycplus        = 0;                 // produce extern "C"
66static int promote_extern_c    = 0;                 // produce extern "C" into prototype
67static int extern_c_seen       = 0;                 // true if extern "C" was parsed
68static int search__ATTR__      = 0;                 // search for ARB-__attribute__-macros (__ATTR__) ?
69
70static const char *include_wrapper = NULL;          // add include wrapper (contains name of header or NULL)
71
72static int inquote      = 0;                        // in a quote??
73static int newline_seen = 1;                        // are we at the start of a line
74static int glastc       = ' ';                      // last char. seen by getsym()
75
76static char       *current_file   = 0;              // name of current file
77static char       *current_dir    = 0;              // name of current directory
78static const char *header_comment = 0;              // comment written into header
79static long        linenum        = 1L;             // line number in current file
80
81static char const *macro_name = "P_";               //   macro to use for prototypes
82static char const *ourname;                         // our name, from argv[] array
83
84// ------------------------------------------------------------
85
86static void errorAt(long line, const char *msg) {
87    printf("\n"
88           "#line %li \"%s/%s\"\n"
89           "#error in aisc_mkpt: %s\n",
90           line,
91           current_dir,
92           current_file,
93           msg);
94}
95
96static void error(const char *msg) {
97    errorAt(linenum, msg);
98}
99
100static void errorf(const char *format, ...) __attribute__((format(__printf__, 1, 2)));
101static void errorf(const char *format, ...) {
102    const int BUFFERSIZE = 1000;
103    char      buffer[BUFFERSIZE];
104    va_list   args;
105
106    va_start(args, format);
107    int printed = vsprintf(buffer, format, args);
108    if (printed >= BUFFERSIZE) {
109        fputs("buffer overflow\n", stderr);
110        exit(EXIT_FAILURE);
111    }
112    va_end(args);
113
114    error(buffer);
115}
116
117// ------------------------------------------------------------
118
119struct SymPart {
120    char *part;
121    int   len;                                      // len of part
122    bool  atStart;                                  // part has to match at start of name
123
124    SymPart *And;
125    SymPart *next;
126};
127
128static SymPart* makeSymPart(char *token) {
129    SymPart *sp = (SymPart*)malloc(sizeof(*sp));
130
131    sp->atStart = token[0] == '^';
132    char *part  = sp->atStart ? token+1 : token;
133    char *plus  = strchr(part, '+');
134
135    if (plus) {
136        *plus++ = 0;
137        sp->And = makeSymPart(plus);
138    }
139    else {
140        sp->And = NULL;
141    }
142    sp->part = strdup(part);
143    sp->len = strlen(sp->part);
144
145    sp->next = NULL;
146
147    return sp;
148}
149
150static void addSymParts(SymPart*& symParts, const char *parts) {
151    char       *p   = strdup(parts);
152    const char *sep = ",";
153    char       *s   = strtok(p, sep);
154
155    while (s) {
156        SymPart *sp = makeSymPart(s);
157        sp->next    = symParts;
158        symParts    = sp;
159        s           = strtok(0, sep);
160    }
161
162    free(p);
163}
164
165static bool matchesSymPart(const SymPart* symParts, const char *name) {
166    const SymPart *sp      = symParts;
167    bool           matches = false;
168
169    while (sp && !matches) {
170        if (sp->atStart) {
171            matches = strncmp(name, sp->part, sp->len) == 0;
172        }
173        else {
174            matches = strstr(name, sp->part) != NULL;
175        }
176        if (matches && sp->And) matches = matchesSymPart(sp->And, name);
177        sp = sp->next;
178    }
179
180    return matches;
181}
182
183static void freeSymParts(SymPart*& symParts) {
184    SymPart *next = symParts;
185
186    while (next) {
187        SymPart *del = next;
188        next         = del->next;
189
190        if (del->And) freeSymParts(del->And);
191        free(del->part);
192        free(del);
193    }
194   
195    symParts = NULL;
196}
197
198// ----------------------------------------
199
200static SymPart *requiredSymParts = 0; // only create prototypes if function-name matches one of these parts
201static SymPart *excludedSymParts = 0; // DONT create prototypes if function-name matches one of these parts
202
203inline void addRequiredSymParts(const char *parts) { addSymParts(requiredSymParts, parts); }
204inline void addExcludedSymParts(const char *parts) { addSymParts(excludedSymParts, parts); }
205
206inline void freeRequiredSymParts() { freeSymParts(requiredSymParts); }
207inline void freeExcludedSymParts() { freeSymParts(excludedSymParts); }
208
209inline bool hasRequiredSymPart(const char *name) { return !requiredSymParts || matchesSymPart(requiredSymParts, name); }
210inline bool hasExcludedSymPart(const char *name) { return excludedSymParts  && matchesSymPart(excludedSymParts, name); }
211
212inline bool wantPrototypeFor(const char *name) {
213    return hasRequiredSymPart(name) && !hasExcludedSymPart(name);
214}
215
216// ----------------------------------------
217
218struct Word {
219    Word *next;
220    char  string[1];
221};
222
223// Routines for manipulating lists of words.
224
225static Word *word_alloc(const char *s)
226{
227    Word *w;
228
229    /* note that sizeof(Word) already contains space for a terminating null
230     * however, we add 1 so that typefixhack can promote "float" to "double"
231     * by just doing a strcpy.
232     */
233    w = (Word *) malloc(sizeof(Word) + strlen(s) + 1);
234    strcpy(w->string, s);
235    w->next = NULL;
236    return w;
237}
238
239static void word_free(Word *w)
240{
241    Word *oldw;
242    while (w) {
243        oldw = w;
244        w = w->next;
245        free(oldw);
246    }
247}
248
249static int List_len(Word *w) {
250    // return the length of a list
251    // empty words are not counted
252    int count = 0;
253
254    while (w) {
255        if (*w->string) count++;
256        w = w->next;
257    }
258    return count;
259}
260
261static Word *word_append(Word *w1, Word *w2) {
262    // Append two lists, and return the result
263
264    Word *r, *w;
265
266    r = w = word_alloc("");
267
268    while (w1) {
269        w->next = word_alloc(w1->string);
270        w = w->next;
271        w1 = w1->next;
272    }
273    while (w2) {
274        w->next = word_alloc(w2->string);
275        w = w->next;
276        w2 = w2->next;
277    }
278
279    return r;
280}
281
282static int foundin(Word *w1, Word *w2) {
283    // see if the last entry in w2 is in w1
284
285    while (w2->next)
286        w2 = w2->next;
287
288    while (w1) {
289        if (strcmp(w1->string, w2->string)==0)
290            return 1;
291        w1 = w1->next;
292    }
293    return 0;
294}
295
296static void addword(Word *w, const char *s) {
297    // add the string s to the given list of words
298
299    while (w->next) w = w->next;
300    w->next = word_alloc(s);
301
302    DEBUG_PRINT("addword: '");
303    DEBUG_PRINT(s);
304    DEBUG_PRINT("'\n");
305}
306
307static void typefixhack(Word *w) {
308    // typefixhack: promote formal parameters of type "char", "unsigned char",
309    // "short", or "unsigned short" to "int".
310
311    Word *oldw = 0;
312
313    if (dont_promote)
314        return;
315
316    while (w) {
317        if (*w->string) {
318            if ((strcmp(w->string, "char")==0 || strcmp(w->string, "short")==0) && (List_len(w->next) < 2)) {
319                // delete any "unsigned" specifier present -- yes, it's supposed to do this
320                if (oldw && strcmp(oldw->string, "unsigned")==0) {
321                    oldw->next = w->next;
322                    free(w);
323                    w = oldw;
324                }
325                strcpy(w->string, "int");
326            }
327            else if (strcmp(w->string, "float")==0 && List_len(w->next) < 2) {
328                strcpy(w->string, "double");
329            }
330        }
331        w = w->next;
332    }
333}
334
335static int ngetc(FILE *f) {
336    // read a character: if it's a newline, increment the line count
337
338    int c;
339
340    c = getc(f);
341    if (c == '\n') linenum++;
342
343    return c;
344}
345
346#define MAX_COMMENT_SIZE 10000
347
348static char  last_comment[MAX_COMMENT_SIZE];
349static int   lc_size       = 0;
350static char *found__ATTR__ = 0;
351
352static void clear_found_attribute() {
353    free(found__ATTR__);
354    found__ATTR__ = 0;
355}
356
357static const char *nextNonSpace(const char* ptr) {
358    while (isspace(*ptr)) ++ptr;
359    return ptr;
360}
361static const char *nextNonWord(const char* ptr) {
362    while (isalnum(*ptr) || *ptr == '_') ++ptr;
363    return ptr;
364}
365
366inline const char *matchingParen(const char *from) {
367    int open = 1;
368    int i;
369    for (i = 1; from[i] && open>0; ++i) {
370        switch (from[i]) {
371            case '(': open++; break;
372            case ')': open--; break;
373        }
374    }
375    if (open) return NULL; // no matching closing paren
376    return from+i-1;
377}
378
379// -----------------
380//      LinePart
381
382class LinePart {
383    const char *line;
384    size_t      linelen; // of line
385
386    size_t pos;
387    size_t size;
388
389    bool part_is_legal() { return (pos <= linelen) && ((pos+size) <= linelen); }
390
391    void set(size_t newPos, size_t newSize) {
392        pos  = newPos;
393        size = newSize;
394        mp_assert(part_is_legal());
395    }
396   
397public:
398
399    LinePart(const char *wholeLine, size_t len = -1U)
400        : line(wholeLine),
401          linelen(len != -1U ? len : strlen(line))
402    {
403        mp_assert(line[linelen] == 0);
404        undefine();
405    }
406
407    size_t get_size() const { return size; }
408    bool   is_empty() const { return !size; }
409
410    const char *whole_line() const { return line; }
411
412    void define(const char *start, const char *end) { set(start-line, end-start+1); }
413    void undefine() { set(0, 0); }
414
415    void copyTo(char *buffer) const {
416        mp_assert(!is_empty());
417        memcpy(buffer, line+pos, size);
418        buffer[size] = 0;
419    }
420
421    LinePart behind() const {
422        mp_assert(!is_empty());
423        size_t behind_offset = pos+size;
424        mp_assert(linelen >= behind_offset);
425        return LinePart(line+behind_offset, linelen-behind_offset);
426    }
427
428    void error(const char *format) {
429        // 'format' has to contain one '%s'
430        mp_assert(!is_empty());
431        char part[get_size()+1];
432        copyTo(part);
433        errorf(format, part);
434        undefine();
435    }
436};
437
438
439// ------------------
440//      PartQueue
441
442class PartQueue : virtual Noncopyable {
443    LinePart   first;
444    PartQueue *next;
445    size_t     size;
446
447    bool is_empty() const { return !size; }
448    size_t get_size() const { return size; }
449
450    void copyPartsTo(char *buffer) const {
451        mp_assert(!first.is_empty());
452        first.copyTo(buffer);
453        buffer += first.get_size();
454        if (next) {
455            *buffer++ = ' ';
456            next->copyPartsTo(buffer);
457        }
458    }
459
460public:
461    PartQueue(LinePart first_)
462        : first(first_),
463          next(0),
464          size(first.get_size())
465    {}
466    ~PartQueue() { delete next; }
467
468    void append(PartQueue *mp) {
469        mp_assert(!next);
470        next  = mp;
471        size += 1+mp->get_size(); // add pos for space
472    }
473
474    char *to_string() {
475        char *result = (char *)malloc(get_size()+1);
476        copyPartsTo(result);
477        mp_assert(get_size() == strlen(result));
478        return result;
479    }
480};
481
482// ------------------------
483//      AttributeParser
484
485class AttributeParser {
486    const char *attrName;
487    size_t      attrNameLen;
488    bool        expandName;   // try to expand 'attrName'
489    bool        expectParens; // otherwise parens are optional
490
491public:
492    AttributeParser(const char *attrName_, bool expandName_, bool expectParens_)
493        : attrName(attrName_),
494          attrNameLen(strlen(attrName)),
495          expandName(expandName_),
496          expectParens(expectParens_)
497    {}
498
499private:
500    void parse_one_attr(LinePart& part) const {
501        const char *found = strstr(part.whole_line(), attrName);
502        if (found) {
503            const char *behind     = found+attrNameLen;
504            if (expandName) behind = nextNonWord(behind);
505            const char *openParen  = nextNonSpace(behind);
506
507            if (*openParen == '(') {
508                const char *closeParen = matchingParen(openParen);
509                if (closeParen) part.define(found, closeParen);
510                else {
511                    part.define(found, openParen);
512                    part.error("Could not find matching paren after '%s'");
513                }
514            }
515            else {
516                part.define(found, behind-1);
517                if (expectParens) part.error("Expected to see '(' after '%s'");
518            }
519        }
520    }
521
522    // rest is not really part of this class - may go to abstract base class if needed
523
524    PartQueue *parse_all(LinePart from) const {
525        parse_one_attr(from);
526        if (from.is_empty()) return NULL;
527
528        PartQueue *head = new PartQueue(from);
529        PartQueue *rest = parse_all(from.behind());
530        if (rest) head->append(rest);
531
532        return head;
533    }
534
535public:
536    char *parse(const char *toParse, size_t toParseSize) const {
537        char      *result           = NULL;
538        PartQueue *found_attributes = parse_all(LinePart(toParse, toParseSize));
539
540        if (found_attributes) {
541            result = found_attributes->to_string();
542            delete found_attributes;
543        }
544        return result;
545    }
546};
547
548static void search_comment_for_attribute() {
549    if (!found__ATTR__) { // only parse once (until reset)
550        if (search__ATTR__) {
551            last_comment[lc_size] = 0;  // close string
552
553            static AttributeParser ATTR_parser("__ATTR__", true, false);
554            found__ATTR__ = ATTR_parser.parse(last_comment, lc_size);
555        }
556    }
557}
558
559struct promotion {
560    char      *to_promote;                          // text to promote to header
561    promotion *next;
562};
563
564static promotion *promotions = 0;
565
566static void add_promotion(char *to_promote) {
567    promotion *new_promotion = (promotion*)malloc(sizeof(promotion));
568    new_promotion->to_promote       = to_promote;
569    new_promotion->next             = 0;
570
571    if (!promotions) {
572        promotions = new_promotion;
573    }
574    else { // append
575        promotion *last = promotions;
576        while (last->next) last = last->next;
577
578        last->next = new_promotion;
579    }
580}
581
582static void print_promotions() {
583    promotion *p = promotions;
584
585    if (promotions) fputc('\n', stdout);
586
587    while (p) {
588        promotion *next = p->next;
589
590        printf("%s\n", p->to_promote);
591        free(p->to_promote);
592        free(p);
593
594        p = next;
595    }
596
597    if (promotions) fputc('\n', stdout);
598    promotions = 0;
599}
600
601static const char *promotion_tag     = "AISC_MKPT_PROMOTE:";
602static int         promotion_tag_len = 18;
603
604static void search_comment_for_promotion() {
605    char *promotion_found;
606    last_comment[lc_size] = 0;  // close string
607
608    promotion_found = strstr(last_comment, promotion_tag);
609    while (promotion_found) {
610        char *behind_promo = promotion_found+promotion_tag_len;
611        char *eol, *eoc;
612        mp_assert(behind_promo[-1] == ':'); // wrong promotion_tag_len
613
614        eol = strchr(behind_promo, '\n');
615        eoc = strstr(behind_promo, "*/");
616
617        if (eoc && eol) {
618            if (eoc<eol) eol = eoc;
619        }
620        else if (!eol) {
621            eol = eoc;
622        }
623
624        if (!eol) eol = strchr(behind_promo, 0);
625
626        if (eol) {
627            while (eol>behind_promo && eol[-1] == ' ') --eol; // trim spaces at eol
628        }
629
630        mp_assert(eol);
631        if (!eol) {
632            promotion_found = 0;
633        }
634        else {
635            int   promo_length = eol-behind_promo;
636            char *to_promote   = (char*)malloc(promo_length+1);
637
638            memcpy(to_promote, behind_promo, promo_length);
639            to_promote[promo_length] = 0;
640
641            DEBUG_PRINT("promotion found!\n");
642
643            add_promotion(to_promote);
644            promotion_found = strstr(eol, promotion_tag);
645        }
646    }
647}
648
649/* read the next character from the file. If the character is '\' then
650 * read and skip the next character. Any comment sequence is converted
651 * to a blank.
652 *
653 * if a comment contains __ATTR__ and search__ATTR__ != 0
654 * the attribute string is stored in found__ATTR__
655 */
656
657
658static int fnextch(FILE *f) {
659    int c, lastc, incomment;
660
661    c = ngetc(f);
662    while (c == '\\') {
663        DEBUG_PRINT("fnextch: in backslash loop\n");
664        ngetc(f);                               // skip a character
665        c = ngetc(f);
666    }
667    if (c == '/' && !inquote) {
668        c = ngetc(f);
669        if (c == '*') {
670            long commentStartLine = linenum;
671
672            incomment = 1;
673            c         = ' ';
674            DEBUG_PRINT("fnextch: comment seen\n");
675            lc_size   = 0;
676
677            while (incomment) {
678                lastc                   = c;
679                c                       = ngetc(f);
680                last_comment[lc_size++] = c;
681                mp_assert(lc_size<MAX_COMMENT_SIZE);
682
683                if (lastc == '*' && c == '/') incomment = 0;
684                else if (c < 0) {
685                    error("EOF reached in comment");
686                    errorAt(commentStartLine, "comment started here");
687                    return c;
688                }
689            }
690            if (search__ATTR__) search_comment_for_attribute();
691            if (promote_lines) search_comment_for_promotion();
692            return fnextch(f);
693        }
694        else if (c == '/') {                        // C++ style comment
695            incomment = 1;
696            c         = ' ';
697            DEBUG_PRINT("fnextch: C++ comment seen\n");
698            lc_size   = 0;
699
700            while (incomment) {
701                lastc                   = c;
702                c                       = ngetc(f);
703                last_comment[lc_size++] = c;
704                mp_assert(lc_size<MAX_COMMENT_SIZE);
705
706                if (lastc != '\\' && c == '\n') incomment = 0;
707                else if (c < 0) break;
708            }
709            if (search__ATTR__) search_comment_for_attribute();
710            if (promote_lines) search_comment_for_promotion();
711
712            if (c == '\n') return c;
713            return fnextch(f);
714        }
715        else {
716            // if we pre-fetched a linefeed, remember to adjust the line number
717            if (c == '\n') linenum--;
718            ungetc(c, f);
719            return '/';
720        }
721    }
722    return c;
723}
724
725
726static int nextch(FILE *f) {
727    // Get the next "interesting" character. Comments are skipped, and strings
728    // are converted to "0". Also, if a line starts with "#" it is skipped.
729
730    int c = fnextch(f);
731
732    // skip preprocessor directives
733    // EXCEPTION: #line nnn or #nnn lines are interpreted
734
735    if (newline_seen && c == '#') {
736        // skip blanks
737        do {
738            c = fnextch(f);
739        } while (c >= 0 && (c == '\t' || c == ' '));
740
741        // check for #line
742        if (c == 'l') {
743            c = fnextch(f);
744            if (c != 'i')   // not a #line directive
745                goto skip_rest_of_line;
746            do {
747                c = fnextch(f);
748            } while (c >= 0 && c != '\n' && !isdigit(c));
749        }
750
751        // if we have a digit it's a line number, from the preprocessor
752        if (c >= 0 && isdigit(c)) {
753            char numbuf[10];
754            char *p = numbuf;
755            for (int n = 8; n >= 0; --n) {
756                *p++ = c;
757                c = fnextch(f);
758                if (c <= 0 || !isdigit(c))
759                    break;
760            }
761            *p = 0;
762            linenum = atol(numbuf) - 1;
763        }
764
765        // skip the rest of the line
766    skip_rest_of_line :
767        while (c >= 0 && c != '\n')
768            c = fnextch(f);
769        if (c < 0)
770            return c;
771    }
772    newline_seen = (c == '\n');
773
774    if (c == '\'' || c == '\"') {
775        char buffer[11];
776        int  index          = 0;
777        long quoteStartLine = linenum;
778
779        DEBUG_PRINT("nextch: in a quote\n");
780        inquote = c;
781        while ((c = fnextch(f)) >= 0) {
782            if (c == inquote) {
783                buffer[index] = 0;
784                DEBUG_PRINT("quoted content='");
785                DEBUG_PRINT(buffer);
786                DEBUG_PRINT("'\n");
787
788                DEBUG_PRINT("nextch: out of quote\n");
789
790                if (linenum != quoteStartLine) {
791                    error("multiline quotes");
792                    errorAt(quoteStartLine, "quotes opened here");
793                }
794
795                if (inquote=='\"' && strcmp(buffer, "C")==0) {
796                    inquote = 0;
797                    return '$';                     // found "C" (needed for 'extern "C"')
798                }
799                inquote = 0;
800                return '0';
801            }
802            else {
803                if (index<10) buffer[index++] = c;
804            }
805        }
806        error("EOF in a quote");
807        errorAt(quoteStartLine, "quote started here");
808        DEBUG_PRINT("nextch: EOF in a quote\n");
809    }
810    return c;
811}
812
813static int getsym(char *buf, FILE *f) {
814    /* Get the next symbol from the file, skipping blanks.
815     * Return 0 if OK, -1 for EOF.
816     * Also collapses everything between { and }
817     */
818
819    int c;
820    int inbrack = 0;
821
822#if defined(DEBUG_PRINTS)
823    char *bufStart = buf;
824#endif // DEBUG_PRINTS
825
826    c = glastc;
827    while ((c > 0) && isspace(c)) {
828        c = nextch(f);
829    }
830
831    if (c < 0) {
832        DEBUG_PRINT("EOF read in getsym\n");
833        return -1;
834    }
835
836    if (c == '{') {
837        long bracketStartLine = linenum;
838
839        inbrack = 1;
840        DEBUG_PRINT("getsym: in '{'\n");
841        while (inbrack) {
842            c = nextch(f);
843            if (c < 0) {
844                error("EOF seen in bracket loop (unbalanced brackets?)");
845                errorAt(bracketStartLine, "bracket opened here");
846                DEBUG_PRINT("getsym: EOF seen in bracket loop\n");
847                glastc = c;
848                return c;
849            }
850            if (c == '{') {
851                inbrack++;
852#if defined(DEBUG_PRINTS)
853                fprintf(stderr, "inbrack=%i (line=%li)\n", inbrack, linenum);
854#endif // DEBUG_PRINTS
855            }
856            else if (c == '}') {
857                inbrack--;
858#if defined(DEBUG_PRINTS)
859                fprintf(stderr, "inbrack=%i (line=%li)\n", inbrack, linenum);
860#endif // DEBUG_PRINTS
861            }
862        }
863        strcpy(buf, "{}");
864        glastc = nextch(f);
865        DEBUG_PRINT("getsym: returning brackets '");
866    }
867    else if (!IS_CSYM(c)) {
868        *buf++ = c;
869        *buf   = 0;
870        glastc = nextch(f);
871
872        DEBUG_PRINT("getsym: returning special symbol '");
873    }
874    else {
875        while (IS_CSYM(c)) {
876            *buf++ = c;
877            c = nextch(f);
878        }
879        *buf   = 0;
880        glastc = c;
881        DEBUG_PRINT("getsym: returning word '");
882    }
883
884    DEBUG_PRINT(bufStart);
885    DEBUG_PRINT("'\n");
886
887    return 0;
888}
889
890static int skipit(char *buf, FILE *f) {
891    // skip until a ";" or the end of a function declaration is seen
892
893    int i;
894    do {
895        DEBUG_PRINT("in skipit loop\n");
896
897        i = getsym(buf, f);
898        if (i < 0) return i;
899
900        DEBUG_PRINT("skipit: '");
901        DEBUG_PRINT(buf);
902        DEBUG_PRINT("'\n");
903
904    } while (*buf != ';' && *buf != '{');
905
906    return 0;
907}
908
909static int is_type_word(char *s) {
910    // find most common type specifiers for purpose of ruling them out as parm names
911
912    static const char *typewords[] = {
913        "char",     "const",    "double",   "enum",
914        "float",    "int",      "long",     "short",
915        "signed",   "struct",   "union",    "unsigned",
916        "void",     "volatile", (char *)0
917    };
918
919    const char **ss;
920
921    for (ss = typewords; *ss; ++ss)
922        if (strcmp(s, *ss) == 0)
923            return 1;
924
925    return 0;
926}
927
928
929/* Ad-hoc macro to recognize parameter name for purposes of removal.
930 * Idea is to remove the bulk of easily recognized parm names without
931 * losing too many type specifiers. (sg)
932 */
933#define IS_PARM_NAME(w) \
934    (IS_CSYM(*(w)->string) && !is_type_word((w)->string) && \
935    (!(w)->next || *(w)->next->string == ',' || \
936     *(w)->next->string == '['))
937
938
939static Word *typelist(Word *p) {
940    // given a list representing a type and a variable name, extract just
941    // the base type, e.g. "struct word *x" would yield "struct word"
942
943    Word *w, *r;
944
945    r = w = word_alloc("");
946    while (p && p->next) {
947        // handle int *x --> int
948        if (p->string[0] && !IS_CSYM(p->string[0]))
949            break;
950        // handle int x[] --> int
951        if (p->next->string[0] == '[')
952            break;
953        w->next = word_alloc(p->string);
954        w = w->next;
955        p = p->next;
956    }
957    return r;
958}
959
960static Word *getparamlist(FILE *f) {
961    // Get a parameter list; when this is called the next symbol in line
962    // should be the first thing in the list.
963
964    static Word *pname[MAXPARAM];                   // parameter names
965    Word        *tlist;                             // type name
966    Word        *plist;                             // temporary
967    int          np      = 0;                       // number of parameters
968    int          typed[MAXPARAM];                   // parameter has been given a type
969    int          tlistdone;                         // finished finding the type name
970    int          sawsomething;
971    int          i;
972    int          inparen = 0;
973    char         buf[80];
974
975    DEBUG_PRINT("in getparamlist\n");
976    for (i = 0; i < MAXPARAM; i++)
977        typed[i] = 0;
978
979    plist = word_alloc("");
980
981    // first, get the stuff inside brackets (if anything)
982
983    sawsomething = 0;                               // gets set nonzero when we see an arg
984    for (;;) {
985        if (getsym(buf, f) < 0) return NULL;
986        if (*buf == ')' && (--inparen < 0)) {
987            if (sawsomething) {                     // if we've seen an arg
988                pname[np] = plist;
989                plist = word_alloc(""); // @@@ Value stored to 'plist' is never read
990                np++;
991            }
992            break;
993        }
994        if (*buf == ';') {                          // something weird
995            return ABORTED;
996        }
997        sawsomething = 1;                           // there's something in the arg. list
998        if (*buf == ',' && inparen == 0) {
999            pname[np] = plist;
1000            plist = word_alloc("");
1001            np++;
1002        }
1003        else {
1004            addword(plist, buf);
1005            if (*buf == '(') inparen++;
1006        }
1007    }
1008
1009    // next, get the declarations after the function header
1010
1011    inparen = 0;
1012
1013    tlist = word_alloc("");
1014    plist = word_alloc("");
1015    tlistdone = 0;
1016    sawsomething = 0;
1017    for (;;) {
1018        if (getsym(buf, f) < 0) return NULL;
1019
1020        if (*buf == ',' && !inparen) {              // handle a list like "int x,y,z"
1021            if (!sawsomething)
1022                return NULL;
1023            for (i = 0; i < np; i++) {
1024                if (!typed[i] && foundin(plist, pname[i])) {
1025                    typed[i] = 1;
1026                    word_free(pname[i]);
1027                    pname[i] = word_append(tlist, plist);
1028                    // promote types
1029                    typefixhack(pname[i]);
1030                    break;
1031                }
1032            }
1033            if (!tlistdone) {
1034                tlist = typelist(plist);
1035                tlistdone = 1;
1036            }
1037            word_free(plist);
1038            plist = word_alloc("");
1039        }
1040        else if (*buf == ';') {                     // handle the end of a list
1041            if (!sawsomething)
1042                return ABORTED;
1043            for (i = 0; i < np; i++) {
1044                if (!typed[i] && foundin(plist, pname[i])) {
1045                    typed[i] = 1;
1046                    word_free(pname[i]);
1047                    pname[i] = word_append(tlist, plist);
1048                    typefixhack(pname[i]);
1049                    break;
1050                }
1051            }
1052            tlistdone = 0;
1053            word_free(tlist); word_free(plist);
1054            tlist = word_alloc("");
1055            plist = word_alloc("");
1056        }
1057        else if (strcmp(buf, "{}") == 0) break;     // handle the  beginning of the function
1058        else if (strcmp(buf, "register")) {         // otherwise, throw the word into the list (except for "register")
1059            addword(plist, buf);
1060            if (*buf == '(') inparen++;
1061            else if (*buf == ')') inparen--;
1062            else sawsomething = 1;
1063        }
1064    }
1065
1066    // Now take the info we have and build a prototype list
1067
1068    // empty parameter list means "void"
1069    if (np == 0) return word_alloc("void");
1070
1071    plist = tlist = word_alloc("");
1072
1073    /* how to handle parameters which contain only one word ?
1074     *
1075     * void -> void
1076     * UNFIXED -> UNFIXED     see ../SL/CB/cb_base.h@UNFIXED
1077     * xxx  -> int xxx        (if AUTO_INT is defined)
1078     * int  -> int dummyXX    (otherwise)
1079     */
1080
1081    // #define AUTO_INT
1082
1083    {
1084#if !defined(AUTO_INT)
1085        int dummy_counter = 1;
1086        char dummy_name[] = "dummy_xx";
1087#define DUMMY_COUNTER_POS 6
1088#endif
1089
1090        for (i = 0; i < np; i++) {
1091#if !defined(AUTO_INT)
1092            int add_dummy_name = 0;
1093#endif
1094            {
1095                Word *pn_list    = pname[i];
1096                int   cnt        = 0;
1097                bool  is_void    = false;
1098                bool  is_UNFIXED = false;
1099
1100                while (pn_list) {                   // count words
1101                    if (pn_list->string[0]) {
1102                        ++cnt;
1103                        if (strcmp(pn_list->string, "void")==0) is_void = true;
1104                        if (strcmp(pn_list->string, "UNFIXED")==0) is_UNFIXED = true;
1105                    }
1106                    pn_list = pn_list->next;
1107                }
1108                if (cnt==1 && !is_void && !is_UNFIXED) { // only name, but neighter 'void' nor 'UNFIXED'
1109                    // no type or no parameter name
1110#ifdef AUTO_INT
1111                    // If no type provided, make it an "int"
1112                    addword(tlist, "int"); // add int to tlist before adding pname[i]
1113#else
1114                    add_dummy_name = 1; // add a dummy name after adding pname[i]
1115#endif
1116                }
1117            }
1118
1119            while (tlist->next) tlist = tlist->next;
1120            tlist->next               = pname[i];
1121
1122#if !defined(AUTO_INT)
1123            if (add_dummy_name) {
1124                mp_assert(dummy_counter<100);
1125                dummy_name[DUMMY_COUNTER_POS] = (dummy_counter/10)+'0';
1126                dummy_name[DUMMY_COUNTER_POS] = (dummy_counter%10)+'0';
1127                addword(tlist, dummy_name);
1128                dummy_counter++;
1129            }
1130#endif
1131
1132            if (i < np - 1) addword(tlist, ",");
1133        }
1134    }
1135
1136    return plist;
1137}
1138
1139inline Word *getLastPtrRef(Word *w) {
1140    Word *last = NULL;
1141    while (w) {
1142        if (strchr("&*", w->string[0]) == NULL) break;
1143        last = w;
1144        w    = w->next;
1145    }
1146    return last;
1147}
1148
1149static void emit(Word *wlist, Word *plist, long startline) {
1150    // emit a function declaration. The attributes and name of the function
1151    // are in wlist; the parameters are in plist.
1152
1153    Word *w;
1154    int   count     = 0;
1155    int   isstatic  = 0;
1156    int   ismain    = 0;
1157    int   refs      = 0;
1158
1159    if (promote_lines) print_promotions();
1160
1161    for (w = wlist; w; w = w->next) {
1162        if (w->string[0]) {
1163            count ++;
1164            if      (strcmp(w->string, "static") == 0) isstatic = 1;
1165            else if (strcmp(w->string, "main")   == 0) ismain = 1;
1166        }
1167    }
1168
1169    if (ismain && !use_main) return;
1170
1171    if (aisc) {
1172        if (count < 2) {
1173            printf("int\t");
1174            w = wlist;
1175        }
1176        else {
1177            refs = 0;
1178            for (w = wlist; w; w = w->next) {
1179                if (w->string[0]) {
1180                    printf("%s,\t", w->string);
1181                    w = w->next;
1182                    break;
1183                }
1184            }
1185        }
1186        for (; w; w = w->next) {
1187            if (w->string[0] == '*') {
1188                refs++;
1189                continue;
1190            }
1191            if (w->string[0]) {
1192                printf("%s,", w->string);
1193                break;
1194            }
1195        }
1196        if (refs) {
1197            printf("\tlink%i", refs);
1198            refs = 0;
1199        }
1200        else {
1201            printf("\tterm");
1202        }
1203
1204        if (strcmp(plist->string, "void") != 0) {   // if parameter is not 'void'
1205            printf(",\t{\n");
1206            printf("\t@TYPE,\t@IDENT,\t@REF;\n");
1207
1208            int name_seen       = 0;
1209            int unnamed_counter = 1;
1210            for (w = plist; w; w = w->next) {
1211                if (w->string[0] == '*') {
1212                    refs++;
1213                    name_seen = 0;
1214                    continue;
1215                }
1216                if (w->string[0] == ',') {
1217                    if (refs) {
1218                        printf("\tlink%i;\n", refs);
1219                        refs = 0;
1220                        continue;
1221                    }
1222                    else {
1223                        printf("\tterm;\n");
1224                        continue;
1225                    }
1226                }
1227                if (w->string[0]) {
1228                    printf("\t%s,", w->string);
1229                    name_seen = 1;
1230                }
1231            }
1232            if (refs) {
1233                if (!name_seen) {                   // automatically insert missing parameter names
1234                    printf("\tunnamed%i,", unnamed_counter++);
1235                }
1236                printf("\tlink%i;\n", refs);
1237                refs = 0;
1238            }
1239            else {
1240                printf("\tterm;\n");
1241            }
1242            printf("}");
1243        }
1244        printf(";\n\n");
1245        return;
1246    }
1247    DEBUG_PRINT("emit called\n");
1248    if (donum)
1249        printf("/*%8ld */ ", startline);
1250
1251
1252    // if the -e flag was given, and it's not a static function, print "extern"
1253
1254    if (print_extern && !isstatic) {
1255        printf("extern ");
1256    }
1257
1258    int spaceBeforeNext = 0;
1259    if (count < 2) {
1260        printf("int");
1261        spaceBeforeNext = 1;
1262    }
1263
1264    for (w = wlist; w; w = w->next) {
1265        if (spaceBeforeNext) {
1266            DEBUG_PRINT("emit[1] ' '\n");
1267            putchar(' ');
1268        }
1269        printf("%s", w->string);
1270        DEBUG_PRINT_STRING("emit[2] ", w->string);
1271        spaceBeforeNext = IS_CSYM(w->string[0]);
1272    }
1273
1274    if (use_macro) printf(" %s((", macro_name);
1275    else putchar('(');
1276    DEBUG_PRINT("emit[3] '('\n");
1277
1278    spaceBeforeNext = 0;
1279    for (w = plist; w; w = w->next) {
1280        if (no_parm_names && IS_PARM_NAME(w)) continue;
1281
1282        const char *token    = w->string;
1283        char        tokStart = token[0];
1284
1285        if (!tokStart) continue;                    // empty token
1286
1287        if (tokStart == ',')                       spaceBeforeNext = 1;
1288        else if (strchr("[])", tokStart) != NULL)  spaceBeforeNext = 0;
1289        else {
1290            int nextSpaceBeforeNext;
1291            if (strchr("&*", tokStart) != NULL) {
1292                if (spaceBeforeNext) {
1293                    Word *lastPtrRef = getLastPtrRef(w);
1294                    if (lastPtrRef->string[0] == '&') spaceBeforeNext = 0;
1295                }
1296                nextSpaceBeforeNext = tokStart == '&';
1297            }
1298            else {
1299                nextSpaceBeforeNext = IS_CSYM(tokStart);;
1300            }
1301            if (spaceBeforeNext) {
1302                putchar(' ');
1303                DEBUG_PRINT("emit[4] ' '\n");
1304            }
1305            spaceBeforeNext = nextSpaceBeforeNext;
1306        }
1307        fputs(token, stdout);
1308        DEBUG_PRINT_STRING("emit[5] ", token);
1309    }
1310
1311    if (use_macro)  PRINT("))");
1312    else            PRINT(")");
1313    DEBUG_PRINT("emit[6] ')'\n");
1314
1315    if (found__ATTR__) { PRINT(" "); PRINT(found__ATTR__); }
1316
1317    PRINT(";\n");
1318}
1319
1320static void getdecl(FILE *f, const char *header) {
1321    // parse all function declarations and print to STDOUT
1322
1323    Word *plist, *wlist  = NULL;
1324    char  buf[80];
1325    int   sawsomething;
1326    long  startline;                                // line where declaration started
1327    int   oktoprint;
1328    int   header_printed = 0;
1329
1330    current_file = strdup(header);
1331
1332 again :
1333    DEBUG_PRINT("again\n");
1334    word_free(wlist);
1335    wlist         = word_alloc("");
1336    sawsomething  = 0;
1337    oktoprint     = 1;
1338    extern_c_seen = 0;
1339
1340    for (;;) {
1341        DEBUG_PRINT("main getdecl loop\n");
1342        if (getsym(buf, f) < 0) {
1343            DEBUG_PRINT("EOF in getdecl loop\n");
1344            return;
1345        }
1346
1347        DEBUG_PRINT("getdecl: '");
1348        DEBUG_PRINT(buf);
1349        DEBUG_PRINT("'\n");
1350
1351        // try to guess when a declaration is not an external function definition
1352        if (strcmp(buf, ",")==0 ||
1353            strcmp(buf, "=")==0 ||
1354            strcmp(buf, "typedef")==0 ||
1355            strcmp(buf, "[")==0)
1356        {
1357            skipit(buf, f);
1358            goto again;
1359        }
1360
1361        if (strcmp(buf, "{}")==0) {
1362            if (!extern_c_seen) skipit(buf, f);
1363            goto again;
1364        }
1365
1366        if (strcmp(buf, "extern")==0) {
1367            if (getsym(buf, f)<0) {
1368                DEBUG_PRINT("EOF in getdecl loop\n");
1369                return;
1370            }
1371
1372            DEBUG_PRINT("test auf extern \"C\": '");
1373            DEBUG_PRINT(buf);
1374            DEBUG_PRINT("'\n");
1375
1376            if (strcmp(buf, "$") == 0) {            // symbol used if "C" was found
1377                extern_c_seen = 1;
1378                if (promote_extern_c) {
1379                    addword(wlist, "extern");
1380                    addword(wlist, "\"C\" ");
1381                    sawsomething = 1;
1382                }
1383                continue;
1384            }
1385
1386            skipit(buf, f);
1387            goto again;
1388        }
1389
1390        if (strncmp(buf, "__ATTR__", 8) == 0) { // prefix attribute (should only be used for static and inline)
1391            DEBUG_PRINT("seen prefix __ATTR__: '");
1392            DEBUG_PRINT(buf);
1393            DEBUG_PRINT("'\n");
1394
1395            bool seen_static_or_inline = false;
1396
1397            while (!seen_static_or_inline) {
1398                if (getsym(buf, f)<0) {
1399                    DEBUG_PRINT("EOF in getdecl loop (behind prefix __ATTR__)\n");
1400                    return;
1401                }
1402                if (strcmp(buf, "static") == 0 || strcmp(buf, "inline") == 0) {
1403                    seen_static_or_inline = true;
1404                }
1405                else {
1406                    DEBUG_PRINT("read over (behind prefix __ATTR__): '");
1407                    DEBUG_PRINT(buf);
1408                    DEBUG_PRINT("'\n");
1409                }
1410            }
1411        }
1412
1413        if (oktoprint) {
1414            if (strcmp(buf, "static") == 0 || strcmp(buf, "inline") == 0) {
1415                oktoprint = 0;
1416            }
1417        }
1418
1419
1420        if (strcmp(buf, ";") == 0) goto again;
1421
1422        // A left parenthesis *might* indicate a function definition
1423        if (strcmp(buf, "(")==0) {
1424            startline = linenum;
1425            if (!sawsomething || !(plist = getparamlist(f))) {
1426                skipit(buf, f);
1427                goto again;
1428            }
1429            if (plist == ABORTED)
1430                goto again;
1431
1432            // It seems to have been what we wanted
1433
1434            if (oktoprint) {                        // check function-name
1435                Word *w;
1436
1437                for (w=wlist; w->next && oktoprint; w=w->next) {
1438                    if (w->string[0]==':' && w->string[1]==0) oktoprint = 0; // do not emit prototypes for member functions
1439                }
1440
1441                if (oktoprint && !wantPrototypeFor(w->string)) { 
1442                    oktoprint = 0;                  // => do not emit prototype
1443                }
1444            }
1445
1446            if (oktoprint) {
1447                if (!header_printed) {
1448                    if (aisc) printf("\n# %s\n", header);
1449                    else printf("\n/* %s */\n", header);
1450                    header_printed = 1;
1451                }
1452                emit(wlist, plist, startline);
1453            }
1454            clear_found_attribute();
1455
1456            word_free(plist);
1457            goto again;
1458        }
1459
1460        addword(wlist, buf);
1461        sawsomething = 1;
1462    }
1463}
1464
1465__ATTR__NORETURN static void Usage(const char *msg = NULL) {
1466    fprintf(stderr,
1467            "\naisc_mkpts - ARB prototype generator"
1468            "\nUsage: %s [options] [files ...]", ourname);
1469    fputs("\nSupported options:"
1470          "\n   -F part[,part]*  only promote declarations for functionnames containing one of the parts"
1471          "\n                    if 'part' starts with a '^' functionname has to start with rest of part"
1472          "\n   -S (like -F)     do NOT promote matching declarations (defaults to -S '^TEST_,^NOTEST_')"
1473          "\n"
1474          "\n                    Instead of ',' (=or) you may use '+' (=and)"
1475          "\n"
1476          "\n   -G               search for ARB macro __ATTR__ in comment behind function header"
1477          "\n   -P               promote /*AISC_MKPT_PROMOTE:forHeader*/ to header"
1478          "\n"
1479          "\n   -w gen.h         add standard include wrapper (needs name of generated header)"
1480          "\n   -c \"text\"        add text as comment into header"
1481          "\n"
1482          "\n   -C               insert 'extern \"C\"'"
1483          "\n   -E               prefix 'extern \"C\"' at prototype"
1484          "\n"
1485          "\n   -W               don't promote types in old style declarations"
1486          "\n   -x               omit parameter names in prototypes"
1487          "\n"
1488          "\n   -K               use K&R prototype macro (default assumes header files are strict ANSI)"
1489          "\n   -D               define K&R prototype macro"
1490          "\n   -p sym           use \"sym\" as the prototype macro (default \"P_\")"
1491          "\n"
1492          "\n   -m               promote declaration of 'main()' (default is to skip it)"
1493          "\n   -a               make a function list for aisc_includes (default: generate C prototypes)"
1494          "\n   -e               put an explicit \"extern\" keyword in declarations"
1495          "\n   -n               put line numbers of declarations as comments"
1496          "\n"
1497          "\n   -V               print version number"
1498          "\n   -h               print this help"
1499          "\n"
1500          , stderr);
1501    if (msg) fprintf(stderr, "\nError: %s", msg);
1502    fputc('\n', stderr);
1503    exit(EXIT_FAILURE);
1504}
1505
1506static int string_comparator(const void *v0, const void *v1) {
1507    return strcmp(*(const char **)v0, *(const char **)v1);
1508}
1509
1510static void MissingArgumentFor(char option) {
1511    char buffer[100];
1512    sprintf(buffer, "option -%c expects an argument", option);
1513    Usage(buffer);
1514}
1515static void UnknownOption(char option) {
1516    char buffer[100];
1517    sprintf(buffer, "unknown option -%c", option);
1518    Usage(buffer);
1519}
1520
1521int ARB_main(int argc, char *argv[]) {
1522    bool exit_if_noargs = false;
1523
1524    if (argv[0] && argv[0][0]) {
1525        ourname = strrchr(argv[0], '/');
1526        if (!ourname) ourname = argv[0];
1527    }
1528    else ourname = "mkptypes";
1529
1530    argv++; argc--;
1531
1532    addExcludedSymParts("^TEST_,^NOTEST_"); // exclude unit tests
1533
1534    char *iobuf = (char *)malloc(NEWBUFSIZ);
1535    while (*argv && **argv == '-') {
1536        const char *t = *argv++; --argc; t++;
1537        while (*t) {
1538            if (*t == 'e')      print_extern        = 1;
1539            else if (*t == 'C') cansibycplus        = 1;
1540            else if (*t == 'E') promote_extern_c    = 1;
1541            else if (*t == 'W') dont_promote        = 1;
1542            else if (*t == 'a') aisc                = 1;
1543            else if (*t == 'G') search__ATTR__      = 1;
1544            else if (*t == 'n') donum               = 1;
1545            else if (*t == 'x') no_parm_names       = 1; // no parm names, only types (sg)
1546            else if (*t == 'D') define_macro        = 1;
1547            else if (*t == 'K') use_macro           = 1;
1548            else if (*t == 'P') promote_lines       = 1;
1549            else if (*t == 'm') use_main            = 1;
1550            else if (*t == 'p') {
1551                t = *argv++; --argc;
1552                if (!t) MissingArgumentFor(*t);
1553                use_macro = 1;
1554                macro_name = t;
1555                break;
1556            }
1557            else if (*t == 'c') {
1558                t = *argv++; --argc;
1559                if (!t) MissingArgumentFor(*t);
1560                header_comment = t;
1561                break;
1562            }
1563            else if (*t == 'w') {
1564                t = *argv++; --argc;
1565                if (!t) MissingArgumentFor(*t);
1566                include_wrapper = t;
1567                break;
1568            }
1569            else if (*t == 'F') {
1570                t = *argv++; --argc;
1571                if (!t) MissingArgumentFor(*t);
1572                addRequiredSymParts(t);
1573                break;
1574            }
1575            else if (*t == 'S') {
1576                t = *argv++; --argc;
1577                if (!t) MissingArgumentFor(*t);
1578                freeExcludedSymParts();
1579                addExcludedSymParts(t);
1580                break;
1581            }
1582            else if (*t == 'V') {
1583                exit_if_noargs = true;
1584                Version();
1585            }
1586            else if (*t == 'h') Usage();
1587            else UnknownOption(*t);
1588            t++;
1589        }
1590    }
1591
1592    if (argc == 0 && exit_if_noargs) {
1593        exit(EXIT_FAILURE);
1594    }
1595
1596    char *include_macro = 0;
1597    if (aisc) {
1598        if (header_comment) {
1599            printf("# %s\n#\n", header_comment);
1600        }
1601        fputs("# This file is generated by aisc_mkpt.\n"
1602              "# Any changes you make here will be overwritten later!\n"
1603              "\n"
1604              "@FUNCTION_TYPE, @FUNCTION, @FUNCTION_REF;", stdout);
1605    }
1606    else {
1607        fputs("/*", stdout);
1608        if (header_comment) printf(" %s.\n *\n *", header_comment);
1609        fputs(" This file is generated by aisc_mkpt.\n"
1610              " * Any changes you make here will be overwritten later!\n"
1611              " */\n"
1612              "\n"
1613              , stdout);
1614
1615        if (include_wrapper) {
1616            int p;
1617            include_macro = strdup(include_wrapper);
1618            for (p = 0; include_macro[p]; p++) {
1619                char c           = include_macro[p];
1620                c                = strchr(".-", c) ? '_' : toupper(c);
1621                include_macro[p] = c;
1622            }
1623
1624            printf("#ifndef %s\n"
1625                   "#define %s\n"
1626                   "\n",
1627                   include_macro,
1628                   include_macro);
1629        }
1630
1631        if (use_macro) {
1632            if (define_macro) {
1633                fprintf(stdout,
1634                        "#ifndef %s\n"
1635                        "# if defined(__STDC__) || defined(__cplusplus)\n"
1636                        "#  define %s(s) s\n"
1637                        "# else\n"
1638                        "#  define %s(s) ()\n"
1639                        "# endif\n"
1640                        "#else\n"
1641                        "# error %s already defined elsewhere\n"
1642                        "#endif\n\n",
1643                        macro_name, macro_name, macro_name, macro_name);
1644            }
1645            else {
1646                fprintf(stdout,
1647                        "#ifndef %s\n"
1648                        "# error %s is not defined\n"
1649                        "#endif\n\n",
1650                        macro_name, macro_name);
1651            }
1652        }
1653        if (search__ATTR__) {
1654            fputs("/* define ARB attributes: */\n"
1655                  "#ifndef ATTRIBUTES_H\n"
1656                  "# include <attributes.h>\n"
1657                  "#endif\n\n", stdout);
1658        }
1659        if (cansibycplus) {
1660            fputs("#ifdef __cplusplus\n"
1661                  "extern \"C\" {\n"
1662                  "#endif\n\n", stdout);
1663        }
1664    }
1665
1666    current_dir = strdup(getcwd(0, 255));
1667    if (argc == 0) {
1668        getdecl(stdin, "<from stdin>");
1669    }
1670    else {
1671        const char *filename[1000];
1672        int         fcount = 0;
1673
1674        while (argc > 0 && *argv) {
1675            filename[fcount++] = *argv;
1676            argc--; argv++;
1677        }
1678
1679        qsort(&filename, fcount, sizeof(filename[0]), string_comparator);
1680
1681        for (int i = 0; i<fcount; ++i) {
1682            DEBUG_PRINT("trying new file '");
1683            DEBUG_PRINT(filename[i]);
1684            DEBUG_PRINT("'\n");
1685
1686            FILE *f = fopen(filename[i], "r");
1687            if (!f) {
1688                perror(filename[i]);
1689                exit(EXIT_FAILURE);
1690            }
1691            if (iobuf) setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ);
1692
1693            linenum      = 1;
1694            newline_seen = 1;
1695            glastc       = ' ';
1696            getdecl(f, filename[i]);
1697            fclose(f);
1698
1699            free(current_file);
1700            current_file = 0;
1701        }
1702    }
1703    if (aisc) {
1704    }
1705    else {
1706        if (cansibycplus) {
1707            fputs("\n#ifdef __cplusplus\n"
1708                  "}\n"
1709                  "#endif\n", stdout);
1710        }
1711        if (use_macro && define_macro) {
1712            printf("\n#undef %s\n", macro_name);    // clean up namespace
1713        }
1714
1715        if (include_wrapper) {
1716            printf("\n"
1717                   "#else\n"
1718                   "#error %s included twice\n"
1719                   "#endif /* %s */\n",
1720                   include_wrapper,
1721                   include_macro);
1722        }
1723    }
1724
1725    free(include_macro);
1726
1727    freeRequiredSymParts();
1728    freeExcludedSymParts();
1729
1730    free(current_file);
1731    free(current_dir);
1732
1733    free(iobuf);
1734
1735    return EXIT_SUCCESS;
1736}
1737
1738static void Version() {
1739    fprintf(stderr, "%s 1.1 ARB\n", ourname);
1740}
1741
1742// --------------------------------------------------------------------------------
1743
1744#ifdef UNIT_TESTS
1745
1746#include <test_unit.h>
1747
1748inline const char *test_extract(bool ATTR, const char *str) {
1749    search__ATTR__ = true;
1750
1751    clear_found_attribute();
1752
1753    strcpy(last_comment, str);
1754    lc_size = strlen(last_comment);
1755
1756    search_comment_for_attribute();
1757
1758    return found__ATTR__;
1759}
1760
1761#define TEST_ATTR_____(comment,extracted) TEST_EXPECT_EQUAL(test_extract(true, comment), extracted)
1762
1763void TEST_attribute_parser() {
1764    TEST_ATTR_____("",             (const char*)NULL);
1765    TEST_ATTR_____("nothing here", (const char*)NULL);
1766
1767    TEST_ATTR_____("bla bla __ATTR__DEPRECATED(\" my reason \") more content", "__ATTR__DEPRECATED(\" my reason \")");
1768    TEST_ATTR_____("bla bla __ATTR__FORMAT(pos) more content",                 "__ATTR__FORMAT(pos)");
1769   
1770    TEST_ATTR_____("__ATTR__DEPRECATED",       "__ATTR__DEPRECATED");
1771    TEST_ATTR_____("__ATTR__FORMAT(pos)",      "__ATTR__FORMAT(pos)");
1772    TEST_ATTR_____("bla __ATTR__FORMAT(pos)",  "__ATTR__FORMAT(pos)");
1773    TEST_ATTR_____("__ATTR__FORMAT(pos) bla",  "__ATTR__FORMAT(pos)");
1774    TEST_ATTR_____("    __ATTR__FORMAT(pos) ", "__ATTR__FORMAT(pos)");
1775   
1776    TEST_ATTR_____("__ATTR__FORMAT((pos)",           (const char*)NULL);
1777    TEST_ATTR_____("__attribute__(pos",              (const char*)NULL);
1778    TEST_ATTR_____("__ATTR__FORMAT(pos))",           "__ATTR__FORMAT(pos)");
1779    TEST_ATTR_____("__ATTR__FORMAT((pos))",          "__ATTR__FORMAT((pos))");
1780    TEST_ATTR_____("__ATTR__FORMAT((pos)+((sop)))",  "__ATTR__FORMAT((pos)+((sop)))");
1781    TEST_ATTR_____("__ATTR__FORMAT(((pos)))+(sop))", "__ATTR__FORMAT(((pos)))");
1782
1783    TEST_ATTR_____("bla bla __ATTR__DEPRECATED __ATTR__FORMAT(pos) more content", "__ATTR__DEPRECATED __ATTR__FORMAT(pos)");
1784    TEST_ATTR_____("bla __ATTR__DEPRECATED bla more__ATTR__FORMAT(pos)content", "__ATTR__DEPRECATED __ATTR__FORMAT(pos)");
1785   
1786    TEST_ATTR_____(" goes to header: __ATTR__NORETURN  */", "__ATTR__NORETURN");
1787
1788    clear_found_attribute();
1789}
1790TEST_PUBLISH(TEST_attribute_parser);
1791
1792#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.