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