home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume26 / unproto / part02 / unproto.c
Encoding:
C/C++ Source or Header  |  1991-12-07  |  19.0 KB  |  737 lines

  1. /*++
  2. /* NAME
  3. /*    unproto 1
  4. /* SUMMARY
  5. /*    ANSI C to old C converter
  6. /* PACKAGE
  7. /*    unproto
  8. /* SYNOPSIS
  9. /*    /lib/cpp ... | unproto
  10. /*
  11. /*    /somewhere/cpp ...
  12. /* DESCRIPTION
  13. /*    This document describes a filter that sits between the
  14. /*    C preprocessor (usually \fI/lib/cpp\fP) and the next C compiler
  15. /*    pass. It rewrites ANSI-C style function headers, function type
  16. /*    declarations, function pointer types, and function pointer casts
  17. /*    to old style. Other ANSI-isms are passed on without modification
  18. /*    (token pasting, pragmas, etcetera).
  19. /*
  20. /*    For maximal flexibility, the "cpp | unproto" pipeline can  be
  21. /*    packaged as an executable shell script named "/somewhere/cpp".
  22. /*    This script should then be specified to the C compiler as a 
  23. /*    non-default preprocessor. It will not work if your C compiler
  24. /*    specifies output file names to the preprocessor.
  25. /*
  26. /*    The overhead of shell script interpretation can be avoided by
  27. /*    having the unprototyper itself open the pipe to the preprocessor.
  28. /*    In that case, the source should be compiled with the PIPE_THROUGH_CPP 
  29. /*    macro defined (usually as "/lib/cpp"), and the resulting binary 
  30. /*    should be installed as "/somewhere/cpp".
  31. /* SEE ALSO
  32. /* .ad
  33. /* .fi
  34. /*    cc(1), how to specify a non-default C preprocessor.
  35. /*
  36. /*    Some versions of the lint command are implemented as a shell
  37. /*    script. It should require only minor modification for integration
  38. /*    with the unprotoizer. Other versions of the lint command accept the same
  39. /*    command syntax as the C compiler for the specification of a non-default
  40. /*    preprocessor. Some research may be needed.
  41. /* DIAGNOSTICS
  42. /*    The progam will complain if it unexpectedly
  43. /*    reaches the end of input.
  44. /* BUGS
  45. /*    Should be run on preprocessed source only, i.e. after macro expansion.
  46. /*
  47. /*    Declarations of (whatever) are misunderstood and will result in
  48. /*    syntax errors.
  49. /*
  50. /*    Does not generate explicit type casts for function argument 
  51. /*    expressions.
  52. /* AUTHOR(S)
  53. /*    Wietse Venema (wietse@wzv.win.tue.nl)
  54. /*    Eindhoven University of Technology
  55. /*    Department of Mathematics and Computer Science
  56. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  57. /* LAST MODIFICATION
  58. /*    91/09/22 21:21:35
  59. /* VERSION/RELEASE
  60. /*    1.2
  61. /*--*/
  62.  
  63. static char unproto_sccsid[] = "@(#) unproto.c 1.3 91/11/30 21:10:30";
  64.  
  65. /* C library */
  66.  
  67. #include <stdio.h>
  68. #include <errno.h>
  69.  
  70. extern void exit();
  71. extern int optind;
  72. extern char *optarg;
  73. extern int getopt();
  74.  
  75. /* Application-specific stuff */
  76.  
  77. #include "vstring.h"
  78. #include "stdarg.h"
  79. #include "token.h"
  80. #include "error.h"
  81. #include "symbol.h"
  82.  
  83. /* Forward declarations. */
  84.  
  85. static struct token *dcl_flush();
  86. static void block_flush();
  87. static void block_dcls();
  88. static struct token *show_func_ptr_type();
  89. static struct token *show_struct_type();
  90. static void show_arg_name();
  91. static void show_type();
  92. static void pair_flush();
  93. static void check_cast();
  94.  
  95. #define    check_cast_flush(t)    (check_cast(t), tok_free(t))
  96.  
  97. #ifdef PIPE_THROUGH_CPP
  98. static int pipe_stdin_through_cpp();
  99. #endif
  100.  
  101. /* Disable debugging printfs while preserving side effects. */
  102.  
  103. #ifdef DEBUG
  104. #define    DPRINTF    printf
  105. #else
  106. #define    DPRINTF (void)
  107. #endif
  108.  
  109. /* An attempt to make some complicated expressions a bit more readable. */
  110.  
  111. #define    STREQ(x,y)        (*(x) == *(y) && !strcmp((x),(y)))
  112.  
  113. #define    LAST_ARG_AND_EQUAL(s,c)    ((s)->next == 0 && (s)->head \
  114.                 && ((s)->head == (s)->tail) \
  115.                 && (STREQ((s)->head->vstr->str, (c))))
  116.  
  117. #define    LIST_BEGINS_WITH_STAR(s) (s->head->head && s->head->head->tokno == '*')
  118.  
  119. #define    IS_FUNC_PTR_TYPE(s)    (s->tokno == TOK_LIST && s->next \
  120.                 && s->next->tokno == TOK_LIST \
  121.                 && LIST_BEGINS_WITH_STAR(s))
  122.  
  123. /* main - driver */
  124.  
  125. int     main(argc, argv)
  126. int     argc;
  127. char  **argv;
  128. {
  129.     register struct token *t;
  130. #ifdef    PIPE_THROUGH_CPP            /* pipe through /lib/cpp */
  131.     int     cpp_status;
  132.     int     wait_pid;
  133.     int     cpp_pid;
  134.  
  135.     cpp_pid = pipe_stdin_through_cpp(argv);
  136. #endif
  137.  
  138.     sym_init();                    /* prime the symbol table */
  139.  
  140.     while (t = tok_class(DO_WSPACE)) {
  141.     if (t = dcl_flush(t)) {            /* try declaration */
  142.         if (t->tokno == '{') {        /* examine rejected token */
  143.         block_flush(t);            /* body */
  144.         } else {
  145.         tok_flush(t);            /* other, recover */
  146.         }
  147.     }
  148.     }
  149.  
  150. #ifdef    PIPE_THROUGH_CPP            /* pipe through /lib/cpp */
  151.     while ((wait_pid = wait(&cpp_status)) != -1 && wait_pid != cpp_pid)
  152.      /* void */ ;
  153.     return (wait_pid != cpp_pid || cpp_status != 0);
  154. #else
  155.     return (0);
  156. #endif
  157. }
  158.  
  159. #ifdef    PIPE_THROUGH_CPP        /* pipe through /lib/cpp */
  160.  
  161. /* pipe_stdin_through_cpp - avoid shell script overhead */
  162.  
  163. static int pipe_stdin_through_cpp(argv)
  164. char  **argv;
  165. {
  166.     int     pipefds[2];
  167.     int     pid;
  168.     char  **cpptr = argv;
  169.  
  170.     /*
  171.      * With most UNIX implementations, the second non-option argument to
  172.      * /lib/cpp specifies the output file. If an output file other than
  173.      * stdout is specified, we must force /lib/cpp to write to stdout, and we
  174.      * must redirect our own standard output to the specified output file.
  175.      */
  176.  
  177. #define    IS_OPTION(cp) ((cp)[0] == '-' && (cp)[1] != 0)
  178.  
  179.     /* Skip to first non-option argument, if any. */
  180.  
  181.     while (*++cpptr && IS_OPTION(*cpptr))
  182.      /* void */ ;
  183.  
  184.     /*
  185.      * Assume that the first non-option argument is the input file name. The
  186.      * next argument could be the output destination or an option (System V
  187.      * Release 2 /lib/cpp gets the options *after* the file arguments).
  188.      */
  189.  
  190.     if (*cpptr && *++cpptr && **cpptr != '-') {
  191.  
  192.     /*
  193.      * The first non-option argument is followed by another argument that
  194.      * is not an option ("-stuff") or a hyphen ("-"). Redirect our own
  195.      * standard output before we clobber the file name.
  196.      */
  197.  
  198.     if (freopen(*cpptr, "w", stdout) == 0) {
  199.         perror(*cpptr);
  200.         exit(1);
  201.     }
  202.     /* Clobber the file name argument so that /lib/cpp writes to stdout */
  203.  
  204.     *cpptr = "-";
  205.     }
  206.     /* Set up the pipe that connects /lib/cpp to our standard input. */
  207.  
  208.     if (pipe(pipefds)) {
  209.     perror("pipe");
  210.     exit(1);
  211.     }
  212.     switch (pid = fork()) {
  213.     case -1:                    /* error */
  214.     perror("fork");
  215.     exit(1);
  216.     case 0:                    /* child */
  217.     close(pipefds[0]);            /* close reading end */
  218.     close(1);                /* connect stdout to pipe */
  219.     if (dup(pipefds[1]) != 1)
  220.         error(1, "dup() problem");
  221.     close(pipefds[1]);            /* close redundant fd */
  222.     execv(PIPE_THROUGH_CPP, argv);
  223.     perror(PIPE_THROUGH_CPP);
  224.     exit(1);
  225.     default:                    /* parent */
  226.     close(pipefds[1]);            /* close writing end */
  227.     close(0);                /* connect stdin to pipe */
  228.     if (dup(pipefds[0]) != 0)
  229.         error(1, "dup() problem");
  230.     close(pipefds[0]);            /* close redundant fd */
  231.     return (pid);
  232.     }
  233. }
  234.  
  235. #endif
  236.  
  237. /* header_flush - rewrite new-style function header to old style */
  238.  
  239. static void header_flush(t)
  240. register struct token *t;
  241. {
  242.     register struct token *s;
  243.  
  244.     /* Do argument names, but suppress void and rewrite trailing ... */
  245.  
  246.     if (LAST_ARG_AND_EQUAL(t->head, "void")) {
  247.     put_str("()\n");            /* no arguments */
  248.     } else {
  249.     for (s = t->head; s; s = s->next) {    /* foreach argument... */
  250.         if (LAST_ARG_AND_EQUAL(s, "...")) {
  251. #ifdef _VA_ALIST_                /* see ./stdarg.h */
  252.         put_ch(s->tokno);        /* ',' */
  253.         put_str(_VA_ALIST_);        /* varargs magic */
  254. #endif
  255.         } else {
  256.         put_ch(s->tokno);        /* opening '(' or ',' */
  257.         show_arg_name(s);        /* extract argument name */
  258.         }
  259.     }
  260.     put_str(")\n");                /* closing ')' */
  261.     }
  262.  
  263.     /* Do argument types, but suppress void and trailing ... */
  264.  
  265.     if (!LAST_ARG_AND_EQUAL(t->head, "void")) {
  266.     for (s = t->head; s; s = s->next) {    /* foreach argument... */
  267.         if (!LAST_ARG_AND_EQUAL(s, "...")) {
  268.         if (s->head != s->tail) {    /* really new-style argument? */
  269.             show_line_control();    /* fix line number */
  270.             show_type(s);        /* rewrite type info */
  271.             put_str(";\n");
  272.         }
  273.         }
  274.     }
  275.     }
  276.     tok_free(t);
  277.     show_line_control();            /* because '{' follows */
  278. }
  279.  
  280. /* show_arg_name - extract argument name from argument type info */
  281.  
  282. static void show_arg_name(s)
  283. register struct token *s;
  284. {
  285.     if (s->head) {
  286.     register struct token *p;
  287.     register struct token *t = 0;
  288.  
  289.     /* Find the last interesting item. */
  290.  
  291.     for (p = s->head; p; p = p->next) {
  292.         if (p->tokno == TOK_WORD) {
  293.         t = p;                /* remember last word */
  294.         } else if (IS_FUNC_PTR_TYPE(p)) {
  295.         t = p;                /* or function pointer */
  296.         p = p->next;
  297.         }
  298.     }
  299.  
  300.     /* Extract argument name from last interesting item. */
  301.  
  302.     if (t) {
  303.         if (t->tokno == TOK_LIST)
  304.         show_arg_name(t->head);        /* function pointer, recurse */
  305.         else
  306.         tok_show(t);            /* print last word */
  307.     }
  308.     }
  309. }
  310.  
  311. /* show_type - rewrite type to old-style syntax */
  312.  
  313. static void show_type(s)
  314. register struct token *s;
  315. {
  316.     register struct token *p;
  317.  
  318.     for (p = s->head; p; p = p->next) {
  319.     if (IS_FUNC_PTR_TYPE(p)) {
  320.         p = show_func_ptr_type(p);        /* function pointer type */
  321.     } else {
  322.         tok_show(p);            /* other */
  323.     }
  324.     }
  325. }
  326.  
  327. /* show_func_ptr_type - display function_pointer type using old-style syntax */
  328.  
  329. static struct token *show_func_ptr_type(t)
  330. struct token *t;
  331. {
  332.     register struct token *s;
  333.  
  334.     /*
  335.      * Rewrite (list1) (list2) to (list1) (). Only (list1) is given to us;
  336.      * the caller must have verified the presence of (list2). Account for the
  337.      * rare case that (list1) is a comma-separated list. That should be an
  338.      * error, but we do not want to waste any information.
  339.      */
  340.  
  341.     for (s = t->head; s; s = s->next) {
  342.     put_ch(s->tokno);            /* opening paren or ',' */
  343.     show_type(s);                /* recurse */
  344.     }
  345.     put_str(")()");                /* closing paren */
  346.     return (t->next);
  347. }
  348.  
  349. /* show_struct_type - display structured type, rewrite function-pointer types */
  350.  
  351. static struct token *show_struct_type(p)
  352. register struct token *p;
  353. {
  354.     tok_show(p);                /* opening brace */
  355.  
  356.     while (p->next) {                /* XXX cannot return 0 */
  357.     p = p->next;
  358.     if (IS_FUNC_PTR_TYPE(p)) {
  359.         p = show_func_ptr_type(p);        /* function-pointer member */
  360.     } else if (p->tokno == '{') {
  361.         p = show_struct_type(p);        /* recurse */
  362.     } else {
  363.         tok_show(p);            /* other */
  364.         if (p->tokno == '}') {
  365.         return (p);            /* done */
  366.         }
  367.     }
  368.     }
  369.     DPRINTF("/* missing '}' */");
  370.     return (p);
  371. }
  372.  
  373. /* is_func_ptr_cast - recognize function-pointer type cast */
  374.  
  375. static int is_func_ptr_cast(t)
  376. register struct token *t;
  377. {
  378.     register struct token *p;
  379.  
  380.     /*
  381.      * Examine superficial structure. Require (list1) (list2). Require that
  382.      * list1 begins with a star.
  383.      */
  384.  
  385.     if (!IS_FUNC_PTR_TYPE(t))
  386.     return (0);
  387.  
  388.     /*
  389.      * Make sure that there is no name in (list1). Do not worry about
  390.      * unexpected tokens, because the compiler will complain anyway.
  391.      */
  392.  
  393.     for (p = t->head->head; p; p = p->next) {
  394.     switch (p->tokno) {
  395.     case TOK_LIST:                /* recurse */
  396.         return (is_func_ptr_cast(p));
  397.     case TOK_WORD:                /* name in list */
  398.         return (0);
  399.     }
  400.     }
  401.     return (1);                    /* no name found */
  402. }
  403.  
  404. /* check_cast - display ()-delimited, comma-separated list */
  405.  
  406. static void check_cast(t)
  407. struct token *t;
  408. {
  409.     register struct token *s;
  410.     register struct token *p;
  411.  
  412.     /*
  413.      * Rewrite function-pointer types and function-pointer casts. Do not
  414.      * blindly rewrite (*list1)(list2) to (*list1)(). Function argument lists
  415.      * are about the only thing we can discard without provoking diagnostics
  416.      * from the compiler.
  417.      */
  418.  
  419.     for (s = t->head; s; s = s->next) {
  420.     put_ch(s->tokno);            /* opening paren or ',' */
  421.     for (p = s->head; p; p = p->next) {
  422.         switch (p->tokno) {
  423.         case TOK_LIST:
  424.         if (is_func_ptr_cast(p)) {    /* not: IS_FUNC_PTR_TYPE(p) */
  425.             p = show_func_ptr_type(p);    /* or we might take away */
  426.         } else {            /* function-call arguments */
  427.             check_cast(p);        /* recurse */
  428.         }
  429.         break;
  430.         case '{':
  431.         p = show_struct_type(p);    /* rewrite func. ptr. types */
  432.         break;
  433.         default:
  434.         tok_show(p);
  435.         break;
  436.         }
  437.     }
  438.     }
  439.     put_ch(')');                /* closing paren */
  440. }
  441.  
  442. /* block_dcls - on the fly rewrite decls/initializers at start of block */
  443.  
  444. static void block_dcls()
  445. {
  446.     register struct token *t;
  447.  
  448.     /*
  449.      * Away from the top level, a declaration should be preceded by type or
  450.      * storage-class information. That is why inside blocks, structs and
  451.      * unions we insist on reading one word before passing the _next_ token
  452.      * to the dcl_flush() function.
  453.      * 
  454.      * Struct and union declarations look the same everywhere: we make an
  455.      * exception for these more regular constructs and pass the "struct" and
  456.      * "union" tokens to the type_dcl() function.
  457.      */
  458.  
  459.     while (t = tok_class(DO_WSPACE)) {
  460.     switch (t->tokno) {
  461.     case TOK_WSPACE:            /* preserve white space */
  462.     case '\n':                /* preserve line count */
  463.         tok_flush(t);
  464.         break;
  465.     case TOK_WORD:                /* type declarations? */
  466.         tok_flush(t);            /* advance to next token */
  467.         t = tok_class(DO_WSPACE);        /* null return is ok */
  468.     case TOK_COMPOSITE:            /* struct or union */
  469.         if ((t = dcl_flush(t)) == 0)
  470.         break;
  471.         /* FALLTRHOUGH */
  472.     default:                /* end of declarations */
  473.         DPRINTF("/* end dcls */");
  474.         /* FALLTRHOUGH */
  475.     case '}':                /* end of block */
  476.         tok_unget(t);
  477.         return;
  478.     }
  479.     }
  480. }
  481.  
  482. /* block_flush - rewrite struct, union or statement block on the fly */
  483.  
  484. static void block_flush(t)
  485. register struct token *t;
  486. {
  487.     static int count = 0;
  488.  
  489.     tok_flush(t);
  490.     DPRINTF("/*%d*/", ++count);
  491.  
  492.     /*
  493.      * Rewrite function pointer types in declarations and function pointer
  494.      * casts in initializers at start of block.
  495.      */
  496.  
  497.     block_dcls();
  498.  
  499.     /* Remainder of block: only rewrite function pointer casts. */
  500.  
  501.     while (t = tok_class(DO_WSPACE)) {
  502.     if (t->tokno == TOK_LIST) {
  503.         check_cast_flush(t);
  504.     } else if (t->tokno == '{') {
  505.         block_flush(t);
  506.     } else {
  507.         tok_flush(t);
  508.         if (t->tokno == '}') {
  509.         DPRINTF("/*%d*/", count--);
  510.         return;
  511.         }
  512.     }
  513.     }
  514.     DPRINTF("/* missing '}' */");
  515. }
  516.  
  517. /* pair_flush - on the fly rewrite casts in grouped stuff */
  518.  
  519. static void pair_flush(t, start, stop)
  520. register struct token *t;
  521. register int start;
  522. register int stop;
  523. {
  524.     tok_flush(t);
  525.  
  526.     while (t = tok_class(DO_WSPACE)) {
  527.     if (t->tokno == start) {        /* recurse */
  528.         pair_flush(t, start, stop);
  529.     } else if (t->tokno == TOK_LIST) {    /* expression or cast */
  530.         check_cast_flush(t);
  531.     } else {                /* other, copy */
  532.         tok_flush(t);
  533.         if (t->tokno == stop) {        /* done */
  534.         return;
  535.         }
  536.     }
  537.     }
  538.     DPRINTF("/* missing '%c' */", stop);
  539. }
  540.  
  541. /* initializer - on the fly rewrite casts in initializer */
  542.  
  543. static void initializer()
  544. {
  545.     register struct token *t;
  546.  
  547.     while (t = tok_class(DO_WSPACE)) {
  548.     switch (t->tokno) {
  549.     case ',':                /* list separator */
  550.     case ';':                /* list terminator */
  551.         tok_unget(t);
  552.         return;
  553.     case TOK_LIST:                /* expression or cast */
  554.         check_cast_flush(t);
  555.         break;
  556.     case '[':                /* array substript, may nest */
  557.         pair_flush(t, '[', ']');
  558.         break;
  559.     case '{':                /* structured data, may nest */
  560.         pair_flush(t, '{', '}');
  561.         break;
  562.     default:                /* other, just copy */
  563.         tok_flush(t);
  564.         break;
  565.     }
  566.     }
  567. }
  568.  
  569. /* func_ptr_dcl_flush - rewrite function pointer declaration */
  570.  
  571. static struct token *func_ptr_dcl_flush(list)
  572. register struct token *list;
  573. {
  574.     register struct token *t;
  575.  
  576.     /*
  577.      * Ignore blanks because they would be output earlier than the list that
  578.      * preceded them... Recover gracefully from syntax errors.
  579.      */
  580.  
  581.     while (t = tok_class(NO_WSPACE)) {
  582.     switch (t->tokno) {
  583.     case '\n':                /* preserve line count */
  584.         tok_flush(t);
  585.         break;
  586.     case TOK_LIST:
  587.         /* Function pointer type: (list1) (list2) -> (list1) () */
  588.         (void) show_func_ptr_type(list);    /* may be recursive */
  589.         tok_free(list);
  590.         tok_free(t);
  591.         return (0);
  592.     default:                /* not a declaration */
  593.         tok_unget(t);
  594.         return (list);
  595.     }
  596.     }
  597.  
  598.     /* Hit EOF; must be mistake, but do not waste any information. */
  599.  
  600.     return (list);
  601. }
  602.  
  603. /* function_dcl_flush - rewrite function { heading, type declaration } */
  604.  
  605. static struct token *function_dcl_flush(list)
  606. register struct token *list;
  607. {
  608.     register struct token *t;
  609.  
  610.     /*
  611.      * Ignore blanks because they would be output earlier than the list that
  612.      * preceded them...
  613.      */
  614.  
  615.     while (t = tok_class(NO_WSPACE)) {
  616.     switch (t->tokno) {
  617.     case '\n':
  618.         /* Preserve line count */
  619.         tok_flush(t);
  620.         break;
  621.     case '{':
  622.         /* Function heading: word (list) { -> old style heading */
  623.         header_flush(list);
  624.         tok_unget(t);
  625.         return (0);
  626.     case TOK_WORD:
  627.         /* Old-style function heading: word (list) word...{ */
  628.         tok_flush(list);
  629.         tok_unget(t);
  630.         return (0);
  631.     case TOK_LIST:
  632.         /* Function typedef? word (list1) (list) -> word (list1) () */
  633.         tok_flush(list);
  634.         put_str("()");
  635.         tok_free(t);
  636.         return (0);
  637.     case ',':
  638.     case ';':
  639.         /* Function type declaration: word (list) -> word () */
  640.         tok_free(list);
  641.         put_str("()");
  642.         tok_unget(t);
  643.         return (0);
  644.     default:
  645.         /* Something else, reject the list. */
  646.         tok_unget(t);
  647.         return (list);
  648.     }
  649.     }
  650.  
  651.     /* Hit EOF; must be mistake, but do not waste any information. */
  652.  
  653.     return (list);
  654. }
  655.  
  656. /* dcl_flush - parse declaration on the fly, return rejected token */
  657.  
  658. static struct token *dcl_flush(t)
  659. register struct token *t;
  660. {
  661.     register int got_word;
  662.  
  663.     /*
  664.      * Away from the top level, type or storage-class information is required
  665.      * for an (extern or forward) function type declaration or a variable
  666.      * declaration.
  667.      * 
  668.      * With our naive word-counting approach, this means that the caller should
  669.      * read one word before passing the next token to us. This is how we
  670.      * distinguish, for example, function declarations from function calls.
  671.      * 
  672.      * An exception are structs and unions, because they look the same at any
  673.      * level. The caller should give is the "struct" or "union" token.
  674.      */
  675.  
  676.     for (got_word = 0; t; t = tok_class(DO_WSPACE)) {
  677.     switch (t->tokno) {
  678.     case TOK_WSPACE:            /* advance past blanks */
  679.     case '\n':                /* advance past newline */
  680.     case '*':                /* indirection: keep trying */
  681.         tok_flush(t);
  682.         break;
  683.     case TOK_WORD:                /* word: keep trying */
  684.     case TOK_COMPOSITE:            /* struct or union */
  685.         got_word = 1;
  686.         tok_flush(t);
  687.         break;
  688.     default:
  689.  
  690.         /*
  691.          * Function pointer types can be preceded by zero or more words
  692.          * (at least one when not at the top level). Other stuff can be
  693.          * accepted only after we have seen at least one word (two words
  694.          * when not at the top level). See also the above comment on
  695.          * structs and unions.
  696.          */
  697.  
  698.         if (t->tokno == TOK_LIST && LIST_BEGINS_WITH_STAR(t)) {
  699.         if (t = func_ptr_dcl_flush(t)) {
  700.             return (t);            /* reject token */
  701.         } else {
  702.             got_word = 1;        /* for = and [ and , and ; */
  703.         }
  704.         } else if (got_word == 0) {
  705.         return (t);            /* reject token */
  706.         } else {
  707.         switch (t->tokno) {
  708.         case TOK_LIST:            /* function type */
  709.             if (t = function_dcl_flush(t))
  710.             return (t);        /* reject token */
  711.             break;
  712.         case '[':            /* dimension, does not nest */
  713.             pair_flush(t, '[', ']');
  714.             break;
  715.         case '=':            /* initializer follows */
  716.             tok_flush(t);
  717.             initializer();        /* rewrite casts */
  718.             break;
  719.         case '{':            /* struct, union, may nest */
  720.             block_flush(t);        /* use code for stmt blocks */
  721.             break;
  722.         case ',':            /* separator: keep trying */
  723.             got_word = 0;
  724.             tok_flush(t);
  725.             break;
  726.         case ';':            /* terminator: succeed */
  727.             tok_flush(t);
  728.             return (0);
  729.         default:            /* reject token */
  730.             return (t);
  731.         }
  732.         }
  733.     }
  734.     }
  735.     return (0);                    /* hit EOF */
  736. }
  737.