home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume27 / unproto / part01 / unproto.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-01-17  |  22.9 KB  |  850 lines

  1. /*++
  2. /* NAME
  3. /*    unproto 1
  4. /* SUMMARY
  5. /*    compile ANSI C with traditional UNIX C compiler
  6. /* PACKAGE
  7. /*    unproto
  8. /* SYNOPSIS
  9. /*    /somewhere/cpp ...
  10. /* DESCRIPTION
  11. /*    This document describes a filter that sits in between the UNIX
  12. /*    C preprocessor and the next UNIX C compiler stage, on the fly rewriting
  13. /*    ANSI-style syntax to old-style syntax. Typically, the program is
  14. /*    invoked by the native UNIX C compiler as an alternate preprocessor.
  15. /*    The unprototyper in turn invokes the native C preprocessor and
  16. /*    massages its output. Similar tricks can be used with the lint(1)
  17. /*    command.
  18. /*
  19. /*    Language constructs that are always rewritten:
  20. /* .TP
  21. /* function headings, prototypes, pointer types
  22. /*    ANSI-C style function headings, function prototypes, function
  23. /*    pointer types and type casts are rewritten to old style.
  24. /*    <stdarg.h> support is provided for functions with variable-length
  25. /*    argument lists.
  26. /* .TP
  27. /* character and string constants
  28. /*    The \\a and \\x escape sequences are rewritten to their (three-digit)
  29. /*    octal equivalents.
  30. /*
  31. /*    Multiple string tokens are concatenated; an arbitrary number of
  32. /*    whitespace or comment tokens may appear between successive
  33. /*    string tokens.
  34. /*
  35. /*    Within string constants, octal escape sequences are rewritten to the
  36. /*    three-digit \\ddd form, so that string concatenation produces correct
  37. /*    results.
  38. /* .TP
  39. /* date and time
  40. /*    The __DATE__ and __TIME__ tokens are replaced by string constants
  41. /*    of the form "Mmm dd yyyy" and "hh:mm:ss", respectively. The result
  42. /*    is subjected to string concatenation, just like any other string
  43. /*    constant.
  44. /* .PP
  45. /*    Language constructs that are rewritten only if the program has been
  46. /*    configured to do so:
  47. /* .TP
  48. /* void types
  49. /*    The unprototyper can be configured to rewrite "void *" to "char *",
  50. /*    and even to rewrite plain "void" to "int".
  51. /*    These features are configurable because many traditional UNIX C
  52. /*    compilers do not need them.
  53. /*
  54. /*    Note: (void) argument lists are always replaced by empty ones.
  55. /* .PP
  56. /*    ANSI C constructs that are not rewritten because the traditional
  57. /*    UNIX C preprocessor provides suitable workarounds:
  58. /* .TP
  59. /* const and volatile
  60. /*    Use the "-Dconst=" and/or "-Dvolatile=" preprocessor directives to
  61. /*    get rid of unimplemented keywords.
  62. /* .TP
  63. /* token pasting and stringizing
  64. /*    The traditional UNIX C preprocessor provides excellent alternatives.
  65. /*    For example:
  66. /*
  67. /* .nf
  68. /* .ne 2
  69. /*    #define    string(bar)    "bar"        /* instead of: # x */
  70. /*    #define    paste(x,y)    x/**\/y        /* instead of: x##y */
  71. /* .fi
  72. /*
  73. /*    There is a good reason why the # and ## operators are not implemented
  74. /*    in the unprototyper.
  75. /*    After program text has gone through a non-ANSI C preprocessor, all
  76. /*    information about the grouping of the operands of # and ## is lost.
  77. /*    Thus, if the unprototyper were to perform these operations, it would
  78. /*    produce correct results only in the most trivial cases. Operands
  79. /*    with embedded blanks, operands that expand to null tokens, and nested
  80. /*    use of # and/or ## would cause all kinds of obscure problems.
  81. /* .PP
  82. /*    Unsupported ANSI features:
  83. /* .TP
  84. /* trigraphs and #pragmas
  85. /*    Trigraphs are useful only for systems with broken character sets.
  86. /*    If the local compiler chokes on #pragma, insert a blank before the
  87. /*    "#" character, and enclose the offending directive between #ifdef
  88. /*    and #endif.
  89. /* SEE ALSO
  90. /* .ad
  91. /* .fi
  92. /*    cc(1), how to specify a non-default C preprocessor.
  93. /*    Some versions of the lint(1) command are implemented as a shell
  94. /*    script. It should require only minor modification for integration
  95. /*    with the unprototyper. Other versions of the lint(1) command accept
  96. /*    the same command syntax as the C compiler for the specification of a
  97. /*    non-default preprocessor. Some research may be needed.
  98. /* FILES
  99. /*    /wherever/stdarg.h, provided with the unproto filter.
  100. /* DIAGNOSTICS
  101. /*    Problems are reported on the standard error stream.
  102. /*    A non-zero exit status means that there was a problem.
  103. /* BUGS
  104. /*    The unprototyper should be run on preprocessed source only:
  105. /*    unexpanded macros may confuse the program.
  106. /*
  107. /*    Declarations of (object) are misunderstood and will result in
  108. /*    syntax errors: the objects between parentheses disappear.
  109. /*
  110. /*    Sometimes does not preserve whitespace after parentheses and commas.
  111. /*    This is a purely aesthetical matter, and the compiler should not care.
  112. /*    Whitespace within string constants is, of course, left intact.
  113. /*
  114. /*    Does not generate explicit type casts for function-argument
  115. /*    expressions.  The lack of explicit conversions between integral
  116. /*    and/or pointer argument types should not be a problem in environments
  117. /*    where sizeof(int) == sizeof(long) == sizeof(pointer).  A more serious
  118. /*    problem is the lack of automatic type conversions between integral and
  119. /*    floating-point argument types.  Let lint(1) be your friend.
  120. /* AUTHOR(S)
  121. /*    Wietse Venema (wietse@wzv.win.tue.nl)
  122. /*    Eindhoven University of Technology
  123. /*    Department of Mathematics and Computer Science
  124. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  125. /* LAST MODIFICATION
  126. /*    92/01/15 21:52:56
  127. /* VERSION/RELEASE
  128. /*    1.4
  129. /*--*/
  130.  
  131. static char unproto_sccsid[] = "@(#) unproto.c 1.4 92/01/15 21:52:56";
  132.  
  133. /* C library */
  134.  
  135. #include <sys/types.h>
  136. #include <sys/stat.h>
  137. #include <stdio.h>
  138. #include <errno.h>
  139.  
  140. extern void exit();
  141. extern int optind;
  142. extern char *optarg;
  143. extern int getopt();
  144.  
  145. /* Application-specific stuff */
  146.  
  147. #include "vstring.h"
  148. #include "stdarg.h"
  149. #include "token.h"
  150. #include "error.h"
  151. #include "symbol.h"
  152.  
  153. /* Forward declarations. */
  154.  
  155. static struct token *dcl_flush();
  156. static void block_flush();
  157. static void block_dcls();
  158. static struct token *show_func_ptr_type();
  159. static struct token *show_struct_type();
  160. static void show_arg_name();
  161. static void show_type();
  162. static void pair_flush();
  163. static void check_cast();
  164. static void show_empty_list();
  165.  
  166. #define    check_cast_flush(t)    (check_cast(t), tok_free(t))
  167.  
  168. #ifdef PIPE_THROUGH_CPP
  169. static int pipe_stdin_through_cpp();
  170. #endif
  171.  
  172. /* Disable debugging printfs while preserving side effects. */
  173.  
  174. #ifdef DEBUG
  175. #define    DPRINTF    printf
  176. #else
  177. #define    DPRINTF (void)
  178. #endif
  179.  
  180. /* An attempt to make some complicated expressions a bit more readable. */
  181.  
  182. #define    STREQ(x,y)        (*(x) == *(y) && !strcmp((x),(y)))
  183.  
  184. #define    LAST_ARG_AND_EQUAL(s,c)    ((s)->next && (s)->next->next == 0 \
  185.                 && (s)->head && ((s)->head == (s)->tail) \
  186.                 && (STREQ((s)->head->vstr->str, (c))))
  187.  
  188. #define    LIST_BEGINS_WITH_STAR(s) (s->head->head && s->head->head->tokno == '*')
  189.  
  190. #define    IS_FUNC_PTR_TYPE(s)    (s->tokno == TOK_LIST && s->next \
  191.                 && s->next->tokno == TOK_LIST \
  192.                 && LIST_BEGINS_WITH_STAR(s))
  193.  
  194. /* What to look for to detect a (void) argument list. */
  195.  
  196. #ifdef MAP_VOID
  197. #define    VOID_ARG    "int"        /* bare "void" is mapped to "int" */
  198. #else
  199. #define    VOID_ARG    "void"        /* bare "void" is left alone */
  200. #endif
  201.  
  202. /* main - driver */
  203.  
  204. int     main(argc, argv)
  205. int     argc;
  206. char  **argv;
  207. {
  208.     register struct token *t;
  209. #ifdef    PIPE_THROUGH_CPP            /* pipe through /lib/cpp */
  210.     int     cpp_status;
  211.     int     wait_pid;
  212.     int     cpp_pid;
  213.  
  214.     cpp_pid = pipe_stdin_through_cpp(argv);
  215. #endif
  216.  
  217.     sym_init();                    /* prime the symbol table */
  218.  
  219.     while (t = tok_class()) {
  220.     if (t = dcl_flush(t)) {            /* try declaration */
  221.         if (t->tokno == '{') {        /* examine rejected token */
  222.         block_flush(t);            /* body */
  223.         } else {
  224.         tok_flush(t);            /* other, recover */
  225.         }
  226.     }
  227.     }
  228.  
  229. #ifdef    PIPE_THROUGH_CPP            /* pipe through /lib/cpp */
  230.     while ((wait_pid = wait(&cpp_status)) != -1 && wait_pid != cpp_pid)
  231.      /* void */ ;
  232.     return (errcount != 0 || wait_pid != cpp_pid || cpp_status != 0);
  233. #else
  234.     return (errcount != 0);
  235. #endif
  236. }
  237.  
  238. #ifdef    PIPE_THROUGH_CPP        /* pipe through /lib/cpp */
  239.  
  240. /* pipe_stdin_through_cpp - avoid shell script overhead */
  241.  
  242. static int pipe_stdin_through_cpp(argv)
  243. char  **argv;
  244. {
  245.     int     pipefds[2];
  246.     int     pid;
  247.     char  **cpptr = argv;
  248.     int     i;
  249.     struct stat st;
  250.  
  251.     /*
  252.      * The code that sets up the pipe requires that file descriptors 0,1,2
  253.      * are already open. All kinds of mysterious things will happen if that
  254.      * is not the case. The following loops makes sure that descriptors 0,1,2
  255.      * are set up properly. 
  256.      */
  257.  
  258.     for (i = 0; i < 3; i++) {
  259.     if (fstat(i, &st) == -1 && open("/dev/null", 2) != i) {
  260.         perror("open /dev/null");
  261.         exit(1);
  262.     }
  263.     }
  264.  
  265.     /*
  266.      * With most UNIX implementations, the second non-option argument to
  267.      * /lib/cpp specifies the output file. If an output file other than
  268.      * stdout is specified, we must force /lib/cpp to write to stdout, and we
  269.      * must redirect our own standard output to the specified output file.
  270.      */
  271.  
  272. #define    IS_OPTION(cp) ((cp)[0] == '-' && (cp)[1] != 0)
  273.  
  274.     /* Skip to first non-option argument, if any. */
  275.  
  276.     while (*++cpptr && IS_OPTION(*cpptr))
  277.      /* void */ ;
  278.  
  279.     /*
  280.      * Assume that the first non-option argument is the input file name. The
  281.      * next argument could be the output destination or an option (System V
  282.      * Release 2 /lib/cpp gets the options *after* the file arguments).
  283.      */
  284.  
  285.     if (*cpptr && *++cpptr && **cpptr != '-') {
  286.  
  287.     /*
  288.      * The first non-option argument is followed by another argument that
  289.      * is not an option ("-stuff") or a hyphen ("-"). Redirect our own
  290.      * standard output before we clobber the file name.
  291.      */
  292.  
  293.     if (freopen(*cpptr, "w", stdout) == 0) {
  294.         perror(*cpptr);
  295.         exit(1);
  296.     }
  297.     /* Clobber the file name argument so that /lib/cpp writes to stdout */
  298.  
  299.     *cpptr = "-";
  300.     }
  301.     /* Set up the pipe that connects /lib/cpp to our standard input. */
  302.  
  303.     if (pipe(pipefds)) {
  304.     perror("pipe");
  305.     exit(1);
  306.     }
  307.     switch (pid = fork()) {
  308.     case -1:                    /* error */
  309.     perror("fork");
  310.     exit(1);
  311.     /* NOTREACHED */
  312.     case 0:                    /* child */
  313.     (void) close(pipefds[0]);        /* close reading end */
  314.     (void) close(1);            /* connect stdout to pipe */
  315.     if (dup(pipefds[1]) != 1)
  316.         fatal("dup() problem");
  317.     (void) close(pipefds[1]);        /* close redundant fd */
  318.     (void) execv(PIPE_THROUGH_CPP, argv);
  319.     perror(PIPE_THROUGH_CPP);
  320.     exit(1);
  321.     /* NOTREACHED */
  322.     default:                    /* parent */
  323.     (void) close(pipefds[1]);        /* close writing end */
  324.     (void) close(0);            /* connect stdin to pipe */
  325.     if (dup(pipefds[0]) != 0)
  326.         fatal("dup() problem");
  327.     close(pipefds[0]);            /* close redundant fd */
  328.     return (pid);
  329.     }
  330. }
  331.  
  332. #endif
  333.  
  334. /* header_flush - rewrite new-style function header to old style */
  335.  
  336. static void header_flush(t)
  337. register struct token *t;
  338. {
  339.     register struct token *s;
  340.  
  341.     /* Do argument names, but suppress void and rewrite trailing ... */
  342.  
  343.     if (LAST_ARG_AND_EQUAL(t->head, VOID_ARG)) {
  344.     show_empty_list(t);            /* no arguments */
  345.     } else {
  346.     for (s = t->head; s; s = s->next) {    /* foreach argument... */
  347.         if (LAST_ARG_AND_EQUAL(s, "...")) {
  348. #ifdef _VA_ALIST_                /* see ./stdarg.h */
  349.         tok_show_ch(s);            /* ',' */
  350.         put_str(_VA_ALIST_);        /* varargs magic */
  351. #endif
  352.         } else {
  353.         tok_show_ch(s);            /* '(' or ',' or ')' */
  354.         show_arg_name(s);        /* extract argument name */
  355.         }
  356.     }
  357.     }
  358.     put_nl();                    /* make output look nicer */
  359.  
  360.     /* Do argument types, but suppress void and trailing ... */
  361.  
  362.     if (!LAST_ARG_AND_EQUAL(t->head, VOID_ARG)) {
  363.     for (s = t->head; s; s = s->next) {    /* foreach argument... */
  364.         if (!LAST_ARG_AND_EQUAL(s, "...")) {
  365.         if (s->head != s->tail) {    /* really new-style argument? */
  366.             show_type(s);        /* rewrite type info */
  367.             put_ch(';');
  368.             put_nl();            /* make output look nicer */
  369.         }
  370.         }
  371.     }
  372.     }
  373.     tok_free(t);
  374. }
  375.  
  376. /* show_arg_name - extract argument name from argument type info */
  377.  
  378. static void show_arg_name(s)
  379. register struct token *s;
  380. {
  381.     if (s->head) {
  382.     register struct token *p;
  383.     register struct token *t = 0;
  384.  
  385.     /* Find the last interesting item. */
  386.  
  387.     for (p = s->head; p; p = p->next) {
  388.         if (p->tokno == TOK_WORD) {
  389.         t = p;                /* remember last word */
  390.         } else if (p->tokno == '[') {
  391.         break;                /* dimension may be a macro */
  392.         } else if (IS_FUNC_PTR_TYPE(p)) {
  393.         t = p;                /* or function pointer */
  394.         p = p->next;
  395.         }
  396.     }
  397.  
  398.     /* Extract argument name from last interesting item. */
  399.  
  400.     if (t) {
  401.         if (t->tokno == TOK_LIST)
  402.         show_arg_name(t->head);        /* function pointer, recurse */
  403.         else
  404.         tok_show(t);            /* print last word */
  405.     }
  406.     }
  407. }
  408.  
  409. /* show_type - rewrite type to old-style syntax */
  410.  
  411. static void show_type(s)
  412. register struct token *s;
  413. {
  414.     register struct token *p;
  415.  
  416.     for (p = s->head; p; p = p->next) {
  417.     if (IS_FUNC_PTR_TYPE(p)) {
  418.         p = show_func_ptr_type(p, p->next);    /* function pointer type */
  419.     } else {
  420.         tok_show(p);            /* other */
  421.     }
  422.     }
  423. }
  424.  
  425. /* show_func_ptr_type - display function_pointer type using old-style syntax */
  426.  
  427. static struct token *show_func_ptr_type(t1, t2)
  428. struct token *t1;
  429. struct token *t2;
  430. {
  431.     register struct token *s;
  432.  
  433.     /*
  434.      * Rewrite (list1) (list2) to (list1) (). Account for the rare case that
  435.      * (list1) is a comma-separated list. That should be an error, but we do
  436.      * not want to waste any information.
  437.      */
  438.  
  439.     for (s = t1->head; s; s = s->next) {
  440.     tok_show_ch(s);                /* '(' or ',' or ')' */
  441.     show_type(s);                /* recurse */
  442.     }
  443.     show_empty_list(t2);
  444.     return (t2);
  445. }
  446.  
  447. /* show_empty_list - display opening and closing parentheses (if available) */
  448.  
  449. static void show_empty_list(t)
  450. register struct token *t;
  451. {
  452.     tok_show_ch(t->head);            /* opening paren */
  453.     if (t->tail->tokno == ')')
  454.     tok_show_ch(t->tail);            /* closing paren */
  455. }
  456.  
  457. /* show_struct_type - display structured type, rewrite function-pointer types */
  458.  
  459. static struct token *show_struct_type(p)
  460. register struct token *p;
  461. {
  462.     tok_show(p);                /* opening brace */
  463.  
  464.     while (p->next) {                /* XXX cannot return 0 */
  465.     p = p->next;
  466.     if (IS_FUNC_PTR_TYPE(p)) {
  467.         p = show_func_ptr_type(p, p->next);    /* function-pointer member */
  468.     } else if (p->tokno == '{') {
  469.         p = show_struct_type(p);        /* recurse */
  470.     } else {
  471.         tok_show(p);            /* other */
  472.         if (p->tokno == '}') {
  473.         return (p);            /* done */
  474.         }
  475.     }
  476.     }
  477.     DPRINTF("/* missing '}' */");
  478.     return (p);
  479. }
  480.  
  481. /* is_func_ptr_cast - recognize function-pointer type cast */
  482.  
  483. static int is_func_ptr_cast(t)
  484. register struct token *t;
  485. {
  486.     register struct token *p;
  487.  
  488.     /*
  489.      * Examine superficial structure. Require (list1) (list2). Require that
  490.      * list1 begins with a star.
  491.      */
  492.  
  493.     if (!IS_FUNC_PTR_TYPE(t))
  494.     return (0);
  495.  
  496.     /*
  497.      * Make sure that there is no name in (list1). Do not worry about
  498.      * unexpected tokens, because the compiler will complain anyway.
  499.      */
  500.  
  501.     for (p = t->head->head; p; p = p->next) {
  502.     switch (p->tokno) {
  503.     case TOK_LIST:                /* recurse */
  504.         return (is_func_ptr_cast(p));
  505.     case TOK_WORD:                /* name in list */
  506.         return (0);
  507.     case '[':
  508.         return (1);                /* dimension may be a macro */
  509.     }
  510.     }
  511.     return (1);                    /* no name found */
  512. }
  513.  
  514. /* check_cast - display ()-delimited, comma-separated list */
  515.  
  516. static void check_cast(t)
  517. struct token *t;
  518. {
  519.     register struct token *s;
  520.     register struct token *p;
  521.  
  522.     /*
  523.      * Rewrite function-pointer types and function-pointer casts. Do not
  524.      * blindly rewrite (*list1)(list2) to (*list1)(). Function argument lists
  525.      * are about the only thing we can discard without provoking diagnostics
  526.      * from the compiler.
  527.      */
  528.  
  529.     for (s = t->head; s; s = s->next) {
  530.     tok_show_ch(s);                /* '(' or ',' or ')' */
  531.     for (p = s->head; p; p = p->next) {
  532.         switch (p->tokno) {
  533.         case TOK_LIST:
  534.         if (is_func_ptr_cast(p)) {    /* not: IS_FUNC_PTR_TYPE(p) */
  535.             p = show_func_ptr_type(p, p->next);
  536.         } else {
  537.             check_cast(p);        /* recurse */
  538.         }
  539.         break;
  540.         case '{':
  541.         p = show_struct_type(p);    /* rewrite func. ptr. types */
  542.         break;
  543.         default:
  544.         tok_show(p);
  545.         break;
  546.         }
  547.     }
  548.     }
  549. }
  550.  
  551. /* block_dcls - on the fly rewrite decls/initializers at start of block */
  552.  
  553. static void block_dcls()
  554. {
  555.     register struct token *t;
  556.  
  557.     /*
  558.      * Away from the top level, a declaration should be preceded by type or
  559.      * storage-class information. That is why inside blocks, structs and
  560.      * unions we insist on reading one word before passing the _next_ token
  561.      * to the dcl_flush() function.
  562.      * 
  563.      * Struct and union declarations look the same everywhere: we make an
  564.      * exception for these more regular constructs and pass the "struct" and
  565.      * "union" tokens to the type_dcl() function.
  566.      */
  567.  
  568.     while (t = tok_class()) {
  569.     switch (t->tokno) {
  570.     case TOK_WSPACE:            /* preserve white space */
  571.     case '\n':                /* preserve line count */
  572.         tok_flush(t);
  573.         break;
  574.     case TOK_WORD:                /* type declarations? */
  575.         tok_flush(t);            /* advance to next token */
  576.         t = tok_class();            /* null return is ok */
  577.         /* FALLTRHOUGH */
  578.     case TOK_COMPOSITE:            /* struct or union */
  579.         if ((t = dcl_flush(t)) == 0)
  580.         break;
  581.         /* FALLTRHOUGH */
  582.     default:                /* end of declarations */
  583.         DPRINTF("/* end dcls */");
  584.         /* FALLTRHOUGH */
  585.     case '}':                /* end of block */
  586.         tok_unget(t);
  587.         return;
  588.     }
  589.     }
  590. }
  591.  
  592. /* block_flush - rewrite struct, union or statement block on the fly */
  593.  
  594. static void block_flush(t)
  595. register struct token *t;
  596. {
  597.     static int count = 0;
  598.  
  599.     tok_flush(t);
  600.     DPRINTF("/*%d*/", ++count);
  601.  
  602.     /*
  603.      * Rewrite function pointer types in declarations and function pointer
  604.      * casts in initializers at start of block.
  605.      */
  606.  
  607.     block_dcls();
  608.  
  609.     /* Remainder of block: only rewrite function pointer casts. */
  610.  
  611.     while (t = tok_class()) {
  612.     if (t->tokno == TOK_LIST) {
  613.         check_cast_flush(t);
  614.     } else if (t->tokno == '{') {
  615.         block_flush(t);
  616.     } else {
  617.         tok_flush(t);
  618.         if (t->tokno == '}') {
  619.         DPRINTF("/*%d*/", count--);
  620.         return;
  621.         }
  622.     }
  623.     }
  624.     DPRINTF("/* missing '}' */");
  625. }
  626.  
  627. /* pair_flush - on the fly rewrite casts in grouped stuff */
  628.  
  629. static void pair_flush(t, start, stop)
  630. register struct token *t;
  631. register int start;
  632. register int stop;
  633. {
  634.     tok_flush(t);
  635.  
  636.     while (t = tok_class()) {
  637.     if (t->tokno == start) {        /* recurse */
  638.         pair_flush(t, start, stop);
  639.     } else if (t->tokno == TOK_LIST) {    /* expression or cast */
  640.         check_cast_flush(t);
  641.     } else {                /* other, copy */
  642.         tok_flush(t);
  643.         if (t->tokno == stop) {        /* done */
  644.         return;
  645.         }
  646.     }
  647.     }
  648.     DPRINTF("/* missing '%c' */", stop);
  649. }
  650.  
  651. /* initializer - on the fly rewrite casts in initializer */
  652.  
  653. static void initializer()
  654. {
  655.     register struct token *t;
  656.  
  657.     while (t = tok_class()) {
  658.     switch (t->tokno) {
  659.     case ',':                /* list separator */
  660.     case ';':                /* list terminator */
  661.         tok_unget(t);
  662.         return;
  663.     case TOK_LIST:                /* expression or cast */
  664.         check_cast_flush(t);
  665.         break;
  666.     case '[':                /* array subscript, may nest */
  667.         pair_flush(t, '[', ']');
  668.         break;
  669.     case '{':                /* structured data, may nest */
  670.         pair_flush(t, '{', '}');
  671.         break;
  672.     default:                /* other, just copy */
  673.         tok_flush(t);
  674.         break;
  675.     }
  676.     }
  677. }
  678.  
  679. /* func_ptr_dcl_flush - rewrite function pointer declaration */
  680.  
  681. static struct token *func_ptr_dcl_flush(list)
  682. register struct token *list;
  683. {
  684.     register struct token *t;
  685.  
  686.     /*
  687.      * Ignore blanks and newlines because we are too lazy to maintain more
  688.      * than one token worth of lookahead. The output routines will regenerate
  689.      * discarded newline tokens.
  690.      */
  691.  
  692.     while (t = tok_class()) {
  693.     switch (t->tokno) {
  694.     case TOK_WSPACE:
  695.     case '\n':
  696.         tok_free(t);
  697.         break;
  698.     case TOK_LIST:
  699.         /* Function pointer type: (list1) (list2) -> (list1) () */
  700.         (void) show_func_ptr_type(list, t);    /* may be recursive */
  701.         tok_free(list);
  702.         tok_free(t);
  703.         return (0);
  704.     default:                /* not a declaration */
  705.         tok_unget(t);
  706.         return (list);
  707.     }
  708.     }
  709.  
  710.     /* Hit EOF; must be mistake, but do not waste any information. */
  711.  
  712.     return (list);
  713. }
  714.  
  715. /* function_dcl_flush - rewrite function { heading, type declaration } */
  716.  
  717. static struct token *function_dcl_flush(list)
  718. register struct token *list;
  719. {
  720.     register struct token *t;
  721.  
  722.     /*
  723.      * Ignore blanks and newlines because we are too lazy to maintain more
  724.      * than one token worth of lookahead. The output routines will regenerate
  725.      * ignored newline tokens.
  726.      */
  727.  
  728.     while (t = tok_class()) {
  729.     switch (t->tokno) {
  730.     case TOK_WSPACE:
  731.     case '\n':
  732.         tok_free(t);
  733.         break;
  734.     case '{':
  735.         /* Function heading: word (list) { -> old style heading */
  736.         header_flush(list);
  737.         tok_unget(t);
  738.         return (0);
  739.     case TOK_WORD:
  740.         /* Old-style function heading: word (list) word... */
  741.         tok_flush(list);
  742.         tok_unget(t);
  743.         return (0);
  744.     case TOK_LIST:
  745.         /* Function pointer: word (list1) (list2) -> word (list1) () */
  746.         tok_flush(list);
  747.         show_empty_list(t);
  748.         tok_free(t);
  749.         return (0);
  750.     case ',':
  751.     case ';':
  752.         /* Function type declaration: word (list) -> word () */
  753.         show_empty_list(list);
  754.         tok_free(list);
  755.         tok_unget(t);
  756.         return (0);
  757.     default:
  758.         /* Something else, reject the list. */
  759.         tok_unget(t);
  760.         return (list);
  761.     }
  762.     }
  763.  
  764.     /* Hit EOF; must be mistake, but do not waste any information. */
  765.  
  766.     return (list);
  767. }
  768.  
  769. /* dcl_flush - parse declaration on the fly, return rejected token */
  770.  
  771. static struct token *dcl_flush(t)
  772. register struct token *t;
  773. {
  774.     register int got_word;
  775.  
  776.     /*
  777.      * Away from the top level, type or storage-class information is required
  778.      * for an (extern or forward) function type declaration or a variable
  779.      * declaration.
  780.      * 
  781.      * With our naive word-counting approach, this means that the caller should
  782.      * read one word before passing the next token to us. This is how we
  783.      * distinguish, for example, function declarations from function calls.
  784.      * 
  785.      * An exception are structs and unions, because they look the same at any
  786.      * level. The caller should give is the "struct" or "union" token.
  787.      */
  788.  
  789.     for (got_word = 0; t; t = tok_class()) {
  790.     switch (t->tokno) {
  791.     case TOK_WSPACE:            /* advance past blanks */
  792.     case '\n':                /* advance past newline */
  793.     case '*':                /* indirection: keep trying */
  794.         tok_flush(t);
  795.         break;
  796.     case TOK_WORD:                /* word: keep trying */
  797.     case TOK_COMPOSITE:            /* struct or union */
  798.         got_word = 1;
  799.         tok_flush(t);
  800.         break;
  801.     default:
  802.  
  803.         /*
  804.          * Function pointer types can be preceded by zero or more words
  805.          * (at least one when not at the top level). Other stuff can be
  806.          * accepted only after we have seen at least one word (two words
  807.          * when not at the top level). See also the above comment on
  808.          * structs and unions.
  809.          */
  810.  
  811.         if (t->tokno == TOK_LIST && LIST_BEGINS_WITH_STAR(t)) {
  812.         if (t = func_ptr_dcl_flush(t)) {
  813.             return (t);            /* reject token */
  814.         } else {
  815.             got_word = 1;        /* for = and [ and , and ; */
  816.         }
  817.         } else if (got_word == 0) {
  818.         return (t);            /* reject token */
  819.         } else {
  820.         switch (t->tokno) {
  821.         case TOK_LIST:            /* function type */
  822.             if (t = function_dcl_flush(t))
  823.             return (t);        /* reject token */
  824.             break;
  825.         case '[':            /* dimension, does not nest */
  826.             pair_flush(t, '[', ']');
  827.             break;
  828.         case '=':            /* initializer follows */
  829.             tok_flush(t);
  830.             initializer();        /* rewrite casts */
  831.             break;
  832.         case '{':            /* struct, union, may nest */
  833.             block_flush(t);        /* use code for stmt blocks */
  834.             break;
  835.         case ',':            /* separator: keep trying */
  836.             got_word = 0;
  837.             tok_flush(t);
  838.             break;
  839.         case ';':            /* terminator: succeed */
  840.             tok_flush(t);
  841.             return (0);
  842.         default:            /* reject token */
  843.             return (t);
  844.         }
  845.         }
  846.     }
  847.     }
  848.     return (0);                    /* hit EOF */
  849. }
  850.