ARB
mkptypes.cxx
Go to the documentation of this file.
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 
34 static 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 
56 static int donum = 0; // print line numbers?
57 static int define_macro = 0; // define macro for K&R prototypes?
58 static int use_macro = 0; // use a macro to support K&R prototypes?
59 static int use_main = 0; // add prototype for main?
60 static int no_parm_names = 0; // no parm names - only types
61 static int print_extern = 0; // use "extern" before function declarations
62 static int dont_promote = 0; // don't promote prototypes
63 static int promote_lines = 0; // promote 'AISC_MKPT_PROMOTE'-lines
64 static int aisc = 0; // aisc compatible output
65 static int cansibycplus = 0; // produce extern "C"
66 static int promote_extern_c = 0; // produce extern "C" into prototype
67 static int extern_c_seen = 0; // true if extern "C" was parsed
68 static int search__ATTR__ = 0; // search for ARB-__attribute__-macros (__ATTR__) ?
69 
70 static const char *include_wrapper = NULp; // add include wrapper (contains name of header or NULp)
71 
72 static int inquote = 0; // in a quote??
73 static int newline_seen = 1; // are we at the start of a line
74 static int glastc = ' '; // last char. seen by getsym()
75 
76 static char *current_file = NULp; // name of current file
77 static char *current_dir = NULp; // name of current directory
78 static const char *header_comment = NULp; // comment written into header
79 static long linenum = 1L; // line number in current file
80 
81 static char const *macro_name = "P_"; // macro to use for prototypes
82 static char const *ourname; // our name, from argv[] array
83 
84 // ------------------------------------------------------------
85 
86 static 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 
96 static void error(const char *msg) {
97  errorAt(linenum, msg);
98 }
99 
100 static void errorf(const char *format, ...) __attribute__((format(__printf__, 1, 2)));
101 static 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 
119 struct SymPart {
120  char *part;
121  int len; // len of part
122  bool atStart; // part has to match at start of name
123 
126 };
127 
128 static 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 
150 static 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 
165 static 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 
183 static 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 
200 static SymPart *requiredSymParts = NULp; // only create prototypes if function-name matches one of these parts
201 static SymPart *excludedSymParts = NULp; // DONT create prototypes if function-name matches one of these parts
202 
203 inline void addRequiredSymParts(const char *parts) { addSymParts(requiredSymParts, parts); }
204 inline void addExcludedSymParts(const char *parts) { addSymParts(excludedSymParts, parts); }
205 
206 inline void freeRequiredSymParts() { freeSymParts(requiredSymParts); }
207 inline void freeExcludedSymParts() { freeSymParts(excludedSymParts); }
208 
209 inline bool hasRequiredSymPart(const char *name) { return !requiredSymParts || matchesSymPart(requiredSymParts, name); }
210 inline bool hasExcludedSymPart(const char *name) { return excludedSymParts && matchesSymPart(excludedSymParts, name); }
211 
212 inline bool wantPrototypeFor(const char *name) {
213  return hasRequiredSymPart(name) && !hasExcludedSymPart(name);
214 }
215 
216 // ----------------------------------------
217 
218 struct Word {
220  char string[1];
221 };
222 
223 // Routines for manipulating lists of words.
224 
225 static 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 
238 static 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 
248 static 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 
260 static 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 
281 static 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 
295 static 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 
306 static 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 
334 static 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 
348 static int lc_size = 0;
349 static char *found__ATTR__ = NULp;
350 
351 static void clear_found_attribute() {
352  free(found__ATTR__);
353  found__ATTR__ = NULp;
354 }
355 
356 static const char *nextNonSpace(const char* ptr) {
357  while (isspace(*ptr)) ++ptr;
358  return ptr;
359 }
360 static const char *nextNonWord(const char* ptr) {
361  while (isalnum(*ptr) || *ptr == '_') ++ptr;
362  return ptr;
363 }
364 
365 inline 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 
381 class 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 
396 public:
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 
441 class 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 
459 public:
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 
485  const char *attrName;
486  size_t attrNameLen;
487  bool expandName; // try to expand 'attrName'
488  bool expectParens; // otherwise parens are optional
489 
490 public:
491  AttributeParser(const char *attrName_, bool expandName_, bool expectParens_)
492  : attrName(attrName_),
493  attrNameLen(strlen(attrName)),
494  expandName(expandName_),
495  expectParens(expectParens_)
496  {}
497 
498 private:
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 
534 public:
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 
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 
558 struct promotion {
559  char *to_promote; // text to promote to header
561 };
562 
564 
565 static 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 
581 static 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 
600 static const char *promotion_tag = "AISC_MKPT_PROMOTE:";
601 static int promotion_tag_len = 18;
602 
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 
647 static 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 
715 static 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 
802 static 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 
879 static 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 
898 static 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 
928 static 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 
949 static 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 
1164 inline 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 
1174 static 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 
1345 static 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  }
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 
1530 static 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 
1545 int 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);
1597  break;
1598  }
1599  else if (*t == 'S') {
1600  t = *argv++; --argc;
1601  if (!t) MissingArgumentFor(*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 
1753 
1754  free(current_file);
1755  free(current_dir);
1756 
1757  free(iobuf);
1758 
1759  return EXIT_SUCCESS;
1760 }
1761 
1762 static 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 
1772 inline const char *test_extract(const char *str) {
1773  search__ATTR__ = true;
1774 
1776 
1777  strcpy(last_comment, str);
1778  lc_size = strlen(last_comment);
1779 
1781 
1782  return found__ATTR__;
1783 }
1784 
1785 #define TEST_ATTR_____(comment,extracted) TEST_EXPECT_EQUAL(test_extract(comment), extracted)
1786 
1787 void 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 
1813 }
1814 TEST_PUBLISH(TEST_attribute_parser);
1815 
1816 #endif // UNIT_TESTS
static int List_len(Word *w)
Definition: mkptypes.cxx:248
static int use_macro
Definition: mkptypes.cxx:58
static long linenum
Definition: mkptypes.cxx:79
static int string_comparator(const void *v0, const void *v1)
Definition: mkptypes.cxx:1530
string result
promotion * next
Definition: mkptypes.cxx:560
void define(const char *start, const char *end)
Definition: mkptypes.cxx:411
static void addword(Word *w, const char *s)
Definition: mkptypes.cxx:295
AliDataPtr format(AliDataPtr data, const size_t wanted_len, GB_ERROR &error)
Definition: insdel.cxx:615
static int use_main
Definition: mkptypes.cxx:59
#define DEBUG_PRINT(s)
Definition: mkptypes.cxx:44
static char * current_file
Definition: mkptypes.cxx:76
AttributeParser(const char *attrName_, bool expandName_, bool expectParens_)
Definition: mkptypes.cxx:491
static void addSymParts(SymPart *&symParts, const char *parts)
Definition: mkptypes.cxx:150
#define ABORTED
Definition: mkptypes.cxx:51
static SymPart * requiredSymParts
Definition: mkptypes.cxx:200
static int cansibycplus
Definition: mkptypes.cxx:65
static char * found__ATTR__
Definition: mkptypes.cxx:349
char * part
Definition: mkptypes.cxx:120
#define NEWBUFSIZ
Definition: mkptypes.cxx:53
static int inquote
Definition: mkptypes.cxx:72
static SymPart * makeSymPart(char *token)
Definition: mkptypes.cxx:128
static int foundin(Word *w1, Word *w2)
Definition: mkptypes.cxx:281
static void search_comment_for_promotion()
Definition: mkptypes.cxx:603
static void emit(Word *wlist, Word *plist, long startline)
Definition: mkptypes.cxx:1174
#define MAXPARAM
Definition: mkptypes.cxx:52
SymPart * And
Definition: mkptypes.cxx:124
static int skipit(char *buf, FILE *f)
Definition: mkptypes.cxx:879
static int getsym(char *buf, FILE *f)
Definition: mkptypes.cxx:802
static SymPart * excludedSymParts
Definition: mkptypes.cxx:201
static void Version()
Definition: mkptypes.cxx:1762
static Word * word_append(Word *w1, Word *w2)
Definition: mkptypes.cxx:260
void addExcludedSymParts(const char *parts)
Definition: mkptypes.cxx:204
char * to_promote
Definition: mkptypes.cxx:559
#define EXIT_SUCCESS
Definition: arb_a2ps.c:154
static int dont_promote
Definition: mkptypes.cxx:62
static __ATTR__NORETURN void UnknownOption(char option)
Definition: mkptypes.cxx:1539
bool wantPrototypeFor(const char *name)
Definition: mkptypes.cxx:212
Word * getLastPtrRef(Word *w)
Definition: mkptypes.cxx:1164
char buffer[MESSAGE_BUFFERSIZE]
Definition: seq_search.cxx:34
static void getdecl(FILE *f, const char *header)
Definition: mkptypes.cxx:1345
SymPart * next
Definition: mkptypes.cxx:125
void freeRequiredSymParts()
Definition: mkptypes.cxx:206
static HelixNrInfo * start
size_t get_size() const
Definition: mkptypes.cxx:406
#define TEST_PUBLISH(testfunction)
Definition: test_unit.h:1517
static void add_promotion(char *to_promote)
Definition: mkptypes.cxx:565
#define PRINT(s)
Definition: mkptypes.cxx:48
static int search__ATTR__
Definition: mkptypes.cxx:68
const char * matchingParen(const char *from)
Definition: mkptypes.cxx:365
static const char * header_comment
Definition: mkptypes.cxx:78
void freeExcludedSymParts()
Definition: mkptypes.cxx:207
#define IS_PARM_NAME(w)
Definition: mkptypes.cxx:922
static Word * word_alloc(const char *s)
Definition: mkptypes.cxx:225
static char last_comment[MAX_COMMENT_SIZE]
Definition: mkptypes.cxx:347
bool is_empty() const
Definition: mkptypes.cxx:407
static const char * include_wrapper
Definition: mkptypes.cxx:70
static Word * typelist(Word *p)
Definition: mkptypes.cxx:928
int len
Definition: mkptypes.cxx:121
#define DEBUG_PRINT_STRING(name, str)
Definition: mkptypes.cxx:45
static int newline_seen
Definition: mkptypes.cxx:73
static int aisc
Definition: mkptypes.cxx:64
static char const * macro_name
Definition: mkptypes.cxx:81
static void print_promotions()
Definition: mkptypes.cxx:581
static char * current_dir
Definition: mkptypes.cxx:77
static void error(const char *msg)
Definition: mkptypes.cxx:96
fputc('\n', stderr)
bool hasRequiredSymPart(const char *name)
Definition: mkptypes.cxx:209
static int is_type_word(char *s)
Definition: mkptypes.cxx:898
bool hasExcludedSymPart(const char *name)
Definition: mkptypes.cxx:210
#define IS_CSYM(x)
Definition: mkptypes.cxx:50
static int glastc
Definition: mkptypes.cxx:74
static int donum
Definition: mkptypes.cxx:56
static __ATTR__NORETURN void Usage(const char *msg=NULp)
Definition: mkptypes.cxx:1489
static const char * nextNonWord(const char *ptr)
Definition: mkptypes.cxx:360
PartQueue(LinePart first_)
Definition: mkptypes.cxx:460
static Word * getparamlist(FILE *f)
Definition: mkptypes.cxx:949
void error(const char *format)
Definition: mkptypes.cxx:427
static void freeSymParts(SymPart *&symParts)
Definition: mkptypes.cxx:183
int ARB_main(int argc, char *argv[])
Definition: mkptypes.cxx:1545
static int no_parm_names
Definition: mkptypes.cxx:60
va_end(argPtr)
static const char * nextNonSpace(const char *ptr)
Definition: mkptypes.cxx:356
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
void undefine()
Definition: mkptypes.cxx:412
static int promotion_tag_len
Definition: mkptypes.cxx:601
static void typefixhack(Word *w)
Definition: mkptypes.cxx:306
bool atStart
Definition: mkptypes.cxx:122
fputs(TRACE_PREFIX, stderr)
static void cleanup()
Definition: arb_a2ps.c:916
static void errorf(const char *format,...) __attribute__((format(__printf__
Definition: mkptypes.cxx:101
const size_t BUFFERSIZE
char * to_string()
Definition: mkptypes.cxx:473
LinePart(const char *wholeLine, size_t len=-1U)
Definition: mkptypes.cxx:398
static void word_free(Word *&word)
Definition: mkptypes.cxx:238
static const char * promotion_tag
Definition: mkptypes.cxx:600
static char eol[3]
va_start(argPtr, format)
LinePart behind() const
Definition: mkptypes.cxx:420
static int define_macro
Definition: mkptypes.cxx:57
#define DUMMY_COUNTER_POS
static int line
Definition: arb_a2ps.c:296
#define NULp
Definition: cxxforward.h:116
char * parse(const char *toParse, size_t toParseSize) const
Definition: mkptypes.cxx:535
static char const * ourname
Definition: mkptypes.cxx:82
#define __ATTR__NORETURN
Definition: attributes.h:56
static int fnextch(FILE *f)
Definition: mkptypes.cxx:647
void copyTo(char *buffer) const
Definition: mkptypes.cxx:414
#define mp_assert(cond)
Definition: mkptypes.cxx:32
static int ngetc(FILE *f)
Definition: mkptypes.cxx:334
const char * whole_line() const
Definition: mkptypes.cxx:409
static promotion * promotions
Definition: mkptypes.cxx:563
static void clear_found_attribute()
Definition: mkptypes.cxx:351
void append(PartQueue *mp)
Definition: mkptypes.cxx:467
static int extern_c_seen
Definition: mkptypes.cxx:67
Word * next
Definition: mkptypes.cxx:219
static int promote_lines
Definition: mkptypes.cxx:63
static bool matchesSymPart(const SymPart *symParts, const char *name)
Definition: mkptypes.cxx:165
static void errorAt(long line, const char *msg)
Definition: mkptypes.cxx:86
static void search_comment_for_attribute()
Definition: mkptypes.cxx:547
void addRequiredSymParts(const char *parts)
Definition: mkptypes.cxx:203
static __ATTR__NORETURN void MissingArgumentFor(char option)
Definition: mkptypes.cxx:1534
static int lc_size
Definition: mkptypes.cxx:348
char string[1]
Definition: mkptypes.cxx:220
static int nextch(FILE *f)
Definition: mkptypes.cxx:715
static int print_extern
Definition: mkptypes.cxx:61
static Score ** U
Definition: align.cxx:67
struct PT_short_chain_header __attribute__
#define MAX_COMMENT_SIZE
Definition: mkptypes.cxx:345
static int promote_extern_c
Definition: mkptypes.cxx:66
GB_write_int const char s
Definition: AW_awar.cxx:154