home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / cdecl2 / part02 / cdecl.c
Encoding:
C/C++ Source or Header  |  1988-05-09  |  25.0 KB  |  1,015 lines

  1. /*
  2.  * cdecl - ANSI C and C++ declaration composer & decoder
  3.  *
  4.  *    originally written
  5.  *        Graham Ross
  6.  *        once at tektronix!tekmdp!grahamr
  7.  *        now at Context, Inc.
  8.  *
  9.  *    modified to provide hints for unsupported types
  10.  *    added argument lists for functions
  11.  *    added 'explain cast' grammar
  12.  *    added #ifdef for 'create program' feature
  13.  *        ???? (sorry, I lost your name and login)
  14.  *
  15.  *    conversion to ANSI C
  16.  *        David Wolverton
  17.  *        ihnp4!houxs!daw
  18.  *
  19.  *    merged D. Wolverton's ANSI C version w/ ????'s version
  20.  *    added function prototypes
  21.  *    added C++ declarations
  22.  *    made type combination checking table driven
  23.  *    added checks for void variable combinations
  24.  *    made 'create program' feature a runtime option
  25.  *    added file parsing as well as just stdin
  26.  *    added help message at beginning
  27.  *    added prompts when on a TTY or in interactive mode
  28.  *    added getopt() usage
  29.  *    added -a, -r, -p, -c, -d, -D, -V, -i and -+ options
  30.  *    delinted
  31.  *    added #defines for those without getopt or void
  32.  *    added 'set options' command
  33.  *    added 'quit/exit' command
  34.  *    added synonyms
  35.  *        Tony Hansen
  36.  *        attmail!tony, ihnp4!pegasus!hansen
  37.  *
  38.  *    added extern, register, static
  39.  *    added links to explain, cast, declare
  40.  *    separately developed ANSI C support
  41.  *        Merlyn LeRoy
  42.  *        merlyn@rose3.rosemount.com
  43.  *
  44.  *    merged versions from LeRoy
  45.  *    added tmpfile() support
  46.  *    allow more parts to be missing during explanations
  47.  *        Tony Hansen
  48.  *        attmail!tony, ihnp4!pegasus!hansen
  49.  */
  50.  
  51. char cdeclsccsid[] = "@(#)cdecl.c    2.4 3/31/88";
  52.  
  53. #include <stdio.h>
  54. #include <ctype.h>
  55. #if __STDC__ || defined(DOS)
  56. # include <stdlib.h>
  57. # include <stddef.h>
  58. # include <string.h>
  59. # include <stdarg.h>
  60. #else
  61. # ifndef NOVARARGS
  62. #  include <varargs.h>
  63. # endif /* ndef NOVARARGS */
  64. char *malloc();
  65. void free(), exit(), perror();
  66. # ifdef BSD
  67. #  include <strings.h>
  68.    extern int errno;
  69. #  define strrchr rindex
  70. #  define NOTMPFILE
  71. # else
  72. #  include <string.h>
  73. #  include <errno.h>
  74. # endif /* BSD */
  75. # ifdef NOVOID
  76. #  define void int
  77. # endif /* NOVOID */
  78. #endif /* __STDC__ || DOS */
  79.  
  80. #define    MB_SHORT    0001
  81. #define    MB_LONG        0002
  82. #define    MB_UNSIGNED    0004
  83. #define MB_INT        0010
  84. #define MB_CHAR        0020
  85. #define MB_FLOAT    0040
  86. #define MB_DOUBLE    0100
  87. #define MB_VOID        0200
  88. #define    MB_SIGNED    0400
  89.  
  90. #define NullCP ((char*)NULL)
  91. #ifdef dodebug
  92. # define Debug(x) do { if (DebugFlag) (void) fprintf x; } while (0)
  93. #else
  94. # define Debug(x) /* nothing */
  95. #endif
  96.  
  97. #if __STDC__
  98.   char *ds(char *), *cat(char *, ...), *visible(int);
  99.   int getopt(int,char **,char *);
  100.   int main(int, char **);
  101.   int yywrap(void);
  102.   int dostdin(void);
  103.   void mbcheck(void), dohelp(void), usage(void);
  104.   void prompt(void), doprompt(void), noprompt(void);
  105.   void unsupp(char *, char *);
  106.   void notsupported(char *, char *, char *);
  107.   void yyerror(char *);
  108.   void doset(char *);
  109.   void dodeclare(char*, char*, char*, char*, char*);
  110.   void docast(char*, char*, char*, char*);
  111.   void dodexplain(char*, char*, char*, char*);
  112.   void docexplain(char*, char*, char*, char*);
  113.   void setprogname(char *);
  114.   int dotmpfile(int, char**), dofileargs(int, char**);
  115. #else
  116.   char *ds(), *cat(), *visible();
  117.   int getopt();
  118.   void mbcheck(), dohelp(), usage();
  119.   void prompt(), doprompt(), noprompt();
  120.   void unsupp(), notsupported();
  121.   void yyerror();
  122.   void doset(), dodeclare(), docast(), dodexplain(), docexplain();
  123.   void setprogname();
  124.   int dotmpfile(), dofileargs();
  125. #endif /* __STDC__ */
  126.   FILE *tmpfile();
  127.  
  128. /* variables used during parsing */
  129. unsigned modbits = 0;
  130. int arbdims = 1;
  131. char *savedname = 0;
  132. char unknown_name[] = "unknown_name";
  133. char prev = 0;        /* the current type of the variable being examined */
  134.             /*    values    type                   */
  135.             /*    p    pointer                   */
  136.             /*    r    reference               */
  137.             /*    f    function               */
  138.             /*    a    array (of arbitrary dimensions)    */
  139.             /*    A    array with dimensions           */
  140.             /*    n    name                   */
  141.             /*    v    void                   */
  142.             /*    s    struct | class               */
  143.             /*    t    simple type (int, long, etc.)       */
  144.  
  145. /* options */
  146. int RitchieFlag = 0;        /* -r, assume Ritchie PDP C language */
  147. int MkProgramFlag = 0;        /* -c, output {} and ; after declarations */
  148. int PreANSIFlag = 0;        /* -p, assume pre-ANSI C language */
  149. int CplusplusFlag = 0;        /* -+, assume C++ language */
  150. int OnATty = 0;            /* stdin is coming from a terminal */
  151. int Interactive = 0;        /* -i, overrides OnATty */
  152. int KeywordName = 0;        /* $0 is a keyword (declare, explain, cast) */
  153. char *progname = "cdecl";    /* $0 */
  154.  
  155. #if dodebug
  156. int DebugFlag = 0;        /* -d, output debugging trace info */
  157. #endif
  158.  
  159. #ifdef doyydebug        /* compile in yacc trace statements */
  160. #define YYDEBUG 1
  161. #endif /* doyydebug */
  162.  
  163. #include "cdgram.c"
  164. #include "cdlex.c"
  165.  
  166. /* definitions (and abbreviations) for type combinations cross check table */
  167. #define ALWAYS    0    /* combo always okay */
  168. #define _    ALWAYS
  169. #define NEVER    1    /* combo never allowed */
  170. #define X    NEVER
  171. #define RITCHIE    2    /* combo not allowed in Ritchie compiler */
  172. #define R    RITCHIE
  173. #define PREANSI    3    /* combo not allowed in Pre-ANSI compiler */
  174. #define P    PREANSI
  175. #define ANSI    4    /* combo not allowed anymore in ANSI compiler */
  176. #define A    ANSI
  177.  
  178. /* This is an lower left triangular array. If we needed */
  179. /* to save 9 bytes, the "long" row can be removed. */
  180. char crosscheck[9][9] = {
  181.     /*            L, I, S, C, V, U, S, F, D, */
  182.     /* long */        _, _, _, _, _, _, _, _, _,
  183.     /* int */        _, _, _, _, _, _, _, _, _,
  184.     /* short */        X, _, _, _, _, _, _, _, _,
  185.     /* char */        X, X, X, _, _, _, _, _, _,
  186.     /* void */        X, X, X, X, _, _, _, _, _,
  187.     /* unsigned */    R, _, R, R, X, _, _, _, _,
  188.     /* signed */    P, P, P, P, X, X, _, _, _,
  189.     /* float */        A, X, X, X, X, X, X, _, _,
  190.     /* double */    P, X, X, X, X, X, X, X, _
  191. };
  192.  
  193. /* the names and bits checked for each row in the above array */
  194. struct
  195.     {
  196.     char *name;
  197.     int bit;
  198.     } crosstypes[9] =
  199.     {
  200.         { "long",        MB_LONG        },
  201.         { "int",        MB_INT        },
  202.         { "short",        MB_SHORT    },
  203.         { "char",        MB_CHAR        },
  204.         { "void",        MB_VOID        },
  205.         { "unsigned",    MB_UNSIGNED    },
  206.         { "signed",        MB_SIGNED    },
  207.         { "float",        MB_FLOAT    },
  208.         { "double",        MB_DOUBLE    }
  209.     };
  210.  
  211. /* Run through the crosscheck array looking */
  212. /* for unsupported combinations of types. */
  213. void mbcheck()
  214. {
  215.     register int i, j, restrict;
  216.     char *t1, *t2;
  217.  
  218.     /* Loop through the types */
  219.     /* (skip the "long" row) */
  220.     for (i = 1; i < 9; i++)
  221.     {
  222.     /* if this type is in use */
  223.     if ((modbits & crosstypes[i].bit) != 0)
  224.         {
  225.         /* check for other types also in use */
  226.         for (j = 0; j < i; j++)
  227.         {
  228.         /* this type is not in use */
  229.         if (!(modbits & crosstypes[j].bit))
  230.             continue;
  231.         /* check the type of restriction */
  232.         restrict = crosscheck[i][j];
  233.         if (restrict == ALWAYS)
  234.             continue;
  235.         t1 = crosstypes[i].name;
  236.         t2 = crosstypes[j].name;
  237.         if (restrict == NEVER)
  238.             {
  239.             notsupported("", t1, t2);
  240.             }
  241.         else if (restrict == RITCHIE)
  242.             {
  243.             if (RitchieFlag)
  244.             notsupported(" (Ritchie Compiler)", t1, t2);
  245.             }
  246.         else if (restrict == PREANSI)
  247.             {
  248.             if (PreANSIFlag || RitchieFlag)
  249.             notsupported(" (Pre-ANSI Compiler)", t1, t2);
  250.             }
  251.         else if (restrict == ANSI)
  252.             {
  253.             if (!RitchieFlag && !PreANSIFlag)
  254.             notsupported(" (ANSI Compiler)", t1, t2);
  255.             }
  256.         else
  257.             {
  258.             (void) fprintf (stderr,
  259.             "%s: Internal error in crosscheck[%d,%d]=%d!\n",
  260.             progname, i, j, restrict);
  261.             exit(1); /* NOTREACHED */
  262.             }
  263.         }
  264.         }
  265.     }
  266. }
  267.  
  268. /* undefine these as they are no longer needed */
  269. #undef _
  270. #undef ALWAYS
  271. #undef X
  272. #undef NEVER
  273. #undef R
  274. #undef RITCHIE
  275. #undef P
  276. #undef PREANSI
  277. #undef A
  278. #undef ANSI
  279.  
  280. /* Write out a message about something */
  281. /* being unsupported, possibly with a hint. */
  282. void unsupp(s,hint)
  283. char *s,*hint;
  284. {
  285.     notsupported("", s, NullCP);
  286.     if (hint)
  287.     (void) fprintf(stderr, "\t(maybe you mean \"%s\")\n", hint);
  288. }
  289.  
  290. /* Write out a message about something */
  291. /* being unsupported on a particular compiler. */
  292. void notsupported(compiler, type1, type2)
  293. char *compiler, *type1, *type2;
  294. {
  295.     if (type2)
  296.     (void) fprintf(stderr,
  297.         "Warning: Unsupported in%s C%s -- '%s' with '%s'\n",
  298.         compiler, CplusplusFlag ? "++" : "", type1, type2);
  299.     else
  300.     (void) fprintf(stderr,
  301.         "Warning: Unsupported in%s C%s -- '%s'\n",
  302.         compiler, CplusplusFlag ? "++" : "", type1);
  303. }
  304.  
  305. /* Called by the yacc grammar */
  306. void yyerror(s)
  307. char *s;
  308. {
  309.     (void) printf("%s\n",s);
  310.     Debug((stdout, "yychar=%d\n", yychar));
  311. }
  312.  
  313. /* Called by the yacc grammar */
  314. int yywrap()
  315. {
  316.     return 1;
  317. }
  318.  
  319. /*
  320.  * Support for dynamic strings:
  321.  * cat() creates a string from the concatenation
  322.  * of a null terminated list of input strings.
  323.  * The input strings are free()'d by cat()
  324.  * (so they better have been malloc()'d).
  325.  *
  326.  * the different methods of <stdarg.h> and
  327.  * <vararg.h> are handled within these macros
  328.  */
  329. #if __STDC__
  330. #  define VA_DCL(type,var)        (type var,...)
  331. #  define VA_START(list,var,type)    ((va_start(list,var)) , (var))
  332. #else
  333. #if defined(DOS)
  334. #  define VA_DCL(type,var)        (var,...) type var;
  335. #  define VA_START(list,var,type)    ((va_start(list,var)) , (var))
  336. #else
  337. #ifndef NOVARARGS
  338. # define VA_DCL(type,var)        (va_alist) va_dcl
  339. # define VA_START(list,var,type)    ((va_start(list)) , va_arg(list,type))
  340. #else
  341.    /*
  342.     *    it is assumed here that machines which don't have either
  343.     *    <varargs.h> or <stdarg.h> will put its arguments on
  344.     *    the stack in the "usual" way and consequently can grab
  345.     *    the arguments using the "take the address of the first
  346.     *    parameter and increment by sizeof" trick.
  347.     */
  348. # define VA_DCL(type,var)        (var) type var;
  349. # define VA_START(list,var,type)    (list = (va_list)&(var) , (var))
  350. # define va_arg(list,type)        ((type *)(list += sizeof(type)))[-1]
  351. # define va_end(p)            /* nothing */
  352. typedef char *va_list;
  353. #endif /* NOVARARGS */
  354. #endif /* DOS */
  355. #endif /* __STDC__ */
  356.  
  357. /* VARARGS */
  358. char *cat
  359. VA_DCL(char*, s1)
  360. {
  361.     register char *newstr;
  362.     register unsigned len = 1;
  363.     char *str;
  364.     va_list args;
  365.  
  366.     /* find the length which needs to be allocated */
  367.     str = VA_START(args, s1, char*);
  368.     for ( ; str; str = va_arg(args, char*))
  369.     len += strlen(str);
  370.     va_end(args);
  371.  
  372.     /* allocate it */
  373.     newstr = malloc(len);
  374.     if (newstr == 0)
  375.     {
  376.     (void) fprintf (stderr, "%s: out of malloc space within cat()!\n",
  377.         progname);
  378.     exit(1);
  379.     }
  380.     newstr[0] = '\0';
  381.  
  382.     /* copy in the strings */
  383.     str = VA_START(args, s1, char*);
  384.     for ( ; str; str = va_arg(args, char*))
  385.     {
  386.     (void) strcat(newstr,str);
  387.     free(str);
  388.     }
  389.     va_end(args);
  390.  
  391.     Debug((stderr, "\tcat created '%s'\n", newstr));
  392.     return newstr;
  393. }
  394.  
  395. /*
  396.  * ds() makes a malloc()'d string from one that's not.
  397.  */
  398. char *ds(s)
  399. char *s;
  400. {
  401.     register char *p = malloc((unsigned)(strlen(s)+1));
  402.  
  403.     if (p)
  404.     (void) strcpy(p,s);
  405.     else
  406.     {
  407.     (void) fprintf (stderr, "%s: malloc() failed!\n", progname);
  408.     exit(1);
  409.     }
  410.     return p;
  411. }
  412.  
  413. /* return a visible representation of a character */
  414. char *visible(c)
  415. int c;
  416. {
  417.     static char buf[5];
  418.  
  419.     c &= 0377;
  420.     if (isprint(c))
  421.     {
  422.     buf[0] = c;
  423.     buf[1] = '\0';
  424.     }
  425.     else
  426.     (void) sprintf(buf,"\\%03o",c);
  427.     return buf;
  428. }
  429.  
  430. #ifdef NOTMPFILE
  431. /* provide a conservative version of tmpfile() */
  432. /* for those systems without it. */
  433. /* tmpfile() returns a FILE* of a file opened */
  434. /* for read&write. It is supposed to be */
  435. /* automatically removed when it gets closed, */
  436. /* but here we provide a separate rmtmpfile() */
  437. /* function to perform that function. */
  438. /* Also provide several possible file names to */
  439. /* try for opening. */
  440. static char *file4tmpfile = 0;
  441.  
  442. FILE *tmpfile()
  443. {
  444.     static char *listtmpfiles[] =
  445.     {
  446.     "/usr/tmp/cdeclXXXXXX",
  447.     "/tmp/cdeclXXXXXX",
  448.     "/cdeclXXXXXX",
  449.     "cdeclXXXXXX",
  450.     0
  451.     };
  452.  
  453.     char **listp = listtmpfiles;
  454.     for ( ; *listp; listp++)
  455.     {
  456.     FILE *retfp;
  457.     (void) mktemp(*listp);
  458.     retfp = fopen(*listp, "w+");
  459.     if (!retfp)
  460.         continue;
  461.     file4tmpfile = *listp;
  462.     return retfp;
  463.     }
  464.  
  465.     return 0;
  466. }
  467.  
  468. void rmtmpfile()
  469. {
  470.     if (file4tmpfile)
  471.     (void) unlink(file4tmpfile);
  472. }
  473. #else
  474. /* provide a mock rmtmpfile() for normal systems */
  475. # define rmtmpfile()    /* nothing */
  476. #endif /* NOTMPFILE */
  477.  
  478. #ifndef NOGETOPT
  479. extern int optind;
  480. #else
  481. /* This is a miniature version of getopt() which will */
  482. /* do just barely enough for us to get by below. */
  483. /* Options are not allowed to be bunched up together. */
  484. /* Option arguments are not supported. */
  485. int optind = 1;
  486.  
  487. int getopt(argc,argv,optstring)
  488. char **argv;
  489. char *optstring;
  490. {
  491.     int ret;
  492.     char *p;
  493.  
  494.     if ((argv[optind][0] != '-')
  495. #ifdef DOS
  496.     && (argv[optind][0] != '/')
  497. #endif /* DOS */
  498.     )
  499.     return EOF;
  500.  
  501.     ret = argv[optind][1];
  502.     optind++;
  503.  
  504.     for (p = optstring; *p; p++)
  505.     if (*p == ret)
  506.         return ret;
  507.  
  508.     (void) fprintf (stderr, "%s: illegal option -- %s\n",
  509.     progname, visible(ret));
  510.  
  511.     return '?';
  512. }
  513. #endif
  514.  
  515. /* the help messages */
  516. struct helpstruct
  517.     {
  518.     char *text;    /* generic text */
  519.     char *cpptext;    /* C++ specific text */
  520.     } helptext[] =
  521.     {    /* up-to 23 lines of help text so it fits on (24x80) screens */
  522. /*  1 */{ "[] means optional; {} means 1 or more; <> means defined elsewhere", 0 },
  523. /*  2 */{ "  commands are separated by ';' and newlines", 0 },
  524. /*  3 */{ "command:", 0 },
  525. /*  4 */{ "  declare <name> as <english>", 0 },
  526. /*  5 */{ "  cast <name> into <english>", 0 },
  527. /*  6 */{ "  explain <gibberish>", 0 },
  528. /*  7 */{ "  set or set options", 0 },
  529. /*  8 */{ "  help, ?", 0 },
  530. /*  9 */{ "  quit or exit", 0 },
  531. /* 10 */{ "english:", 0 },
  532. /* 11 */{ "  function [( <decl-list> )] returning <english>", 0 },
  533. /* 12 */{ "  array [<number>] of <english>", 0 },
  534. /* 13 */{ "  [{ const | volatile | noalias }] pointer to <english>",
  535.       "  [{const|volatile}] {pointer|reference} to [member of class <name>] <english>" },
  536. /* 14 */{ "  <type>", 0 },
  537. /* 15 */{ "type:", 0 },
  538. /* 16 */{ "  {[<storage-class>] [{<modifier>}] [<C-type>]}", 0 },
  539. /* 17 */{ "  { struct | union | enum } <name>",
  540.       "  {struct|class|union|enum} <name>" },
  541. /* 18 */{ "decllist: a comma separated list of <name>, <english> or <name> as <english>", 0 },
  542. /* 19 */{ "name: a C identifier", 0 },
  543. /* 20 */{ "gibberish: a C declaration, like 'int *x', or cast, like '(int *)x'", 0 },
  544. /* 21 */{ "storage-class: extern, static, auto, register", 0 },
  545. /* 22 */{ "C-type: int, char, float, double, or void", 0 },
  546. /* 23 */{ "modifier: short, long, signed, unsigned, const, volatile, or noalias",
  547.       "modifier: short, long, signed, unsigned, const, or volatile" },
  548.     { 0, 0 }
  549.     };
  550.  
  551. /* Print out the help text */
  552. void dohelp()
  553. {
  554.     register struct helpstruct *p;
  555.     register char *fmt = CplusplusFlag ? " %s\n" : "\t%s\n";
  556.  
  557.     for (p = helptext; p->text; p++)
  558.     if (CplusplusFlag && p->cpptext)
  559.         (void) printf(fmt, p->cpptext);
  560.     else
  561.         (void) printf(fmt, p->text);
  562. }
  563.  
  564. /* Tell how to invoke cdecl. */
  565. void usage()
  566. {
  567.     (void) fprintf (stderr, "Usage: %s [-r|-p|-a|-+] [-ci%s%s] [files...]\n",
  568.     progname,
  569. #ifdef dodebug
  570.     "d",
  571. #else
  572.     "",
  573. #endif /* dodebug */
  574. #ifdef doyydebug
  575.     "D"
  576. #else
  577.     ""
  578. #endif /* doyydebug */
  579.     );
  580.     (void) fprintf (stderr, "\t-r Check against Ritchie PDP C Compiler\n");
  581.     (void) fprintf (stderr, "\t-p Check against Pre-ANSI C Compiler\n");
  582.     (void) fprintf (stderr, "\t-a Check against ANSI C Compiler%s\n",
  583.     CplusplusFlag ? "" : " (the default)");
  584.     (void) fprintf (stderr, "\t-+ Check against C++ Compiler%s\n",
  585.     CplusplusFlag ? " (the default)" : "");
  586.     (void) fprintf (stderr, "\t-c Create compilable output (include ; and {})\n");
  587.     (void) fprintf (stderr, "\t-i Force interactive mode\n");
  588. #ifdef dodebug
  589.     (void) fprintf (stderr, "\t-d Turn on debugging mode\n");
  590. #endif /* dodebug */
  591. #ifdef doyydebug
  592.     (void) fprintf (stderr, "\t-D Turn on YACC debugging mode\n");
  593. #endif /* doyydebug */
  594.     exit(1);
  595.     /* NOTREACHED */
  596. }
  597.  
  598. /* Manage the prompts. */
  599. static int prompting = 1;
  600.  
  601. void doprompt() { prompting = 1; }
  602. void noprompt() { prompting = 0; }
  603.  
  604. void prompt()
  605. {
  606.     if ((OnATty || Interactive) && prompting) {
  607.     (void) printf("%s> ", progname);
  608.     (void) fflush(stdout);
  609.     }
  610. }
  611.  
  612. /* Save away the name of the program from argv[0] */
  613. void setprogname(argv0)
  614. char *argv0;
  615. {
  616. #ifdef DOS
  617.     char *dot;
  618. #endif /* DOS */
  619.  
  620.     progname = strrchr(argv0, '/');
  621.  
  622. #ifdef DOS
  623.     if (!progname)
  624.     progname = strrchr(argv0, '\\');
  625. #endif /* DOS */
  626.  
  627.     if (progname)
  628.     progname++;
  629.     else
  630.     progname = argv0;
  631.  
  632. #ifdef DOS
  633.     dot = strchr(progname, '.');
  634.     if (dot)
  635.     *dot = '\0';
  636.     for (dot = progname; *dot; dot++)
  637.     *dot = tolower(*dot);
  638. #endif /* DOS */
  639. }
  640.  
  641. /* Run down the list of keywords to see if the */
  642. /* program is being called named as one of them */
  643. /* or the first argument is one of them. */
  644. int namedkeyword(argn)
  645. char *argn;
  646. {
  647.     static char *cmdlist[] =
  648.     {
  649.     "explain", "declare", "cast", "help", "?", "set", 0
  650.     };
  651.  
  652.     /* first check the program name */
  653.     char **cmdptr = cmdlist;
  654.     for ( ; *cmdptr; cmdptr++)
  655.     if (strcmp(*cmdptr, progname) == 0)
  656.         {
  657.         KeywordName = 1;
  658.         return 1;
  659.         }
  660.  
  661.     /* now check $1 */
  662.     for (cmdptr = cmdlist; *cmdptr; cmdptr++)
  663.     if (strcmp(*cmdptr, argn) == 0)
  664.         return 1;
  665.  
  666.     /* nope, must be file name arguments */
  667.     return 0;
  668. }
  669.  
  670. /* Read from standard input, turning */
  671. /* on prompting if necessary. */
  672. int dostdin()
  673. {
  674.     int ret;
  675.     OnATty = isatty(0);
  676.     if (OnATty || Interactive)
  677.     {
  678.     (void) printf("Type `help' or `?' for help\n");
  679.     prompt();
  680.     }
  681.  
  682.     yyin = stdin;
  683.     ret = yyparse();
  684.     OnATty = 0;
  685.     return ret;
  686. }
  687.  
  688. /* Write the arguments into a file */
  689. /* and treat that file as the input. */
  690. int dotmpfile(argc, argv)
  691. int argc;
  692. char **argv;
  693. {
  694.     int ret = 0;
  695.     FILE *tmpfp = tmpfile();
  696.     if (!tmpfp)
  697.     {
  698.     int sverrno = errno;
  699.     (void) fprintf (stderr, "%s: cannot open temp file\n",
  700.         progname);
  701.     errno = sverrno;
  702.     perror(progname);
  703.     return 1;
  704.     }
  705.  
  706.     if (KeywordName)
  707.     if (fputs(progname, tmpfp) == EOF)
  708.         {
  709.         int sverrno;
  710.     errwrite:
  711.         sverrno = errno;
  712.         (void) fprintf (stderr, "%s: error writing to temp file\n",
  713.         progname);
  714.         errno = sverrno;
  715.         perror(progname);
  716.         (void) fclose(tmpfp);
  717.         rmtmpfile();
  718.         return 1;
  719.         }
  720.  
  721.     for ( ; optind < argc; optind++)
  722.     if (fprintf(tmpfp, " %s", argv[optind]) == EOF)
  723.         goto errwrite;
  724.  
  725.     if (putc('\n', tmpfp) == EOF)
  726.     goto errwrite;
  727.  
  728.     rewind(tmpfp);
  729.     yyin = tmpfp;
  730.     ret += yyparse();
  731.     (void) fclose(tmpfp);
  732.     rmtmpfile();
  733.  
  734.     return ret;
  735. }
  736.  
  737. /* Read each of the named files for input. */
  738. int dofileargs(argc, argv)
  739. int argc;
  740. char **argv;
  741. {
  742.     FILE *ifp;
  743.     int ret = 0;
  744.  
  745.     for ( ; optind < argc; optind++)
  746.     if (strcmp(argv[optind], "-") == 0)
  747.         ret += dostdin();
  748.  
  749.     else if ((ifp = fopen(argv[optind], "r")) == NULL)
  750.         {
  751.         int sverrno = errno;
  752.         (void) fprintf (stderr, "%s: cannot open %s\n",
  753.         progname, argv[optind]);
  754.         errno = sverrno;
  755.         perror(argv[optind]);
  756.         ret++;
  757.         }
  758.  
  759.     else
  760.         {
  761.         yyin = ifp;
  762.         ret += yyparse();
  763.         }
  764.  
  765.     return ret;
  766. }
  767.  
  768. /* print out a cast */
  769. void docast(name, left, right, type)
  770. char *name, *left, *right, *type;
  771. {
  772.     int lenl = strlen(left), lenr = strlen(right);
  773.  
  774.     if (prev == 'f')
  775.         unsupp("Cast into function",
  776.             "cast into pointer to function");
  777.     else if (prev=='A' || prev=='a')
  778.         unsupp("Cast into array","cast into pointer");
  779.     (void) printf("(%s%*s%s)%s\n",
  780.         type, lenl+lenr?lenl+1:0,
  781.         left, right, name ? name : "expression");
  782.     free(left);
  783.     free(right);
  784.     free(type);
  785.     if (name)
  786.         free(name);
  787. }
  788.  
  789. /* print out a declaration */
  790. void dodeclare(name, storage, left, right, type)
  791. char *name, *storage, *left, *right, *type;
  792. {
  793.     if (prev == 'v')
  794.         unsupp("Variable of type void",
  795.             "variable of type pointer to void");
  796.  
  797.     if (*storage == 'r')
  798.     switch (prev)
  799.         {
  800.         case 'f': unsupp("Register function", NullCP); break;
  801.         case 'A':
  802.         case 'a': unsupp("Register array", NullCP); break;
  803.         case 's': unsupp("Register struct/class", NullCP); break;
  804.         }
  805.  
  806.     if (*storage)
  807.         (void) printf("%s ", storage);
  808.     (void) printf("%s %s%s%s",
  809.         type, left,
  810.     name ? name : (prev == 'f') ? "f" : "var", right);
  811.     if (MkProgramFlag) {
  812.         if ((prev == 'f') && (*storage != 'e'))
  813.             (void) printf(" { }\n");
  814.         else
  815.             (void) printf(";\n");
  816.     } else {
  817.         (void) printf("\n");
  818.     }
  819.     free(storage);
  820.     free(left);
  821.     free(right);
  822.     free(type);
  823.     if (name)
  824.         free(name);
  825. }
  826.  
  827. void dodexplain(storage, constvol, type, decl)
  828. char *storage, *constvol, *type, *decl;
  829. {
  830.     if (type && (strcmp(type, "void") == 0))
  831.     if (prev == 'n')
  832.         unsupp("Variable of type void",
  833.            "variable of type pointer to void");
  834.     else if (prev == 'a')
  835.         unsupp("array of type void",
  836.            "array of type pointer to void");
  837.     else if (prev == 'r')
  838.         unsupp("reference to type void",
  839.            "pointer to void");
  840.  
  841.     if (*storage == 'r')
  842.     switch (prev)
  843.         {
  844.         case 'f': unsupp("Register function", NullCP); break;
  845.         case 'A':
  846.         case 'a': unsupp("Register array", NullCP); break;
  847.         case 's': unsupp("Register struct/union/enum/class", NullCP); break;
  848.         }
  849.  
  850.     (void) printf("declare %s as ", savedname);
  851.     if (*storage)
  852.         (void) printf("%s ", storage);
  853.     (void) printf("%s", decl);
  854.     if (*constvol)
  855.         (void) printf("%s ", constvol);
  856.     (void) printf("%s\n", type ? type : "int");
  857. }
  858.  
  859. void docexplain(constvol, type, cast, name)
  860. char *constvol, *type, *cast, *name;
  861. {
  862.     if (strcmp(type, "void") == 0)
  863.     if (prev == 'a')
  864.         unsupp("array of type void",
  865.            "array of type pointer to void");
  866.     else if (prev == 'r')
  867.         unsupp("reference to type void",
  868.            "pointer to void");
  869.     (void) printf("cast %s into %s", name, cast);
  870.     if (strlen(constvol) > 0)
  871.         (void) printf("%s ", constvol);
  872.     (void) printf("%s\n",type);
  873. }
  874.  
  875. /* Do the appropriate things for the "set" command. */
  876. void doset(opt)
  877. char *opt;
  878. {
  879.     if (strcmp(opt, "create") == 0)
  880.     { MkProgramFlag = 1; }
  881.     else if (strcmp(opt, "nocreate") == 0)
  882.     { MkProgramFlag = 0; }
  883.     else if (strcmp(opt, "interactive") == 0)
  884.     { Interactive = 1; }
  885.     else if (strcmp(opt, "nointeractive") == 0)
  886.     { Interactive = 0; OnATty = 0; }
  887.     else if (strcmp(opt, "ritchie") == 0)
  888.     { CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; }
  889.     else if (strcmp(opt, "preansi") == 0)
  890.     { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; }
  891.     else if (strcmp(opt, "ansi") == 0)
  892.     { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; }
  893.     else if (strcmp(opt, "cplusplus") == 0)
  894.     { CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; }
  895. #ifdef dodebug
  896.     else if (strcmp(opt, "debug") == 0)
  897.     { DebugFlag = 1; }
  898.     else if (strcmp(opt, "nodebug") == 0)
  899.     { DebugFlag = 0; }
  900. #endif /* dodebug */
  901. #ifdef doyydebug
  902.     else if (strcmp(opt, "yydebug") == 0)
  903.     { yydebug = 1; }
  904.     else if (strcmp(opt, "noyydebug") == 0)
  905.     { yydebug = 0; }
  906. #endif /* doyydebug */
  907.     else
  908.     {
  909.     if ((strcmp(opt, unknown_name) != 0) &&
  910.         (strcmp(opt, "options") != 0))
  911.         (void) printf("Unknown set option: '%s'\n", opt);
  912.  
  913.     (void) printf("Valid set options (and command line equivalents) are:\n");
  914.     (void) printf("\toptions\n");
  915.     (void) printf("\tcreate (-c), nocreate\n");
  916.     (void) printf("\tinteractive (-i), nointeractive\n");
  917.     (void) printf("\tritchie (-r), preansi (-p), ansi (-a) or cplusplus (-+)\n");
  918. #ifdef dodebug
  919.     (void) printf("\tdebug (-d), nodebug\n");
  920. #endif /* dodebug */
  921. #ifdef doyydebug
  922.     (void) printf("\tyydebug (-D), noyydebug\n");
  923. #endif /* doyydebug */
  924.  
  925.     (void) printf("\nCurrent set values are:\n");
  926.     (void) printf("\t%screate\n", MkProgramFlag ? "   " : " no");
  927.     (void) printf("\t%sinteractive\n",
  928.         (OnATty || Interactive) ? "   " : " no");
  929.     if (RitchieFlag)
  930.         (void) printf("\t   ritchie\n");
  931.     else
  932.         (void) printf("\t(noritchie)\n");
  933.     if (PreANSIFlag)
  934.         (void) printf("\t   preansi\n");
  935.     else
  936.         (void) printf("\t(nopreansi)\n");
  937.     if (!RitchieFlag && !PreANSIFlag && !CplusplusFlag)
  938.         (void) printf("\t   ansi\n");
  939.     else
  940.         (void) printf("\t(noansi)\n");
  941.     if (CplusplusFlag)
  942.         (void) printf("\t   cplusplus\n");
  943.     else
  944.         (void) printf("\t(nocplusplus)\n");
  945. #ifdef dodebug
  946.     (void) printf("\t%sdebug\n", DebugFlag ? "   " : " no");
  947. #endif /* dodebug */
  948. #ifdef doyydebug
  949.     (void) printf("\t%syydebug\n", yydebug ? "   " : " no");
  950. #endif /* doyydebug */
  951.     }
  952. }
  953.  
  954. void versions()
  955. {
  956.     (void) printf("Version:\n\t%s\n\t%s\n\t%s\n",
  957.     cdeclsccsid, cdgramsccsid, cdlexsccsid);
  958.     exit(0);
  959. }
  960.  
  961. int main(argc, argv)
  962. char **argv;
  963. {
  964.     int c, ret = 0;
  965.  
  966.     setprogname(argv[0]);
  967. #ifdef DOS
  968.     if (strcmp(progname, "cppdecl") == 0)
  969. #else
  970.     if (strcmp(progname, "c++decl") == 0)
  971. #endif /* DOS */
  972.     CplusplusFlag = 1;
  973.  
  974.     while ((c = getopt(argc, argv, "cirpa+dDV")) != EOF)
  975.     switch (c)
  976.         {
  977.         case 'c': MkProgramFlag=1; break;
  978.         case 'i': Interactive=1; break;
  979.  
  980.         /* The following are mutually exclusive. */
  981.         /* Only the last one set prevails. */
  982.         case 'r': CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; break;
  983.         case 'p': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; break;
  984.         case 'a': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; break;
  985.         case '+': CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; break;
  986.  
  987. #ifdef dodebug
  988.         case 'd': DebugFlag=1; break;
  989. #endif /* dodebug */
  990. #ifdef doyydebug
  991.         case 'D': yydebug=1; break;
  992. #endif /* doyydebug */
  993.         case 'V': versions(); break;
  994.         case '?': usage(); break;
  995.         }
  996.  
  997.     /* Run down the list of arguments, parsing each one. */
  998.  
  999.     /* Use standard input if no file names or "-" is found. */
  1000.     if (optind == argc)
  1001.     ret += dostdin();
  1002.  
  1003.     /* If called as explain, declare or cast, or first */
  1004.     /* argument is one of those, use the command line */
  1005.     /* as the input. */
  1006.     else if (namedkeyword(argv[optind]))
  1007.     ret += dotmpfile(argc, argv);
  1008.  
  1009.     else
  1010.     ret += dofileargs(argc, argv);
  1011.  
  1012.     exit(ret);
  1013.     /* NOTREACHED */
  1014. }
  1015.