| /* |
| * cdecl - ANSI C and C++ declaration composer & decoder |
| * |
| * originally written |
| * Graham Ross |
| * once at tektronix!tekmdp!grahamr |
| * now at Context, Inc. |
| * |
| * modified to provide hints for unsupported types |
| * added argument lists for functions |
| * added 'explain cast' grammar |
| * added #ifdef for 'create program' feature |
| * ???? (sorry, I lost your name and login) |
| * |
| * conversion to ANSI C |
| * David Wolverton |
| * ihnp4!houxs!daw |
| * |
| * merged D. Wolverton's ANSI C version w/ ????'s version |
| * added function prototypes |
| * added C++ declarations |
| * made type combination checking table driven |
| * added checks for void variable combinations |
| * made 'create program' feature a runtime option |
| * added file parsing as well as just stdin |
| * added help message at beginning |
| * added prompts when on a TTY or in interactive mode |
| * added getopt() usage |
| * added -a, -r, -p, -c, -d, -D, -V, -i and -+ options |
| * delinted |
| * added #defines for those without getopt or void |
| * added 'set options' command |
| * added 'quit/exit' command |
| * added synonyms |
| * Tony Hansen |
| * attmail!tony, ihnp4!pegasus!hansen |
| * |
| * added extern, register, static |
| * added links to explain, cast, declare |
| * separately developed ANSI C support |
| * Merlyn LeRoy |
| * merlyn@rose3.rosemount.com |
| * |
| * merged versions from LeRoy |
| * added tmpfile() support |
| * allow more parts to be missing during explanations |
| * Tony Hansen |
| * attmail!tony, ihnp4!pegasus!hansen |
| */ |
| |
| char cdeclsccsid[] = "@(#)cdecl.c 2.4 3/31/88"; |
| |
| #include <stdio.h> |
| |
| #include <ctype.h> |
| #if __STDC__ || defined(DOS) |
| # include <stdlib.h> |
| # include <stddef.h> |
| # include <string.h> |
| # include <stdarg.h> |
| #else |
| # ifndef NOVARARGS |
| # include <varargs.h> |
| # endif /* ndef NOVARARGS */ |
| char *malloc(); |
| void free(), exit(), perror(); |
| # ifdef BSD |
| # include <strings.h> |
| extern int errno; |
| # define strrchr rindex |
| # define NOTMPFILE |
| # else |
| # include <string.h> |
| # include <errno.h> |
| # endif /* BSD */ |
| # ifdef NOVOID |
| # define void int |
| # endif /* NOVOID */ |
| #endif /* __STDC__ || DOS */ |
| |
| #include <unistd.h> |
| #include <errno.h> |
| |
| #define MB_SHORT 0001 |
| #define MB_LONG 0002 |
| #define MB_UNSIGNED 0004 |
| #define MB_INT 0010 |
| #define MB_CHAR 0020 |
| #define MB_FLOAT 0040 |
| #define MB_DOUBLE 0100 |
| #define MB_VOID 0200 |
| #define MB_SIGNED 0400 |
| |
| #define NullCP ((char*)NULL) |
| #ifdef dodebug |
| # define Debug(x) do { if (DebugFlag) (void) fprintf x; } while (0) |
| #else |
| # define Debug(x) /* nothing */ |
| #endif |
| |
| #if __STDC__ |
| char *ds(char *), *cat(char *, ...), *visible(int); |
| #ifndef __sun__ |
| int getopt(int,char *const*, const char *); |
| #endif |
| int main(int, char **); |
| int yywrap_nasko(void); |
| int dostdin(void); |
| void mbcheck(void), dohelp(void), usage(void); |
| void prompt(void), doprompt(void), noprompt(void); |
| void unsupp(char *, char *); |
| void notsupported(char *, char *, char *); |
| void yyerror(char *); |
| void doset(char *); |
| void dodeclare(char*, char*, char*, char*, char*); |
| void docast(char*, char*, char*, char*); |
| void dodexplain(char*, char*, char*, char*); |
| void docexplain(char*, char*, char*, char*); |
| void setprogname(const char *); |
| int dotmpfile(int, char**), dofileargs(int, char**); |
| #else |
| char *ds(), *cat(), *visible(); |
| int getopt(); |
| void mbcheck(), dohelp(), usage(); |
| void prompt(), doprompt(), noprompt(); |
| void unsupp(), notsupported(); |
| void yyerror(); |
| void doset(), dodeclare(), docast(), dodexplain(), docexplain(); |
| void setprogname(); |
| int dotmpfile(), dofileargs(); |
| #endif /* __STDC__ */ |
| FILE *tmpfile(); |
| |
| /* variables used during parsing */ |
| unsigned modbits = 0; |
| int arbdims = 1; |
| char *savedname = 0; |
| char unknown_name[] = "unknown_name"; |
| char prev = 0; /* the current type of the variable being examined */ |
| /* values type */ |
| /* p pointer */ |
| /* r reference */ |
| /* f function */ |
| /* a array (of arbitrary dimensions) */ |
| /* A array with dimensions */ |
| /* n name */ |
| /* v void */ |
| /* s struct | class */ |
| /* t simple type (int, long, etc.) */ |
| |
| /* options */ |
| int RitchieFlag = 0; /* -r, assume Ritchie PDP C language */ |
| int MkProgramFlag = 0; /* -c, output {} and ; after declarations */ |
| int PreANSIFlag = 0; /* -p, assume pre-ANSI C language */ |
| int CplusplusFlag = 0; /* -+, assume C++ language */ |
| int OnATty = 0; /* stdin is coming from a terminal */ |
| int Interactive = 0; /* -i, overrides OnATty */ |
| int KeywordName = 0; /* $0 is a keyword (declare, explain, cast) */ |
| const char *progname = "cdecl"; /* $0 */ |
| |
| #if dodebug |
| int DebugFlag = 0; /* -d, output debugging trace info */ |
| #endif |
| |
| #ifdef doyydebug /* compile in yacc trace statements */ |
| #define YYDEBUG 1 |
| #endif /* doyydebug */ |
| |
| #include "cdgram.c.source" |
| #include "cdlex.c.source" |
| |
| /* definitions (and abbreviations) for type combinations cross check table */ |
| #define ALWAYS 0 /* combo always okay */ |
| #define _ ALWAYS |
| #define NEVER 1 /* combo never allowed */ |
| #define X NEVER |
| #define RITCHIE 2 /* combo not allowed in Ritchie compiler */ |
| #define R RITCHIE |
| #define PREANSI 3 /* combo not allowed in Pre-ANSI compiler */ |
| #define P PREANSI |
| #define ANSI 4 /* combo not allowed anymore in ANSI compiler */ |
| #define A ANSI |
| |
| /* This is an lower left triangular array. If we needed */ |
| /* to save 9 bytes, the "long" row can be removed. */ |
| char crosscheck[9][9] = { |
| /* L, I, S, C, V, U, S, F, D, */ |
| /* long */ _, _, _, _, _, _, _, _, _, |
| /* int */ _, _, _, _, _, _, _, _, _, |
| /* short */ X, _, _, _, _, _, _, _, _, |
| /* char */ X, X, X, _, _, _, _, _, _, |
| /* void */ X, X, X, X, _, _, _, _, _, |
| /* unsigned */ R, _, R, R, X, _, _, _, _, |
| /* signed */ P, P, P, P, X, X, _, _, _, |
| /* float */ A, X, X, X, X, X, X, _, _, |
| /* double */ P, X, X, X, X, X, X, X, _ |
| }; |
| |
| /* the names and bits checked for each row in the above array */ |
| struct |
| { |
| char *name; |
| int bit; |
| } crosstypes[9] = |
| { |
| { "long", MB_LONG }, |
| { "int", MB_INT }, |
| { "short", MB_SHORT }, |
| { "char", MB_CHAR }, |
| { "void", MB_VOID }, |
| { "unsigned", MB_UNSIGNED }, |
| { "signed", MB_SIGNED }, |
| { "float", MB_FLOAT }, |
| { "double", MB_DOUBLE } |
| }; |
| |
| /* Run through the crosscheck array looking */ |
| /* for unsupported combinations of types. */ |
| void mbcheck() |
| { |
| register int i, j, restrict; |
| char *t1, *t2; |
| |
| /* Loop through the types */ |
| /* (skip the "long" row) */ |
| for (i = 1; i < 9; i++) |
| { |
| /* if this type is in use */ |
| if ((modbits & crosstypes[i].bit) != 0) |
| { |
| /* check for other types also in use */ |
| for (j = 0; j < i; j++) |
| { |
| /* this type is not in use */ |
| if (!(modbits & crosstypes[j].bit)) |
| continue; |
| /* check the type of restriction */ |
| restrict = crosscheck[i][j]; |
| if (restrict == ALWAYS) |
| continue; |
| t1 = crosstypes[i].name; |
| t2 = crosstypes[j].name; |
| if (restrict == NEVER) |
| { |
| notsupported("", t1, t2); |
| } |
| else if (restrict == RITCHIE) |
| { |
| if (RitchieFlag) |
| notsupported(" (Ritchie Compiler)", t1, t2); |
| } |
| else if (restrict == PREANSI) |
| { |
| if (PreANSIFlag || RitchieFlag) |
| notsupported(" (Pre-ANSI Compiler)", t1, t2); |
| } |
| else if (restrict == ANSI) |
| { |
| if (!RitchieFlag && !PreANSIFlag) |
| notsupported(" (ANSI Compiler)", t1, t2); |
| } |
| else |
| { |
| (void) fprintf (stderr, |
| "%s: Internal error in crosscheck[%d,%d]=%d!\n", |
| progname, i, j, restrict); |
| exit(1); /* NOTREACHED */ |
| } |
| } |
| } |
| } |
| } |
| |
| /* undefine these as they are no longer needed */ |
| #undef _ |
| #undef ALWAYS |
| #undef X |
| #undef NEVER |
| #undef R |
| #undef RITCHIE |
| #undef P |
| #undef PREANSI |
| #undef A |
| #undef ANSI |
| |
| /* Write out a message about something */ |
| /* being unsupported, possibly with a hint. */ |
| void unsupp(s,hint) |
| char *s,*hint; |
| { |
| notsupported("", s, NullCP); |
| if (hint) |
| (void) fprintf(stderr, "\t(maybe you mean \"%s\")\n", hint); |
| } |
| |
| /* Write out a message about something */ |
| /* being unsupported on a particular compiler. */ |
| void notsupported(compiler, type1, type2) |
| char *compiler, *type1, *type2; |
| { |
| if (type2) |
| (void) fprintf(stderr, |
| "Warning: Unsupported in%s C%s -- '%s' with '%s'\n", |
| compiler, CplusplusFlag ? "++" : "", type1, type2); |
| else |
| (void) fprintf(stdout, |
| "Warning: Unsupported in%s C%s -- '%s'\n", |
| compiler, CplusplusFlag ? "++" : "", type1); |
| } |
| |
| /* Called by the yacc grammar */ |
| void yyerror(s) |
| char *s; |
| { |
| (void) printf("%s\n",s); |
| Debug((stdout, "yychar=%d\n", yychar)); |
| } |
| |
| /* Called by the yacc grammar */ |
| int yywrap_nasko() |
| { |
| return 1; |
| } |
| |
| /* |
| * Support for dynamic strings: |
| * cat() creates a string from the concatenation |
| * of a null terminated list of input strings. |
| * The input strings are free()'d by cat() |
| * (so they better have been malloc()'d). |
| * |
| * the different methods of <stdarg.h> and |
| * <vararg.h> are handled within these macros |
| */ |
| #if __STDC__ |
| # define VA_DCL(type,var) (type var,...) |
| # define VA_START(list,var,type) ((va_start(list,var)) , (var)) |
| #else |
| #if defined(DOS) |
| # define VA_DCL(type,var) (var,...) type var; |
| # define VA_START(list,var,type) ((va_start(list,var)) , (var)) |
| #else |
| #ifndef NOVARARGS |
| # define VA_DCL(type,var) (va_alist) va_dcl |
| # define VA_START(list,var,type) ((va_start(list)) , va_arg(list,type)) |
| #else |
| /* |
| * it is assumed here that machines which don't have either |
| * <varargs.h> or <stdarg.h> will put its arguments on |
| * the stack in the "usual" way and consequently can grab |
| * the arguments using the "take the address of the first |
| * parameter and increment by sizeof" trick. |
| */ |
| # define VA_DCL(type,var) (var) type var; |
| # define VA_START(list,var,type) (list = (va_list)&(var) , (var)) |
| # define va_arg(list,type) ((type *)(list += sizeof(type)))[-1] |
| # define va_end(p) /* nothing */ |
| typedef char *va_list; |
| #endif /* NOVARARGS */ |
| #endif /* DOS */ |
| #endif /* __STDC__ */ |
| |
| /* VARARGS */ |
| char *cat |
| VA_DCL(char*, s1) |
| { |
| register char *newstr; |
| register unsigned len = 1; |
| char *str; |
| va_list args; |
| |
| /* find the length which needs to be allocated */ |
| str = VA_START(args, s1, char*); |
| for ( ; str; str = va_arg(args, char*)) |
| len += strlen(str); |
| va_end(args); |
| |
| /* allocate it */ |
| newstr = malloc(len); |
| if (newstr == 0) |
| { |
| (void) fprintf (stderr, "%s: out of malloc space within cat()!\n", |
| progname); |
| exit(1); |
| } |
| newstr[0] = '\0'; |
| |
| /* copy in the strings */ |
| str = VA_START(args, s1, char*); |
| for ( ; str; str = va_arg(args, char*)) |
| { |
| (void) strcat(newstr,str); |
| free(str); |
| } |
| va_end(args); |
| |
| Debug((stderr, "\tcat created '%s'\n", newstr)); |
| return newstr; |
| } |
| |
| /* |
| * ds() makes a malloc()'d string from one that's not. |
| */ |
| char *ds(s) |
| char *s; |
| { |
| register char *p = malloc((unsigned)(strlen(s)+1)); |
| |
| if (p) |
| (void) strcpy(p,s); |
| else |
| { |
| (void) fprintf (stderr, "%s: malloc() failed!\n", progname); |
| exit(1); |
| } |
| return p; |
| } |
| |
| /* return a visible representation of a character */ |
| char *visible(c) |
| int c; |
| { |
| static char buf[5]; |
| |
| c &= 0377; |
| if (isprint(c)) |
| { |
| buf[0] = c; |
| buf[1] = '\0'; |
| } |
| else |
| (void) sprintf(buf,"\\%03o",c); |
| return buf; |
| } |
| |
| #ifdef NOTMPFILE |
| /* provide a conservative version of tmpfile() */ |
| /* for those systems without it. */ |
| /* tmpfile() returns a FILE* of a file opened */ |
| /* for read&write. It is supposed to be */ |
| /* automatically removed when it gets closed, */ |
| /* but here we provide a separate rmtmpfile() */ |
| /* function to perform that function. */ |
| /* Also provide several possible file names to */ |
| /* try for opening. */ |
| static char *file4tmpfile = 0; |
| |
| FILE *tmpfile() |
| { |
| static char *listtmpfiles[] = |
| { |
| "/usr/tmp/cdeclXXXXXX", |
| "/tmp/cdeclXXXXXX", |
| "/cdeclXXXXXX", |
| "cdeclXXXXXX", |
| 0 |
| }; |
| |
| char **listp = listtmpfiles; |
| for ( ; *listp; listp++) |
| { |
| FILE *retfp; |
| (void) mktemp(*listp); |
| retfp = fopen(*listp, "w+"); |
| if (!retfp) |
| continue; |
| file4tmpfile = *listp; |
| return retfp; |
| } |
| |
| return 0; |
| } |
| |
| void rmtmpfile() |
| { |
| if (file4tmpfile) |
| (void) unlink(file4tmpfile); |
| } |
| #else |
| /* provide a mock rmtmpfile() for normal systems */ |
| # define rmtmpfile() /* nothing */ |
| #endif /* NOTMPFILE */ |
| |
| #ifndef NOGETOPT |
| extern int optind; |
| #else |
| /* This is a miniature version of getopt() which will */ |
| /* do just barely enough for us to get by below. */ |
| /* Options are not allowed to be bunched up together. */ |
| /* Option arguments are not supported. */ |
| int optind = 1; |
| |
| int getopt(argc,argv,optstring) |
| char **argv; |
| char *optstring; |
| { |
| int ret; |
| char *p; |
| |
| if ((argv[optind][0] != '-') |
| #ifdef DOS |
| && (argv[optind][0] != '/') |
| #endif /* DOS */ |
| ) |
| return EOF; |
| |
| ret = argv[optind][1]; |
| optind++; |
| |
| for (p = optstring; *p; p++) |
| if (*p == ret) |
| return ret; |
| |
| (void) fprintf (stderr, "%s: illegal option -- %s\n", |
| progname, visible(ret)); |
| |
| return '?'; |
| } |
| #endif |
| |
| /* the help messages */ |
| struct helpstruct |
| { |
| char *text; /* generic text */ |
| char *cpptext; /* C++ specific text */ |
| } helptext[] = |
| { /* up-to 23 lines of help text so it fits on (24x80) screens */ |
| /* 1 */{ "[] means optional; {} means 1 or more; <> means defined elsewhere", 0 }, |
| /* 2 */{ " commands are separated by ';' and newlines", 0 }, |
| /* 3 */{ "command:", 0 }, |
| /* 4 */{ " declare <name> as <english>", 0 }, |
| /* 5 */{ " cast <name> into <english>", 0 }, |
| /* 6 */{ " explain <gibberish>", 0 }, |
| /* 7 */{ " set or set options", 0 }, |
| /* 8 */{ " help, ?", 0 }, |
| /* 9 */{ " quit or exit", 0 }, |
| /* 10 */{ "english:", 0 }, |
| /* 11 */{ " function [( <decl-list> )] returning <english>", 0 }, |
| /* 12 */{ " array [<number>] of <english>", 0 }, |
| /* 13 */{ " [{ const | volatile | noalias }] pointer to <english>", |
| " [{const|volatile}] {pointer|reference} to [member of class <name>] <english>" }, |
| /* 14 */{ " <type>", 0 }, |
| /* 15 */{ "type:", 0 }, |
| /* 16 */{ " {[<storage-class>] [{<modifier>}] [<C-type>]}", 0 }, |
| /* 17 */{ " { struct | union | enum } <name>", |
| " {struct|class|union|enum} <name>" }, |
| /* 18 */{ "decllist: a comma separated list of <name>, <english> or <name> as <english>", 0 }, |
| /* 19 */{ "name: a C identifier", 0 }, |
| /* 20 */{ "gibberish: a C declaration, like 'int *x', or cast, like '(int *)x'", 0 }, |
| /* 21 */{ "storage-class: extern, static, auto, register", 0 }, |
| /* 22 */{ "C-type: int, char, float, double, or void", 0 }, |
| /* 23 */{ "modifier: short, long, signed, unsigned, const, volatile, or noalias", |
| "modifier: short, long, signed, unsigned, const, or volatile" }, |
| { 0, 0 } |
| }; |
| |
| /* Print out the help text */ |
| void dohelp() |
| { |
| register struct helpstruct *p; |
| register char *fmt = CplusplusFlag ? " %s\n" : "\t%s\n"; |
| |
| for (p = helptext; p->text; p++) |
| if (CplusplusFlag && p->cpptext) |
| (void) printf(fmt, p->cpptext); |
| else |
| (void) printf(fmt, p->text); |
| } |
| |
| /* Tell how to invoke cdecl. */ |
| void usage() |
| { |
| (void) fprintf (stderr, "Usage: %s [-r|-p|-a|-+] [-ci%s%s] [files...]\n", |
| progname, |
| #ifdef dodebug |
| "d", |
| #else |
| "", |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| "D" |
| #else |
| "" |
| #endif /* doyydebug */ |
| ); |
| (void) fprintf (stderr, "\t-r Check against Ritchie PDP C Compiler\n"); |
| (void) fprintf (stderr, "\t-p Check against Pre-ANSI C Compiler\n"); |
| (void) fprintf (stderr, "\t-a Check against ANSI C Compiler%s\n", |
| CplusplusFlag ? "" : " (the default)"); |
| (void) fprintf (stderr, "\t-+ Check against C++ Compiler%s\n", |
| CplusplusFlag ? " (the default)" : ""); |
| (void) fprintf (stderr, "\t-c Create compilable output (include ; and {})\n"); |
| (void) fprintf (stderr, "\t-i Force interactive mode\n"); |
| #ifdef dodebug |
| (void) fprintf (stderr, "\t-d Turn on debugging mode\n"); |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| (void) fprintf (stderr, "\t-D Turn on YACC debugging mode\n"); |
| #endif /* doyydebug */ |
| exit(1); |
| /* NOTREACHED */ |
| } |
| |
| /* Manage the prompts. */ |
| static int prompting = 1; |
| |
| void doprompt() { prompting = 1; } |
| void noprompt() { prompting = 0; } |
| |
| void prompt() |
| { |
| if ((OnATty || Interactive) && prompting) { |
| (void) printf("%s> ", progname); |
| (void) fflush(stdout); |
| } |
| } |
| |
| /* Save away the name of the program from argv[0] */ |
| void setprogname(argv0) |
| const char *argv0; |
| { |
| #ifdef DOS |
| char *dot; |
| #endif /* DOS */ |
| |
| progname = strrchr(argv0, '/'); |
| |
| #ifdef DOS |
| if (!progname) |
| progname = strrchr(argv0, '\\'); |
| #endif /* DOS */ |
| |
| if (progname) |
| progname++; |
| else |
| progname = argv0; |
| |
| #ifdef DOS |
| dot = strchr(progname, '.'); |
| if (dot) |
| *dot = '\0'; |
| for (dot = progname; *dot; dot++) |
| *dot = tolower(*dot); |
| #endif /* DOS */ |
| } |
| |
| /* Run down the list of keywords to see if the */ |
| /* program is being called named as one of them */ |
| /* or the first argument is one of them. */ |
| int namedkeyword(argn) |
| char *argn; |
| { |
| static char *cmdlist[] = |
| { |
| "explain", "declare", "cast", "help", "?", "set", 0 |
| }; |
| |
| /* first check the program name */ |
| char **cmdptr = cmdlist; |
| for ( ; *cmdptr; cmdptr++) |
| if (strcmp(*cmdptr, progname) == 0) |
| { |
| KeywordName = 1; |
| return 1; |
| } |
| |
| /* now check $1 */ |
| for (cmdptr = cmdlist; *cmdptr; cmdptr++) |
| if (strcmp(*cmdptr, argn) == 0) |
| return 1; |
| |
| /* nope, must be file name arguments */ |
| return 0; |
| } |
| |
| /* Read from standard input, turning */ |
| /* on prompting if necessary. */ |
| int dostdin() |
| { |
| int ret; |
| OnATty = isatty(0); |
| if (OnATty || Interactive) |
| { |
| (void) printf("Type `help' or `?' for help\n"); |
| prompt(); |
| } |
| |
| yyin = stdin; |
| ret = yyparse(); |
| OnATty = 0; |
| return ret; |
| } |
| |
| /* Write the arguments into a file */ |
| /* and treat that file as the input. */ |
| int dotmpfile(argc, argv) |
| int argc; |
| char **argv; |
| { |
| int ret = 0; |
| FILE *tmpfp = tmpfile(); |
| if (!tmpfp) |
| { |
| int sverrno = errno; |
| (void) fprintf (stderr, "%s: cannot open temp file\n", |
| progname); |
| errno = sverrno; |
| perror(progname); |
| return 1; |
| } |
| |
| if (KeywordName) |
| if (fputs(progname, tmpfp) == EOF) |
| { |
| int sverrno; |
| errwrite: |
| sverrno = errno; |
| (void) fprintf (stderr, "%s: error writing to temp file\n", |
| progname); |
| errno = sverrno; |
| perror(progname); |
| (void) fclose(tmpfp); |
| rmtmpfile(); |
| return 1; |
| } |
| |
| for ( ; optind < argc; optind++) |
| if (fprintf(tmpfp, " %s", argv[optind]) == EOF) |
| goto errwrite; |
| |
| if (putc('\n', tmpfp) == EOF) |
| goto errwrite; |
| |
| rewind(tmpfp); |
| yyin = tmpfp; |
| ret += yyparse(); |
| (void) fclose(tmpfp); |
| rmtmpfile(); |
| |
| return ret; |
| } |
| |
| /* Read each of the named files for input. */ |
| int dofileargs(argc, argv) |
| int argc; |
| char **argv; |
| { |
| FILE *ifp; |
| int ret = 0; |
| |
| for ( ; optind < argc; optind++) |
| if (strcmp(argv[optind], "-") == 0) |
| ret += dostdin(); |
| |
| else if ((ifp = fopen(argv[optind], "r")) == NULL) |
| { |
| int sverrno = errno; |
| (void) fprintf (stderr, "%s: cannot open %s\n", |
| progname, argv[optind]); |
| errno = sverrno; |
| perror(argv[optind]); |
| ret++; |
| } |
| |
| else |
| { |
| yyin = ifp; |
| ret += yyparse(); |
| } |
| |
| return ret; |
| } |
| |
| /* print out a cast */ |
| void docast(name, left, right, type) |
| char *name, *left, *right, *type; |
| { |
| int lenl = strlen(left), lenr = strlen(right); |
| |
| if (prev == 'f') |
| unsupp("Cast into function", |
| "cast into pointer to function"); |
| else if (prev=='A' || prev=='a') |
| unsupp("Cast into array","cast into pointer"); |
| (void) printf("(%s%*s%s)%s\n", |
| type, lenl+lenr?lenl+1:0, |
| left, right, name ? name : "expression"); |
| free(left); |
| free(right); |
| free(type); |
| if (name) |
| free(name); |
| } |
| |
| /* print out a declaration */ |
| void dodeclare(name, storage, left, right, type) |
| char *name, *storage, *left, *right, *type; |
| { |
| if (prev == 'v') |
| unsupp("Variable of type void", |
| "variable of type pointer to void"); |
| |
| if (*storage == 'r') |
| switch (prev) |
| { |
| case 'f': unsupp("Register function", NullCP); break; |
| case 'A': |
| case 'a': unsupp("Register array", NullCP); break; |
| case 's': unsupp("Register struct/class", NullCP); break; |
| } |
| |
| if (*storage) |
| (void) printf("%s ", storage); |
| (void) printf("%s %s%s%s", |
| type, left, |
| name ? name : (prev == 'f') ? "f" : "var", right); |
| if (MkProgramFlag) { |
| if ((prev == 'f') && (*storage != 'e')) |
| (void) printf(" { }\n"); |
| else |
| (void) printf(";\n"); |
| } else { |
| (void) printf("\n"); |
| } |
| free(storage); |
| free(left); |
| free(right); |
| free(type); |
| if (name) |
| free(name); |
| } |
| |
| void dodexplain(storage, constvol, type, decl) |
| char *storage, *constvol, *type, *decl; |
| { |
| if (type && (strcmp(type, "void") == 0)) |
| if (prev == 'n') |
| unsupp("Variable of type void", |
| "variable of type pointer to void"); |
| else if (prev == 'a') |
| unsupp("array of type void", |
| "array of type pointer to void"); |
| else if (prev == 'r') |
| unsupp("reference to type void", |
| "pointer to void"); |
| |
| if (*storage == 'r') |
| switch (prev) |
| { |
| case 'f': unsupp("Register function", NullCP); break; |
| case 'A': |
| case 'a': unsupp("Register array", NullCP); break; |
| case 's': unsupp("Register struct/union/enum/class", NullCP); break; |
| } |
| |
| (void) printf("declare %s as ", savedname); |
| if (*storage) |
| (void) printf("%s ", storage); |
| (void) printf("%s", decl); |
| if (*constvol) |
| (void) printf("%s ", constvol); |
| (void) printf("%s\n", type ? type : "int"); |
| } |
| |
| void docexplain(constvol, type, cast, name) |
| char *constvol, *type, *cast, *name; |
| { |
| if (strcmp(type, "void") == 0) |
| if (prev == 'a') |
| unsupp("array of type void", |
| "array of type pointer to void"); |
| else if (prev == 'r') |
| unsupp("reference to type void", |
| "pointer to void"); |
| (void) printf("cast %s into %s", name, cast); |
| if (strlen(constvol) > 0) |
| (void) printf("%s ", constvol); |
| (void) printf("%s\n",type); |
| } |
| |
| /* Do the appropriate things for the "set" command. */ |
| void doset(opt) |
| char *opt; |
| { |
| if (strcmp(opt, "create") == 0) |
| { MkProgramFlag = 1; } |
| else if (strcmp(opt, "nocreate") == 0) |
| { MkProgramFlag = 0; } |
| else if (strcmp(opt, "interactive") == 0) |
| { Interactive = 1; } |
| else if (strcmp(opt, "nointeractive") == 0) |
| { Interactive = 0; OnATty = 0; } |
| else if (strcmp(opt, "ritchie") == 0) |
| { CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; } |
| else if (strcmp(opt, "preansi") == 0) |
| { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; } |
| else if (strcmp(opt, "ansi") == 0) |
| { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; } |
| else if (strcmp(opt, "cplusplus") == 0) |
| { CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; } |
| #ifdef dodebug |
| else if (strcmp(opt, "debug") == 0) |
| { DebugFlag = 1; } |
| else if (strcmp(opt, "nodebug") == 0) |
| { DebugFlag = 0; } |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| else if (strcmp(opt, "yydebug") == 0) |
| { yydebug = 1; } |
| else if (strcmp(opt, "noyydebug") == 0) |
| { yydebug = 0; } |
| #endif /* doyydebug */ |
| else |
| { |
| if ((strcmp(opt, unknown_name) != 0) && |
| (strcmp(opt, "options") != 0)) |
| (void) printf("Unknown set option: '%s'\n", opt); |
| |
| (void) printf("Valid set options (and command line equivalents) are:\n"); |
| (void) printf("\toptions\n"); |
| (void) printf("\tcreate (-c), nocreate\n"); |
| (void) printf("\tinteractive (-i), nointeractive\n"); |
| (void) printf("\tritchie (-r), preansi (-p), ansi (-a) or cplusplus (-+)\n"); |
| #ifdef dodebug |
| (void) printf("\tdebug (-d), nodebug\n"); |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| (void) printf("\tyydebug (-D), noyydebug\n"); |
| #endif /* doyydebug */ |
| |
| (void) printf("\nCurrent set values are:\n"); |
| (void) printf("\t%screate\n", MkProgramFlag ? " " : " no"); |
| (void) printf("\t%sinteractive\n", |
| (OnATty || Interactive) ? " " : " no"); |
| if (RitchieFlag) |
| (void) printf("\t ritchie\n"); |
| else |
| (void) printf("\t(noritchie)\n"); |
| if (PreANSIFlag) |
| (void) printf("\t preansi\n"); |
| else |
| (void) printf("\t(nopreansi)\n"); |
| if (!RitchieFlag && !PreANSIFlag && !CplusplusFlag) |
| (void) printf("\t ansi\n"); |
| else |
| (void) printf("\t(noansi)\n"); |
| if (CplusplusFlag) |
| (void) printf("\t cplusplus\n"); |
| else |
| (void) printf("\t(nocplusplus)\n"); |
| #ifdef dodebug |
| (void) printf("\t%sdebug\n", DebugFlag ? " " : " no"); |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| (void) printf("\t%syydebug\n", yydebug ? " " : " no"); |
| #endif /* doyydebug */ |
| } |
| } |
| |
| void versions() |
| { |
| (void) printf("Version:\n\t%s\n\t%s\n\t%s\n", |
| cdeclsccsid, cdgramsccsid, cdlexsccsid); |
| exit(0); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int c, ret = 0; |
| |
| setprogname(argv[0]); |
| #ifdef DOS |
| if (strcmp(progname, "cppdecl") == 0) |
| #else |
| if (strcmp(progname, "c++decl") == 0) |
| #endif /* DOS */ |
| CplusplusFlag = 1; |
| |
| while ((c = getopt(argc, argv, "cirpa+dDV")) != EOF) |
| switch (c) |
| { |
| case 'c': MkProgramFlag=1; break; |
| case 'i': Interactive=1; break; |
| |
| /* The following are mutually exclusive. */ |
| /* Only the last one set prevails. */ |
| case 'r': CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; break; |
| case 'p': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; break; |
| case 'a': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; break; |
| case '+': CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; break; |
| |
| #ifdef dodebug |
| case 'd': DebugFlag=1; break; |
| #endif /* dodebug */ |
| #ifdef doyydebug |
| case 'D': yydebug=1; break; |
| #endif /* doyydebug */ |
| case 'V': versions(); break; |
| case '?': usage(); break; |
| } |
| |
| /* Run down the list of arguments, parsing each one. */ |
| |
| /* Use standard input if no file names or "-" is found. */ |
| if (optind == argc) |
| ret += dostdin(); |
| |
| /* If called as explain, declare or cast, or first */ |
| /* argument is one of those, use the command line */ |
| /* as the input. */ |
| else if (namedkeyword(argv[optind])) |
| ret += dotmpfile(argc, argv); |
| |
| else |
| ret += dofileargs(argc, argv); |
| |
| return ret; |
| /* NOTREACHED */ |
| } |
| |
| |
| |
| |
| |
| |