source: tags/ms_r16q2/AISC_MKPTPS/mkptypes.cxx

Last change on this file was 14053, checked in by westram, 9 years ago
  • accept 'static __ATTR__XXX' (caused several prototypes to be skipped)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.3 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*& word) {
240    Word *w = word;
241    while (w) {
242        Word *oldw = w;
243        w = w->next;
244        free(oldw);
245    }
246    word = NULL;
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    Word *pname[MAXPARAM];                   // parameter names
965    Word *tlist   = NULL;                    // 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    Word *result = NULL;
976
977    DEBUG_PRINT("in getparamlist\n");
978    for (i = 0; i < MAXPARAM; i++)
979        typed[i] = 0;
980
981    plist = word_alloc("");
982
983    // first, get the stuff inside brackets (if anything)
984
985    sawsomething = 0;                               // gets set nonzero when we see an arg
986    for (;;) {
987        if (getsym(buf, f) < 0) {
988            goto cleanup;
989        }
990        if (*buf == ')' && (--inparen < 0)) {
991            if (sawsomething) {                     // if we've seen an arg
992                pname[np] = plist;
993                plist = word_alloc(""); // @@@ Value stored to 'plist' is never read
994                np++;
995            }
996            break;
997        }
998        if (*buf == ';') {                          // something weird
999            result = ABORTED;
1000            goto cleanup;
1001        }
1002        sawsomething = 1;                           // there's something in the arg. list
1003        if (*buf == ',' && inparen == 0) {
1004            pname[np] = plist;
1005            plist = word_alloc("");
1006            np++;
1007        }
1008        else {
1009            addword(plist, buf);
1010            if (*buf == '(') inparen++;
1011        }
1012    }
1013
1014    // next, get the declarations after the function header
1015
1016    inparen = 0;
1017
1018    word_free(plist);
1019
1020    tlist = word_alloc("");
1021    plist = word_alloc("");
1022
1023    tlistdone    = 0;
1024    sawsomething = 0;
1025
1026    for (;;) {
1027        if (getsym(buf, f) < 0) {
1028            goto cleanup;
1029        }
1030
1031        if (*buf == ',' && !inparen) {              // handle a list like "int x,y,z"
1032            if (!sawsomething) {
1033                goto cleanup;
1034            }
1035            for (i = 0; i < np; i++) {
1036                if (!typed[i] && foundin(plist, pname[i])) {
1037                    typed[i] = 1;
1038                    word_free(pname[i]);
1039                    pname[i] = word_append(tlist, plist);
1040                    // promote types
1041                    typefixhack(pname[i]);
1042                    break;
1043                }
1044            }
1045            if (!tlistdone) {
1046                word_free(tlist);
1047                tlist     = typelist(plist);
1048                tlistdone = 1;
1049            }
1050            word_free(plist);
1051            plist = word_alloc("");
1052        }
1053        else if (*buf == ';') {                     // handle the end of a list
1054            if (!sawsomething) {
1055                result = ABORTED;
1056                goto cleanup;
1057            }
1058            for (i = 0; i < np; i++) {
1059                if (!typed[i] && foundin(plist, pname[i])) {
1060                    typed[i] = 1;
1061                    word_free(pname[i]);
1062                    pname[i] = word_append(tlist, plist);
1063                    typefixhack(pname[i]);
1064                    break;
1065                }
1066            }
1067            tlistdone = 0;
1068            word_free(tlist);
1069            word_free(plist);
1070            tlist = word_alloc("");
1071            plist = word_alloc("");
1072        }
1073        else if (strcmp(buf, "{}") == 0) break;     // handle the  beginning of the function
1074        else if (strcmp(buf, "register")) {         // otherwise, throw the word into the list (except for "register")
1075            addword(plist, buf);
1076            if (*buf == '(') inparen++;
1077            else if (*buf == ')') inparen--;
1078            else sawsomething = 1;
1079        }
1080    }
1081
1082    // Now take the info we have and build a prototype list
1083
1084    word_free(plist);
1085    word_free(tlist);
1086
1087    // empty parameter list means "void"
1088    if (np == 0) {
1089        result = word_alloc("void");
1090        goto cleanup;
1091    }
1092
1093    plist = tlist = word_alloc("");
1094
1095    /* how to handle parameters which contain only one word ?
1096     *
1097     * void -> void
1098     * UNFIXED -> UNFIXED     see ../SL/CB/cb_base.h@UNFIXED
1099     * xxx  -> int xxx        (if AUTO_INT is defined)
1100     * int  -> int dummyXX    (otherwise)
1101     */
1102
1103    // #define AUTO_INT
1104
1105    {
1106#if !defined(AUTO_INT)
1107        int dummy_counter = 1;
1108        char dummy_name[] = "dummy_xx";
1109#define DUMMY_COUNTER_POS 6
1110#endif
1111
1112        for (i = 0; i < np; i++) {
1113#if !defined(AUTO_INT)
1114            int add_dummy_name = 0;
1115#endif
1116            {
1117                Word *pn_list    = pname[i];
1118                int   cnt        = 0;
1119                bool  is_void    = false;
1120                bool  is_UNFIXED = false;
1121
1122                while (pn_list) {                   // count words
1123                    if (pn_list->string[0]) {
1124                        ++cnt;
1125                        if (strcmp(pn_list->string, "void")==0) is_void = true;
1126                        if (strcmp(pn_list->string, "UNFIXED")==0) is_UNFIXED = true;
1127                    }
1128                    pn_list = pn_list->next;
1129                }
1130                if (cnt==1 && !is_void && !is_UNFIXED) { // only name, but neighter 'void' nor 'UNFIXED'
1131                    // no type or no parameter name
1132#ifdef AUTO_INT
1133                    // If no type provided, make it an "int"
1134                    addword(tlist, "int"); // add int to tlist before adding pname[i]
1135#else
1136                    add_dummy_name = 1; // add a dummy name after adding pname[i]
1137#endif
1138                }
1139            }
1140
1141            while (tlist->next) tlist = tlist->next;
1142
1143            tlist->next = pname[i];
1144            pname[i]    = NULL;
1145
1146#if !defined(AUTO_INT)
1147            if (add_dummy_name) {
1148                mp_assert(dummy_counter<100);
1149                dummy_name[DUMMY_COUNTER_POS] = (dummy_counter/10)+'0';
1150                dummy_name[DUMMY_COUNTER_POS] = (dummy_counter%10)+'0';
1151                addword(tlist, dummy_name);
1152                dummy_counter++;
1153            }
1154#endif
1155
1156            if (i < np - 1) addword(tlist, ",");
1157        }
1158    }
1159
1160    result = plist;
1161    plist  = NULL;
1162    tlist  = NULL;
1163
1164  cleanup:
1165
1166    word_free(plist);
1167    word_free(tlist);
1168    for (int n = 0; n<np; ++n) {
1169        word_free(pname[n]);
1170    }
1171
1172    return result;
1173}
1174
1175inline Word *getLastPtrRef(Word *w) {
1176    Word *last = NULL;
1177    while (w) {
1178        if (strchr("&*", w->string[0]) == NULL) break;
1179        last = w;
1180        w    = w->next;
1181    }
1182    return last;
1183}
1184
1185static void emit(Word *wlist, Word *plist, long startline) {
1186    // emit a function declaration. The attributes and name of the function
1187    // are in wlist; the parameters are in plist.
1188
1189    Word *w;
1190    int   count     = 0;
1191    int   isstatic  = 0;
1192    int   ismain    = 0;
1193    int   refs      = 0;
1194
1195    if (promote_lines) print_promotions();
1196
1197    for (w = wlist; w; w = w->next) {
1198        if (w->string[0]) {
1199            count ++;
1200            if      (strcmp(w->string, "static") == 0) isstatic = 1;
1201            else if (strcmp(w->string, "main")   == 0) ismain = 1;
1202        }
1203    }
1204
1205    if (ismain && !use_main) return;
1206
1207    if (aisc) {
1208        if (count < 2) {
1209            printf("int\t");
1210            w = wlist;
1211        }
1212        else {
1213            refs = 0;
1214            for (w = wlist; w; w = w->next) {
1215                if (w->string[0]) {
1216                    printf("%s,\t", w->string);
1217                    w = w->next;
1218                    break;
1219                }
1220            }
1221        }
1222        for (; w; w = w->next) {
1223            if (w->string[0] == '*') {
1224                refs++;
1225                continue;
1226            }
1227            if (w->string[0]) {
1228                printf("%s,", w->string);
1229                break;
1230            }
1231        }
1232        if (refs) {
1233            printf("\tlink%i", refs);
1234            refs = 0;
1235        }
1236        else {
1237            printf("\tterm");
1238        }
1239
1240        if (strcmp(plist->string, "void") != 0) {   // if parameter is not 'void'
1241            printf(",\t{\n");
1242            printf("\t@TYPE,\t@IDENT,\t@REF;\n");
1243
1244            int name_seen       = 0;
1245            int unnamed_counter = 1;
1246            for (w = plist; w; w = w->next) {
1247                if (w->string[0] == '*') {
1248                    refs++;
1249                    name_seen = 0;
1250                    continue;
1251                }
1252                if (w->string[0] == ',') {
1253                    if (refs) {
1254                        printf("\tlink%i;\n", refs);
1255                        refs = 0;
1256                        continue;
1257                    }
1258                    else {
1259                        printf("\tterm;\n");
1260                        continue;
1261                    }
1262                }
1263                if (w->string[0]) {
1264                    printf("\t%s,", w->string);
1265                    name_seen = 1;
1266                }
1267            }
1268            if (refs) {
1269                if (!name_seen) {                   // automatically insert missing parameter names
1270                    printf("\tunnamed%i,", unnamed_counter++);
1271                }
1272                printf("\tlink%i;\n", refs);
1273                refs = 0;
1274            }
1275            else {
1276                printf("\tterm;\n");
1277            }
1278            printf("}");
1279        }
1280        printf(";\n\n");
1281        return;
1282    }
1283    DEBUG_PRINT("emit called\n");
1284    if (donum)
1285        printf("/*%8ld */ ", startline);
1286
1287
1288    // if the -e flag was given, and it's not a static function, print "extern"
1289
1290    if (print_extern && !isstatic) {
1291        printf("extern ");
1292    }
1293
1294    int spaceBeforeNext = 0;
1295    if (count < 2) {
1296        printf("int");
1297        spaceBeforeNext = 1;
1298    }
1299
1300    for (w = wlist; w; w = w->next) {
1301        if (spaceBeforeNext) {
1302            DEBUG_PRINT("emit[1] ' '\n");
1303            putchar(' ');
1304        }
1305        printf("%s", w->string);
1306        DEBUG_PRINT_STRING("emit[2] ", w->string);
1307        spaceBeforeNext = IS_CSYM(w->string[0]);
1308    }
1309
1310    if (use_macro) printf(" %s((", macro_name);
1311    else putchar('(');
1312    DEBUG_PRINT("emit[3] '('\n");
1313
1314    spaceBeforeNext = 0;
1315    for (w = plist; w; w = w->next) {
1316        if (no_parm_names && IS_PARM_NAME(w)) continue;
1317
1318        const char *token    = w->string;
1319        char        tokStart = token[0];
1320
1321        if (!tokStart) continue;                    // empty token
1322
1323        if (tokStart == ',')                       spaceBeforeNext = 1;
1324        else if (strchr("[])", tokStart) != NULL)  spaceBeforeNext = 0;
1325        else {
1326            int nextSpaceBeforeNext;
1327            if (strchr("&*", tokStart) != NULL) {
1328                if (spaceBeforeNext) {
1329                    Word *lastPtrRef = getLastPtrRef(w);
1330                    if (lastPtrRef->string[0] == '&') spaceBeforeNext = 0;
1331                }
1332                nextSpaceBeforeNext = tokStart == '&';
1333            }
1334            else {
1335                nextSpaceBeforeNext = IS_CSYM(tokStart);;
1336            }
1337            if (spaceBeforeNext) {
1338                putchar(' ');
1339                DEBUG_PRINT("emit[4] ' '\n");
1340            }
1341            spaceBeforeNext = nextSpaceBeforeNext;
1342        }
1343        fputs(token, stdout);
1344        DEBUG_PRINT_STRING("emit[5] ", token);
1345    }
1346
1347    if (use_macro)  PRINT("))");
1348    else            PRINT(")");
1349    DEBUG_PRINT("emit[6] ')'\n");
1350
1351    if (found__ATTR__) { PRINT(" "); PRINT(found__ATTR__); }
1352
1353    PRINT(";\n");
1354}
1355
1356static void getdecl(FILE *f, const char *header) {
1357    // parse all function declarations and print to STDOUT
1358
1359    Word *wlist                 = NULL;
1360    char  buf[80];
1361    int   sawsomething;
1362    long  startline;                                // line where declaration started
1363    int   oktoprint;
1364    int   header_printed        = 0;
1365
1366    current_file = strdup(header);
1367
1368  again :
1369    DEBUG_PRINT("again\n");
1370
1371    word_free(wlist);
1372    wlist = word_alloc("");
1373
1374    bool seen_static_or_inline = false;
1375    bool seen__ATTR            = false;
1376
1377    sawsomething  = 0;
1378    oktoprint     = 1;
1379    extern_c_seen = 0;
1380
1381    for (;;) {
1382        DEBUG_PRINT("main getdecl loop\n");
1383        if (getsym(buf, f) < 0) {
1384            DEBUG_PRINT("EOF in getdecl loop\n");
1385            goto end;
1386        }
1387
1388        DEBUG_PRINT("getdecl: '");
1389        DEBUG_PRINT(buf);
1390        DEBUG_PRINT("'\n");
1391
1392        // try to guess when a declaration is not an external function definition
1393        if (strcmp(buf, ",")==0 ||
1394            strcmp(buf, "=")==0 ||
1395            strcmp(buf, "typedef")==0 ||
1396            strcmp(buf, "[")==0)
1397        {
1398            skipit(buf, f);
1399            goto again;
1400        }
1401
1402        if (strcmp(buf, "{}")==0) {
1403            if (!extern_c_seen) skipit(buf, f);
1404            goto again;
1405        }
1406
1407        if (strcmp(buf, "extern")==0) {
1408            if (getsym(buf, f)<0) {
1409                DEBUG_PRINT("EOF in getdecl loop\n");
1410                goto end;
1411            }
1412
1413            DEBUG_PRINT("test auf extern \"C\": '");
1414            DEBUG_PRINT(buf);
1415            DEBUG_PRINT("'\n");
1416
1417            if (strcmp(buf, "$") == 0) {            // symbol used if "C" was found
1418                extern_c_seen = 1;
1419                if (promote_extern_c) {
1420                    addword(wlist, "extern");
1421                    addword(wlist, "\"C\" ");
1422                    sawsomething = 1;
1423                }
1424                continue;
1425            }
1426
1427            skipit(buf, f);
1428            goto again;
1429        }
1430
1431        if (strncmp(buf, "__ATTR__", 8) == 0) { // prefix attribute (should only be used for static and inline)
1432            DEBUG_PRINT("seen prefix __ATTR__: '");
1433            DEBUG_PRINT(buf);
1434            DEBUG_PRINT("'\n");
1435
1436            seen__ATTR = true;
1437        }
1438
1439        if (strcmp(buf, "static") == 0 || strcmp(buf, "inline") == 0) {
1440            seen_static_or_inline = true;
1441            oktoprint = 0;
1442        }
1443
1444
1445        if (strcmp(buf, ";") == 0) goto again;
1446
1447        // A left parenthesis *might* indicate a function definition
1448        if (strcmp(buf, "(")==0) {
1449            startline   = linenum;
1450            Word *plist = NULL;
1451            if (!sawsomething || !(plist = getparamlist(f))) {
1452                skipit(buf, f);
1453                goto again;
1454            }
1455            if (plist == ABORTED)
1456                goto again;
1457
1458            // It seems to have been what we wanted
1459
1460            if (oktoprint) {                        // check function-name
1461                Word *w;
1462
1463                for (w=wlist; w->next && oktoprint; w=w->next) {
1464                    if (w->string[0]==':' && w->string[1]==0) oktoprint = 0; // do not emit prototypes for member functions
1465                }
1466
1467                if (oktoprint && !wantPrototypeFor(w->string)) {
1468                    oktoprint = 0;                  // => do not emit prototype
1469                }
1470            }
1471
1472            if (seen__ATTR && oktoprint) {
1473                DEBUG_PRINT("attempt to emit seen__ATTR (suppressed)");
1474                oktoprint = 0;
1475            }
1476
1477            if (oktoprint) {
1478                if (!header_printed) {
1479                    if (aisc) printf("\n# %s\n", header);
1480                    else printf("\n/* %s */\n", header);
1481                    header_printed = 1;
1482                }
1483                emit(wlist, plist, startline);
1484            }
1485            clear_found_attribute();
1486
1487            word_free(plist);
1488            goto again;
1489        }
1490
1491        addword(wlist, buf);
1492        sawsomething = 1;
1493    }
1494
1495  end:
1496    word_free(wlist);
1497}
1498
1499__ATTR__NORETURN static void Usage(const char *msg = NULL) {
1500    fprintf(stderr,
1501            "\naisc_mkpts - ARB prototype generator"
1502            "\nUsage: %s [options] [files ...]", ourname);
1503    fputs("\nSupported options:"
1504          "\n   -F part[,part]*  only promote declarations for functionnames containing one of the parts"
1505          "\n                    if 'part' starts with a '^' functionname has to start with rest of part"
1506          "\n   -S (like -F)     do NOT promote matching declarations (defaults to -S '^TEST_,^NOTEST_')"
1507          "\n"
1508          "\n                    Instead of ',' (=or) you may use '+' (=and)"
1509          "\n"
1510          "\n   -G               search for ARB macro __ATTR__ in comment behind function header"
1511          "\n   -P               promote /*AISC_MKPT_PROMOTE:forHeader*/ to header"
1512          "\n"
1513          "\n   -w gen.h         add standard include wrapper (needs name of generated header)"
1514          "\n   -c \"text\"        add text as comment into header"
1515          "\n"
1516          "\n   -C               insert 'extern \"C\"'"
1517          "\n   -E               prefix 'extern \"C\"' at prototype"
1518          "\n"
1519          "\n   -W               don't promote types in old style declarations"
1520          "\n   -x               omit parameter names in prototypes"
1521          "\n"
1522          "\n   -K               use K&R prototype macro (default assumes header files are strict ANSI)"
1523          "\n   -D               define K&R prototype macro"
1524          "\n   -p sym           use \"sym\" as the prototype macro (default \"P_\")"
1525          "\n"
1526          "\n   -m               promote declaration of 'main()' (default is to skip it)"
1527          "\n   -a               make a function list for aisc_includes (default: generate C prototypes)"
1528          "\n   -e               put an explicit \"extern\" keyword in declarations"
1529          "\n   -n               put line numbers of declarations as comments"
1530          "\n"
1531          "\n   -V               print version number"
1532          "\n   -h               print this help"
1533          "\n"
1534          , stderr);
1535    if (msg) fprintf(stderr, "\nError: %s", msg);
1536    fputc('\n', stderr);
1537    exit(EXIT_FAILURE);
1538}
1539
1540static int string_comparator(const void *v0, const void *v1) {
1541    return strcmp(*(const char **)v0, *(const char **)v1);
1542}
1543
1544static void MissingArgumentFor(char option) {
1545    char buffer[100];
1546    sprintf(buffer, "option -%c expects an argument", option);
1547    Usage(buffer);
1548}
1549static void UnknownOption(char option) {
1550    char buffer[100];
1551    sprintf(buffer, "unknown option -%c", option);
1552    Usage(buffer);
1553}
1554
1555int ARB_main(int argc, char *argv[]) {
1556    bool exit_if_noargs = false;
1557
1558    if (argv[0] && argv[0][0]) {
1559        ourname = strrchr(argv[0], '/');
1560        if (!ourname) ourname = argv[0];
1561    }
1562    else ourname = "mkptypes";
1563
1564    argv++; argc--;
1565
1566    addExcludedSymParts("^TEST_,^NOTEST_"); // exclude unit tests
1567
1568    char *iobuf = (char *)malloc(NEWBUFSIZ);
1569    while (*argv && **argv == '-') {
1570        const char *t = *argv++; --argc; t++;
1571        while (*t) {
1572            if (*t == 'e')      print_extern        = 1;
1573            else if (*t == 'C') cansibycplus        = 1;
1574            else if (*t == 'E') promote_extern_c    = 1;
1575            else if (*t == 'W') dont_promote        = 1;
1576            else if (*t == 'a') aisc                = 1;
1577            else if (*t == 'G') search__ATTR__      = 1;
1578            else if (*t == 'n') donum               = 1;
1579            else if (*t == 'x') no_parm_names       = 1; // no parm names, only types (sg)
1580            else if (*t == 'D') define_macro        = 1;
1581            else if (*t == 'K') use_macro           = 1;
1582            else if (*t == 'P') promote_lines       = 1;
1583            else if (*t == 'm') use_main            = 1;
1584            else if (*t == 'p') {
1585                t = *argv++; --argc;
1586                if (!t) MissingArgumentFor(*t);
1587                use_macro = 1;
1588                macro_name = t;
1589                break;
1590            }
1591            else if (*t == 'c') {
1592                t = *argv++; --argc;
1593                if (!t) MissingArgumentFor(*t);
1594                header_comment = t;
1595                break;
1596            }
1597            else if (*t == 'w') {
1598                t = *argv++; --argc;
1599                if (!t) MissingArgumentFor(*t);
1600                include_wrapper = t;
1601                break;
1602            }
1603            else if (*t == 'F') {
1604                t = *argv++; --argc;
1605                if (!t) MissingArgumentFor(*t);
1606                addRequiredSymParts(t);
1607                break;
1608            }
1609            else if (*t == 'S') {
1610                t = *argv++; --argc;
1611                if (!t) MissingArgumentFor(*t);
1612                freeExcludedSymParts();
1613                addExcludedSymParts(t);
1614                break;
1615            }
1616            else if (*t == 'V') {
1617                exit_if_noargs = true;
1618                Version();
1619            }
1620            else if (*t == 'h') Usage();
1621            else UnknownOption(*t);
1622            t++;
1623        }
1624    }
1625
1626    if (argc == 0 && exit_if_noargs) {
1627        exit(EXIT_FAILURE);
1628    }
1629
1630    char *include_macro = 0;
1631    if (aisc) {
1632        if (header_comment) {
1633            printf("# %s\n#\n", header_comment);
1634        }
1635        fputs("# This file is generated by aisc_mkpt.\n"
1636              "# Any changes you make here will be overwritten later!\n"
1637              "\n"
1638              "@FUNCTION_TYPE, @FUNCTION, @FUNCTION_REF;", stdout);
1639    }
1640    else {
1641        fputs("/*", stdout);
1642        if (header_comment) printf(" %s.\n *\n *", header_comment);
1643        fputs(" This file is generated by aisc_mkpt.\n"
1644              " * Any changes you make here will be overwritten later!\n"
1645              " */\n"
1646              "\n"
1647              , stdout);
1648
1649        if (include_wrapper) {
1650            int p;
1651            include_macro = strdup(include_wrapper);
1652            for (p = 0; include_macro[p]; p++) {
1653                char c           = include_macro[p];
1654                c                = strchr(".-", c) ? '_' : toupper(c);
1655                include_macro[p] = c;
1656            }
1657
1658            printf("#ifndef %s\n"
1659                   "#define %s\n"
1660                   "\n",
1661                   include_macro,
1662                   include_macro);
1663        }
1664
1665        if (use_macro) {
1666            if (define_macro) {
1667                fprintf(stdout,
1668                        "#ifndef %s\n"
1669                        "# if defined(__STDC__) || defined(__cplusplus)\n"
1670                        "#  define %s(s) s\n"
1671                        "# else\n"
1672                        "#  define %s(s) ()\n"
1673                        "# endif\n"
1674                        "#else\n"
1675                        "# error %s already defined elsewhere\n"
1676                        "#endif\n\n",
1677                        macro_name, macro_name, macro_name, macro_name);
1678            }
1679            else {
1680                fprintf(stdout,
1681                        "#ifndef %s\n"
1682                        "# error %s is not defined\n"
1683                        "#endif\n\n",
1684                        macro_name, macro_name);
1685            }
1686        }
1687        if (search__ATTR__) {
1688            fputs("/* define ARB attributes: */\n"
1689                  "#ifndef ATTRIBUTES_H\n"
1690                  "# include <attributes.h>\n"
1691                  "#endif\n\n", stdout);
1692        }
1693        if (cansibycplus) {
1694            fputs("#ifdef __cplusplus\n"
1695                  "extern \"C\" {\n"
1696                  "#endif\n\n", stdout);
1697        }
1698    }
1699
1700    current_dir = getcwd(0, 255);
1701    if (argc == 0) {
1702        getdecl(stdin, "<from stdin>");
1703    }
1704    else {
1705        const char *filename[1000];
1706        int         fcount = 0;
1707
1708        while (argc > 0 && *argv) {
1709            filename[fcount++] = *argv;
1710            argc--; argv++;
1711        }
1712
1713        qsort(&filename, fcount, sizeof(filename[0]), string_comparator);
1714
1715        for (int i = 0; i<fcount; ++i) {
1716            DEBUG_PRINT("trying new file '");
1717            DEBUG_PRINT(filename[i]);
1718            DEBUG_PRINT("'\n");
1719
1720            FILE *f = fopen(filename[i], "r");
1721            if (!f) {
1722                perror(filename[i]);
1723                exit(EXIT_FAILURE);
1724            }
1725            if (iobuf) setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ);
1726
1727            linenum      = 1;
1728            newline_seen = 1;
1729            glastc       = ' ';
1730            getdecl(f, filename[i]);
1731            fclose(f);
1732
1733            free(current_file);
1734            current_file = 0;
1735        }
1736    }
1737    if (aisc) {
1738    }
1739    else {
1740        if (cansibycplus) {
1741            fputs("\n#ifdef __cplusplus\n"
1742                  "}\n"
1743                  "#endif\n", stdout);
1744        }
1745        if (use_macro && define_macro) {
1746            printf("\n#undef %s\n", macro_name);    // clean up namespace
1747        }
1748
1749        if (include_wrapper) {
1750            printf("\n"
1751                   "#else\n"
1752                   "#error %s included twice\n"
1753                   "#endif /* %s */\n",
1754                   include_wrapper,
1755                   include_macro);
1756        }
1757    }
1758
1759    free(include_macro);
1760
1761    freeRequiredSymParts();
1762    freeExcludedSymParts();
1763
1764    free(current_file);
1765    free(current_dir);
1766
1767    free(iobuf);
1768
1769    return EXIT_SUCCESS;
1770}
1771
1772static void Version() {
1773    fprintf(stderr, "%s 1.1 ARB\n", ourname);
1774}
1775
1776// --------------------------------------------------------------------------------
1777
1778#ifdef UNIT_TESTS
1779
1780#include <test_unit.h>
1781
1782inline const char *test_extract(bool ATTR, const char *str) {
1783    search__ATTR__ = true;
1784
1785    clear_found_attribute();
1786
1787    strcpy(last_comment, str);
1788    lc_size = strlen(last_comment);
1789
1790    search_comment_for_attribute();
1791
1792    return found__ATTR__;
1793}
1794
1795#define TEST_ATTR_____(comment,extracted) TEST_EXPECT_EQUAL(test_extract(true, comment), extracted)
1796
1797void TEST_attribute_parser() {
1798    TEST_ATTR_____("",             (const char*)NULL);
1799    TEST_ATTR_____("nothing here", (const char*)NULL);
1800
1801    TEST_ATTR_____("bla bla __ATTR__DEPRECATED(\" my reason \") more content", "__ATTR__DEPRECATED(\" my reason \")");
1802    TEST_ATTR_____("bla bla __ATTR__FORMAT(pos) more content",                 "__ATTR__FORMAT(pos)");
1803   
1804    TEST_ATTR_____("__ATTR__DEPRECATED",       "__ATTR__DEPRECATED");
1805    TEST_ATTR_____("__ATTR__FORMAT(pos)",      "__ATTR__FORMAT(pos)");
1806    TEST_ATTR_____("bla __ATTR__FORMAT(pos)",  "__ATTR__FORMAT(pos)");
1807    TEST_ATTR_____("__ATTR__FORMAT(pos) bla",  "__ATTR__FORMAT(pos)");
1808    TEST_ATTR_____("    __ATTR__FORMAT(pos) ", "__ATTR__FORMAT(pos)");
1809   
1810    TEST_ATTR_____("__ATTR__FORMAT((pos)",           (const char*)NULL);
1811    TEST_ATTR_____("__attribute__(pos",              (const char*)NULL);
1812    TEST_ATTR_____("__ATTR__FORMAT(pos))",           "__ATTR__FORMAT(pos)");
1813    TEST_ATTR_____("__ATTR__FORMAT((pos))",          "__ATTR__FORMAT((pos))");
1814    TEST_ATTR_____("__ATTR__FORMAT((pos)+((sop)))",  "__ATTR__FORMAT((pos)+((sop)))");
1815    TEST_ATTR_____("__ATTR__FORMAT(((pos)))+(sop))", "__ATTR__FORMAT(((pos)))");
1816
1817    TEST_ATTR_____("bla bla __ATTR__DEPRECATED __ATTR__FORMAT(pos) more content", "__ATTR__DEPRECATED __ATTR__FORMAT(pos)");
1818    TEST_ATTR_____("bla __ATTR__DEPRECATED bla more__ATTR__FORMAT(pos)content", "__ATTR__DEPRECATED __ATTR__FORMAT(pos)");
1819   
1820    TEST_ATTR_____(" goes to header: __ATTR__NORETURN  */", "__ATTR__NORETURN");
1821
1822    clear_found_attribute();
1823}
1824TEST_PUBLISH(TEST_attribute_parser);
1825
1826#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.