source: branches/nameserver/AISC_MKPTPS/mkptypes.cxx

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