-h- mpdefs.h Wed Sep 05 09:43:08 1984 MPDEFS.H;13 /* Compile-time constants */ /* #define UNIMATION */ /* Define to enable page numbers */ #define TRUE 1 #define FALSE 0 #define EOF -1 #define ERROR -1 #define OK 0 #define DEF 0 #define INCL 1 #define UNDEF 2 #define IF 3 #define IFD 4 #define IFN 5 #define ELSE 6 #define ENDIF 7 #define MSG 8 #define LINE 9 #define EOS '\0' #define EQL 0 #define NEQ 1 #define LSS 2 #define LEQ 3 #define GTR 4 #define GEQ 5 #define NIL 0 #define SOH 001 #define OCTAL 8 #define DECIMAL 10 #ifndef stdio #define STDIN 0 /* Standard input file descriptor */ #define STDOUT 1 /* Standard output */ #define STDERR 2 /* Standard error output */ #endif #define LINESZ 300 /* Size of a line */ #define MAXIDLEN 8 /* Maximum length of an identifier */ #define SRCHMAX 2 /* #include search list size */ #define ALASIZE 10 /* Maximum number of macro parameters */ #define INCDEPTH 12 /* Maximum #include depth */ #define IFDEPTH 25 /* Maximum #if nesting depth */ #define DEFDEPTH 25 /* Maximum nested definition level */ #define EXPBUFSZ 300 /* Maximum #if expression buffer size */ #define CATMAX 50 /* Size of 'concat' buffer */ #define MAXARGSZ 100 /* Maximum length of an actual parameter */ #ifdef vms #include #endif /* Structure defintions */ #ifndef stdio struct buf { /* Getc, putc buffer structure */ int fildes; /* File descriptor */ int nleft; /* Number of bytes left in buffer */ char *nextp; /* Buffer pointer */ char buff[512]; /* The I/O buffer itself */ }; #endif struct stack { int spcnt; /* Space count */ int usecnt; /* Use count */ int *stackptr; /* Pointer into stack buffer */ }; struct sym { char name[MAXIDLEN+1]; /* Name buffer */ int nargs; /* Number of arguments */ int ref; /* Reference marker to stop recursive definitions */ char *defptr; /* Pointer to definition block */ struct sym *next; /* Forward link for bucketing */ }; -h- mpextr.h Wed Sep 05 09:43:08 1984 MPEXTR.H;16 /* External references */ /* Standard C library routines */ extern char *alloc(); extern free(); extern printf(); extern exit(); extern abort(); #ifndef stdio extern int fopen(); extern int fcreat(); extern close(); extern fflush(); #endif extern char *ctime(); extern time(); /* References to routines that are compiled separately */ extern printerr(); /* Error message printer */ extern screech(); /* Internal bug reporter */ extern int lexeq(); /* Lexical comparison */ extern char *skipblnk(); /* Skip over white space */ extern char *get_mem(); /* Dynamic memory allocator */ extern char *concat(); /* Concatenate two strings */ extern int expr(); /* Constant expression evaluator */ extern struct sym *lookup(); /* Symbol look-up routine */ extern sym_enter(); /* Enter a symbol into symbol table */ extern int sym_del(); /* Symbol table deletion routine */ extern sym_print(); /* Print symbol table */ extern sym_init(); /* Initialize symbol table */ extern int push(); /* Push onto a stack */ extern int empty(); /* Test for empty stack */ extern int *envsave(); /* Save runtime stack environment */ extern int envreset(); /* Restore to previous environment */ extern char *skipq(); /* Skip over quoted text */ extern int putch(); /* Emit text to buffer or output file */ extern char *get_id(); /* Get an identifier */ extern rlse_ala(); /* Release call_ala entries */ -h- mp.c Wed Sep 05 09:43:08 1984 MP.C;47 /* * MP -- Macro and conditional compilation preprocessor for C */ /*)BUILD $(PROGRAM) = mp $(INCLUDE) = { mpdefs.h mpextr.h } $(FILES) = { mp mpexpr mpsym mputil } $(STACK) = 10000 $(TKBOPTIONS) = { STACK = 1024 TASK = ...MPC ACTFIL = 14 UNITS = 14 } */ #ifdef DOCUMENTATION title mp Macro Pre-processor index Macro Pre-processor for C programs synopsis mp [files] description Mp is a general pre-processor for C programs. It implements the full C pre-processor syntax: .lm +16 .s.i-16;_#line Ignored (needed for communication with program generators such as lex. .s.i-16;_#if Conditional compilation. .s.i-16;_#ifdef Conditional compilation. .s.i-16;_#else Conditional compilation. .s.i-16;_#endif Conditional compilation. .s.i-16;_#message Writes a message on the user terminal. .s.i-16;_#define Define a symbol or macro. .s.i-16;_#include (nested) source files. .s.lm -16 See Kernighan and Ritchie for details of the C source file format. .s On Decus C, the compiler preprocessor phase may be supressed by invoking the compiler with the "-m" switch: .s.nf MP file.c (writes file.mpc) XCC file.mpc -m .s.f diagnostics .lm +8 .s.i -8;_... many, should be self-explanatory. .lm -8 author Robert W. Harper, Jr. .s Mp was distributed via the Unix user's group. bugs #endif /* * Designed and written by Robert W. Harper, Jr. in fullfillment of the * programming requirements for ICSS-580 Systems Programming taken * during Winter quarter 1978/79, with invaluable assistance from * Mike Lutz, my project advisor, who helped me with some sticky * design issues and originally wrote the expression evaluator. * * Version 1.10 - 4/18/79 * * Edit log -- document all source changes here (and increment * the edit level!) * First release: 4/4/79 RWH * Fixed conditional compilation bugs and changed #include paths: * 4/7/79 RWH * Added support for character constants: 4/10/79 RWH * Fixed bug re extraneous in front of #include line itself: * 4/10/79 RWH * Fixed incorrect support of character constants: 4/12/79 RWH * Made alpha() and alphanum() macro's instead of functions: * 4/12/79 RWH * Fixed #ifdef/#ifndef argument length bug: 4/12/79 RWH * Rewrote concat() to make it faster: 4/12/79 RWH * Allowed white space between '#' and directive: 4/13/79 RWH * Modified actual() to propagate actuals through nested calls: * 4/13/79 RWH * Added _FILENAME and _LINE: 4/18/79 RWH * Fixed pop_ala() so that works correctly: 4/18/79 RWH * * Added code for decus and vms implementations, also for stdio. * 05-Sep-80 MM * More Decus stuff ... provide same predefined symbols as provided * by the Decus compiler's preprocessor. Also made '-P' flag * default to 'on', supressing stuff, for Decus compiler, * which does not recognize this. 05-Dec-80 RBD * * N O T E * It is necessary to compile this preprocessor * on the TARGET operating system for the pre- * defined symbols to be correct. * * Changed directory search list to look for 'lb:' on RSX, and * 'sy:' on RT-11. 05-Dec-80 RBD * * Eliminate leading whitespace in macro expansion. 06-Dec-80 RBD * * Display filespec string on failing #include file opens, instead * of useless, terse "File not found". 06-Dec-80 RBD * * Supress call to 'free' on include file open fail for stdio * version. Fix search list so it looks at "LB:[1,1]" for RSX. * Bob Denny 29-May-81 * * Added automatic insertions of #LINE p. * for RSX systems. code completely removed. -P now * defaults to off. Introduced default file extensions of .C * and .MPC. Made output file default to . * Scott Roth, 25-Aug-81 * * Merged into "normal" distribution. #LINE (as defined above) * is now only available by #define'ing UNIMATION in mpdefs.h. * Martin Minow, 28-Mar-82 * * This implementation of the C macro and conditional compilation * preprocessor is designed to bring the currently-distributed version * of the C compiler up to the standards defined in "The C Programming * Language" by Kernighan and Ritchie (sine qua non). The only * preprocessor feature that remains unimplemented is the "#line" * directive. This version is implemented as an independent program, * designed to be forked to by "cc", much like the other passes of the * compiler, though it may be used independently of the C compiler for * any similar purpose. The callling syntax is * * "mp [-P] input_file output_file" * * where the "-P" switch causes suppression of the convention * used by the compiler to flag included files (usually all lines of * source stemming from an include file are preceded by a * character so that the compiler's line numbers agree with the source). * NOTE: the '-P' switch is turned on permanently for the Decus version. * (and under the vax native compiler -- MM 28-Mar-82) * * All routines are documented with introductory comments preceding the * definition of the function, describing the techniques used therein. * Other terse comments appear within the source code to clarify certain * sticky parts. The overall design of the processor is line-oriented, * in that it reads a line, processes as necessary and then writes it * out. Due to lack of clarity in the specification of constant * expressions in the aforementioned text, this processor evaluates * expressions by first running them through the macro expansion routine, * then calling the recursive-descent parser/evaluator. * * Note that recursion is used in two major areas of the design -- in * the expression evaluator and in the macro expansion routine. * Inherent in using a recursive solution to a problem is a certain * degree of difficulty that arises when an error condition occurs. * In order to solve this, the processor uses two small assembly-language * routines, envsave() and envreset(), to save the frame pointer prior * to the call of either routine (expand() or expr()); upon encountering * an error, envreset() is called to restore the environment to that of * the top-level caller and force a particular value to be returned. * (envsave and envreset were changed to setjmp() and longjmp() for * compatiblity with other stdio libraries). * * The code has been fairly thoroughly tested, but inevitably bugs will * appear and complaints about the design will crop up. If you encounter * any bugs or have any suggestions for enhancements, contact the author * or Mike Lutz at RIT School of Computer Science. */ /* * Include compile-time constant definitions, external references, * and structures. */ #ifdef decus #define stdio #define Digital #endif #ifdef vms #define stdio #define Digital #endif #ifdef stdio #include #else #define NULL (0) #endif #ifdef vms /* * This creates files in vanilla RMS on VMS V2 */ extern FILE *fdopen(); #define CREATE(f, m) fdopen(creat(f, 0, "rat=cr", "rfm=var"), m) #else #define CREATE fopen #endif #include "mpdefs.h" #include "mpextr.h" /* * Setjmp/longjmp stuff */ #include jmp_buf jump_env; /* * Directives */ char *dir_tbl[] = { /* Directive name table */ "define", "include", "undef", "if", "ifdef", "ifndef", "else", "endif", "message", "line", NIL }; int dir_type[] = { /* Directive mnemonics for 'switch' */ DEF, INCL, UNDEF, IF, IFD, IFN, ELSE, ENDIF, MSG, LINE, }; int err_cnt = 0; /* Error counter */ #ifdef Digital int nocomp = TRUE; /* No stuff for Decus compiler */ #ifdef rsx char syn_err[] = {"RSX version. Syntax: mpc ifile ofile"}; #else #ifdef rt11 char syn_err[] = {"RT-11 Version. Syntax: run mp \"ifile ofile\""}; #else #ifdef vms char syn_err[] = { "Vax-11C version. Syntax: mp ifile ofile"}; #else char syn_err[] = {"Decus compiler assumed. Syntax: mp ifile ofile"}; #endif #endif #endif #else /* * True Unix */ int nocomp = FALSE; /* No-compilation flag */ char syn_err[] = {"Syntax: mp [-P] ifile ofile"}; #endif /* * I/O-related stuff */ #ifdef stdio FILE *curinfil = NULL; /* Pointer to current input file */ FILE *outfil = NULL; /* Pointer to output file */ #else struct buf *curinfil = NIL; /* Pointer to file buffer for current */ /* input file */ struct buf *outfil = NIL; /* Current output file buffer pointer */ #endif /* * #include */ int inclpdl[INCDEPTH]; struct stack inclstk = { INCDEPTH, 0, inclpdl }; int inclvl = 0; /* #include nesting level */ int inclflag = FALSE; /* Ugly fix for extraneous before */ /* #include line itself */ char *srchlist[] = { "", /* This must be first! */ #ifdef Digital /* Next, look on device 'C:', then ... */ "c:", "lb:", /* RSTS/E, VMS */ #ifdef rt11 /* On RT-11, system disk is SY: */ "sy:@", /* RSTS/E */ "sy:", #else #ifdef rsx "sy:@", /* RSTS/E */ "lb:[1,1]", /* On RSX, default task/library disk LB:*/ #endif #endif #else "/usr/include/", /* UNIX */ #endif NIL /* This must be last! */ }; /* * Conditional compilation */ int ifpdl[IFDEPTH]; struct stack ifstack = { IFDEPTH, 0, ifpdl }; int father = TRUE; /* Previous level expansion flag */ int self = TRUE; /* Current level expansion flag */ /* * Input line processing related data structures */ char line[LINESZ]; int lineno[INCDEPTH]; int in_com = FALSE; /* TRUE if in a comment */ char fname[INCDEPTH][40] = { NULL}; /* Include file names */ int pageno[INCDEPTH]; /* Include page number */ int linefeeds = 1; /* Count line feeds as output */ struct sym *_line; /* _LINE macro -- definition is current */ /* line number */ char *lp; /* Line pointer */ /* * Macro definition and expansion data structures */ char def_ala[ALASIZE][MAXIDLEN+1]; /* Definition ala */ char *call_ala[ALASIZE]; /* Dynamically-allocatable call ala */ char *callpdl[DEFDEPTH]; struct stack callstk = { DEFDEPTH, 0, callpdl }; /* Call stack - used to process nested */ /* macro calls */ struct sym *refpdl[DEFDEPTH]; struct stack refstack = { DEFDEPTH, 0, refpdl }; /* Stack for referenced symbols */ /* * Main driver */ main(argc, argv) char *argv[]; int argc; { register char *tp1, *tp2; register int i; char name[MAXIDLEN+1]; char name_buf[50], *f; int tvec[2], dir_kind, filecnt; /* * Initialize line number stack */ lineno[0] = 0; pageno[0] = 1; for (i = 1, filecnt = 0; i < argc; i++) if (*argv[i] == '-') switch (argv[i][1]) { case 'p': case 'P': nocomp = TRUE; continue; default: puts(syn_err); exits(2); } else if (filecnt > 2) { puts(syn_err); exits(2); } else tvec[filecnt++] = i; if (filecnt == 0) { puts(syn_err); exits(2); } #ifdef stdio f = strcpy(name_buf, argv[tvec[0]]); while (*f != '.' && *f != EOS) f++; if (*f == EOS) strcpy(f, ".C"); if ((curinfil = fopen(name_buf, "r")) == NULL) { perror(argv[tvec[0]]); exits(2); } if (filecnt == 1) *f = EOS; /* f -> after file name */ else { f = strcpy(name_buf, argv[tvec[1]]); while (*f != '.' && *f != EOS) f++; } if (*f == EOS) strcpy(f, ".MPC"); if ((outfil = CREATE(name_buf, "w")) == NULL) { perror(argv[tvec[1]]); exits(2); } *f = EOS; /* name_buf has out name */ #else curinfil = get_mem(sizeof *curinfil); if (fopen(argv[tvec[0]], curinfil) < 0) { puts("Can't open input file"); exit(1); } outfil = get_mem(sizeof *outfil); if (fcreat(argv[tvec[1]], outfil) < 0) { puts("Can't create output file"); exit(1); } strcpy(name_buf, argv[tvec[1]]); f = name_buf + strlen(name_buf); #endif sym_init(); /* Initialize symbol table */ i = strlen(argv[tvec[0]]); sym_enter("_FILENAM", 0, tp1 = get_mem(i+3)); *tp1++ = '"'; strcpy(tp1, argv[tvec[0]]); *(tp1+i) = '"'; *(tp1+i+1) = EOS; _line = sym_enter("_LINE", 0, tp1 = get_mem(9)); strcpy(tp1, "\" \""); #ifdef Digital #ifdef decus sym_enter("decus",0,get_mem(0)); /* Do predefines like Decus compiler */ #ifdef nofpu sym_enter("nofpu",0,get_mem(0)); #endif sym_enter("pdp11",0,get_mem(0)); #endif #ifdef vax sym_enter("vax",0,get_mem(0)); #endif #ifdef rsx sym_enter("rsx",0,get_mem(0)); #endif #ifdef rt11 sym_enter("rt11",0,get_mem(0)); #endif #endif time(&tvec[0]); /* Get encoded time and date */ tp1 = ctime(&tvec[0]); /* Convert to text string */ *(tp1+24) = '"'; /* Replace ending '\n' with a '"' */ tp2 = get_mem(27); /* Date-time string is 27 characters */ sym_enter("_DATE", 0, tp2); *tp2++ = '"'; /* Emit initial double quote */ strcpy(tp2, tp1); /* * Main driving loop of the processor */ while (readline() != EOF) { tp1 = skipblnk(lp); if (tp1[0] == '#' && tp1[1] != EOS && !in_com) { /* * A non-null directive? */ lp = skipblnk(&tp1[1]); lp = get_id(lp, name); lp = skipblnk(lp); if ((dir_kind = dir_find(name)) == ERROR) printerr("Illegal directive"); else { if (father && self) non_cond(dir_kind); cond(dir_kind); } } if (*tp1 == '#' || !(father && self)) line[0] = EOS; writeline(line); } if (!empty(&ifstack)) /* Check for dangling if's */ printerr("Unterminated #if"); #ifdef stdio fclose(outfil); #else fflush(outfil); /* Flush output file buffer */ close(outfil->fildes); /* Close output file */ #endif exits((err_cnt == 0) ? 1 : 0); } /* end main() */ /* * Process conditional compilation directives. This procedure is * always executed, regardless of whether or not we are flushing due * to a false conditional. The general technique used here is as follows. * The variables 'self' and 'father' contain the truth value of the * current if-else-endif clause and the immediately enclosing clause, * respectively. Whenever an if is encountered, both are stacked, * father &= self (to propagate any false conditionals), and * self = eval(arg). * * Upon encountering an else, self = !self, to reverse the sense of the * current condition. An endif causes the condition stack to be popped * into father and self, restoring our context to the immediately * enclosing level. * * Note: * This is a bad algorithm, as it requires a stack (and thus a maximum * nesting level). A true/false counter arraingement is to be preferred. * */ cond(dir_kind) register int dir_kind; { char symbol[MAXIDLEN+1]; /* buffer for ifdef/ifndef */ switch(dir_kind) { case IF: case IFD: case IFN: if ((push(father, &ifstack) == ERROR) || (push(self, &ifstack) == ERROR)) { printerr("Maximum #if depth exceeded"); break; } if (father &= self) switch (dir_kind) { case IF: self = (expr(lp) != 0); break; case IFD: lp = get_id(lp, symbol); self = (lookup(symbol) != NIL); break; case IFN: lp = get_id(lp, symbol); self = (lookup(symbol) == NIL); break; } else self = FALSE; break; case ELSE: if (*lp != EOS) printerr("Extraneous argument to #else"); if (empty(&ifstack)) printerr("#else without #if"); else self = !self; break; case ENDIF: if (*lp != EOS) printerr("Extraneous argument"); if (empty(&ifstack)) printerr("#endif without #if"); else { self = (int)pop(&ifstack); father = (int)pop(&ifstack); } break; } return; } /* * Non-conditional directive processing is performed here. This * procedure is executed only if we are not currently flushing. The * message and undef directives are implemented in a very straightforwar * manner. The others are a little more complex. Macro definition is * accomplished in four steps. First the name of the macro being * defined is picked up; second formal parameters are processed; third, * the actual definition is processed (and index marker substitution is * peformed), and finally, the symbol is entered into the symbol table. * Include processing goes as follows. First the path name and its * delimiter are picked up, then we attempt to find the file by * prepending the paths specified in 'srchlist[]' to it. If the file * is found, the current input file is stacked (without closing it) and * the included file is set up as the new current input file. */ non_cond(dir_kind) int dir_kind; { register int i, t, argcnt; char *defptr, *fid, *tp, delim, name[MAXIDLEN+1]; extern char *get_def(); #ifdef stdio FILE *tbp; #else struct buf *tbp; #endif switch(dir_kind) { case LINE: writeline(line); /* Do nothing */ break; case MSG: printerr(lp); break; case UNDEF: lp = get_id(lp, name); if (sym_del(name) == ERROR) printerr("Symbol not defined"); break; case DEF: lp = get_id(lp, name); if (*name == EOS) { printerr("No symbol given"); break; } if ((argcnt = formal()) == ERROR) { printerr("Illegal argument list"); break; } defptr = get_def(lp, argcnt); sym_enter(name, argcnt, defptr); break; case INCL: if (inclvl == (INCDEPTH-1)) { printerr("Maximum #include depth exceeded"); break; } #ifndef stdio tbp = get_mem(sizeof *curinfil); #endif delim = *lp; if (delim != '<' && delim != '"') { printerr("Illegal file specification delimiter"); break; } delim = (delim == '<') ? '>' : '"'; fid = ++lp; while (*lp != delim && *lp != EOS) lp++; if (*lp == EOS) { printerr("Illegal file specification"); break; } *lp = EOS; /* * At this point 'fid' points to the null-terminated * file specification. If the file specification is * enclosed in '"' then the current directory is * first searched for the specified file; if this * fails or if the file spec is delimited by '<' * and '>' then a standard list of paths is searched * (see 'srchlist' array). */ for (i = (delim == '>') ? 1 : 0; srchlist[i]; i++) { tp = concat(srchlist[i], fid); #ifdef stdio if ((tbp = fopen(tp, "r")) != NULL) break; #else if ((t = fopen(tp, tbp)) >= 0) break; #endif } #ifdef stdio if (tbp == NULL) { #else if (t < 0) { #endif printerr(" "); fprintf(stderr, "Failed to open '#include' file"); fprintf(stderr, "\"%s\" on SY:\n", fid); for (i=1;;i++) { if (srchlist[i] == NIL) break; fprintf(stderr," and '%s'",srchlist[i]); } fputs(". Sorry.\n",stderr); #ifndef stdio free(tbp); #endif break; } if (push(curinfil, &inclstk) == ERROR) screech("#include stack overflow (impossible)"); inclvl++; strcpy(fid, fname[inclvl]); lineno[inclvl] = 0; pageno[inclvl] = 1; curinfil = tbp; /* * Inhibit in front of #include line at top level */ if (inclvl == 1) inclflag = TRUE; break; } /* end switch */ return; } /* end non_cond() */ /* * Read a line of input routine -- loads 'line[]' with the next line * from the current input file. Line continuation is handled here -- * any occurrence of backslash-newline is replaced by a blank and the * next line is tacked on to the current one. * * Upon hitting end of file, the include stack is popped to restore * input to the previous level. If the stack is empty, EOF is returned * (indicating end of file at the top level. */ int readline() { register int buf_indx, curch, i; int ctn_cnt; /* Continuation line counter */ buf_indx = 0, ctn_cnt = 0; do { while ((curch = getc(curinfil)) != '\n') { if (curch == EOF) { /* Current file exhausted */ #ifdef stdio fclose(curinfil); #else close(curinfil->fildes); free(curinfil); /* Release I/O node */ #endif if (empty(&inclstk)) { /* * Don't lose xyz */ if (buf_indx > 0) break; else return(EOF); } else { curinfil = #ifdef stdio (FILE *) #else (struct buf *) #endif pop(&inclstk); inclvl--; if (!nocomp) { fprintf(outfil, #ifdef UNIMATION #ifdef PREPRE "#define pg #%d p.%d %s\npg\n", #else "#%d p.%d %s\n", #endif lineno[inclvl]+1, pageno[inclvl], fname[inclvl]); #else "#d %s\n", lineno[inclvl]+1, fname[inclvl]); #endif linefeeds = 1; } } continue; } line[buf_indx++] = curch; if (buf_indx >= LINESZ) { printerr("Next line too long, truncated."); while (curch != '\n' && curch != EOF) curch = getc(curinfil); break; } } if (line[0] == '\f') { if (buf_indx == 1) { curch = ' '; buf_indx = 0; } #ifdef UNIMATION lineno[inclvl] = 0; #else lineno[inclvl]++; /* == */ #endif pageno[inclvl]++; } else { lineno[inclvl]++; if (inclvl == 0) charincr(_line->defptr, 6); if (line[buf_indx-1] == '\\') { curch = line[buf_indx-1] = ' '; ctn_cnt++; /* Count continuations */ } } } while (curch != '\n'); /* Repeat till real end of line */ line[buf_indx] = EOS; lp = line; for (i = 0; i < ctn_cnt; i++) writeline(""); /* Keep compiler in synch */ return(OK); } /* * Write line routine -- calls expand() to process macro calls, * emitting expanded text to the output file. If we are processing * a line from an include file, a character is emitted preceding * the line as a flag to the compiler. */ writeline(lineptr) char *lineptr; { #ifdef UNIMATION if (lineno[inclvl] == 1 && !nocomp) { #ifdef PREPRE fprintf(outfil, "#define pg #1 p.%d %s\npg\n", #else fprintf(outfil, "#1 p.%d %s\n", #endif pageno[inclvl], fname[inclvl]); linefeeds = 1; } #else if (inclvl > 0 && !inclflag && !nocomp) putc(SOH, outfil); /* Flag a #include line */ inclflag = FALSE; /* Reset #include line flag */ #endif /* * Save our environment for expand() error recovery. * Then, expand the line to the output file. */ if (setjmp(jump_env) == 0) expand(lineptr, NIL, NIL, 0); putch('\n', NIL, NIL); } /* * Find a directive */ dir_find(cp) register char *cp; { register int i; for (i = 0; dir_tbl[i] != NIL; i++) if (lexeq(dir_tbl[i], cp)) return(dir_type[i]); return(ERROR); } /* * Set up definition argument list array (def_ala) with the formal * parameters of the macro currently being defined. Returns the * number of formals actually processed, or ERROR if any syntax errors * are encountered. Nested parantheses are handled here to permit * macro calls as formal parameters. */ int formal() { register int cnt; if (*lp != '(') return(0); for (cnt = 0; cnt < ALASIZE; cnt++) { lp = skipblnk(++lp); lp = get_id(lp, def_ala[cnt]); if (def_ala[cnt][0] == EOS) break; lp = skipblnk(lp); if (*lp != ',') break; } if (*lp != ')') return(ERROR); lp++; /* Skip past the ')' */ return(cnt+1); /* Make it origin 1 and return */ } /* * Set up call argument list array (call_ala) with the actual parameters * of the macro currently being expanded. Each entry of the call_ala * is dynamically allocated; the maximum length of any one actual is * determined by the compile-time constant "MAXARGSZ". All occurrences * of formal parameters are replaced by their corresponding actuals at * the next outer level of call, thereby propagating actual parameters * through nested calls. The updated source pointer is returned to the * call unless an error is encountered, in which case "ERROR" is * returned. */ char * actual(src, argcnt) register char *src; int argcnt; { register int argno, parenlvl; char *dst, *actp; int dstmax; static char arg_buf[MAXARGSZ]; static char *proto_ala[ALASIZE]; /* Call's prototype ala */ if (argcnt == 0) return(src); if (*src != '(') { printerr("Required argument(s) missing"); return(ERROR); } dstmax = arg_buf+MAXARGSZ-1; for (argno = 0; argno < argcnt && *src != ')'; argno++) { dst = arg_buf, src++, parenlvl = 0; for (;;) { src = skipq(src, &dst, dstmax); if ((*src == ')' || *src == ',') && parenlvl == 0) break; switch(*src) { case '(': parenlvl++; break; case ')': parenlvl--; break; case EOS: printerr("Unterminated argument list"); rlse_ala(argno-1); return(ERROR); } if ((*src & 0200) == 0) putch(*src++, &dst, dstmax); else { actp = call_ala[*src++ & 0177]; while (*actp != EOS) putch(*actp++, &dst, dstmax); } } if (putch(EOS, &dst, dstmax) == ERROR) { printerr("Actual parameter length exceeds maximum"); arg_buf[0] = EOS; } proto_ala[argno] = get_mem(dst - arg_buf); strcpy(proto_ala[argno], arg_buf); } if (*src++ != ')' || argno < argcnt) { printerr("Argument count error"); rlse_ala(argno); return(ERROR); } else { for (argno = 0; argno < argcnt; argno++) call_ala[argno] = proto_ala[argno]; return(src); } } /* * Get macro definition routine -- loads a dynamically-allocated buffer * with the text of the macro definition. A pointer to the resulting * definition block is passed back to the caller. A pointer to the * start of the definition to be processed is passed in 'srcp'; the * number of arguments to the macro is passed in 'numargs'. * Index markers representing the index of the formal parameter in the * ala are substituted for formals as the definition is copied. Index * markers are encoded as (0200 | ala index). Note that no buffer * limit checking is performed, because in no case will the processed * text be larger than the original source. * * Leading whitespace is discarded. 05-Dec-80 RBD */ char * get_def(src, numargs) register char *src; int numargs; { register char *p; register int ala_index; char *dst; static char defbuf[LINESZ]; char idbuf[MAXIDLEN+1]; src = skipblnk(src); /* Junk leading whitespace */ dst = defbuf; for (;;) { src = skipq(src, &dst, defbuf+LINESZ-1); if (c_alpha(*src)) { p = get_id(src, idbuf); for (ala_index = 0; ala_index < numargs; ala_index++) if (lexeq(idbuf, def_ala[ala_index])) { *dst++ = 0200 | ala_index; src = p; break; } while (src < p) *dst++ = *src++; } else if ((*dst++ = *src++) == EOS) break; } p = get_mem(dst - defbuf); strcpy(p, defbuf); return(p); } /* * Macro expansion routine -- scans text pointed to by 'srcp' for macro * calls and formal parameters (which are by now represented as index * markers (see 'get_def()') ). If a character is neither a part of a * macro call nor an index marker, it is transmitted to the output file * or buffer (see 'putch()'). Upon encountering a macro call, the * current argument list array is pushed onto the stack, the new actual * parameters are loaded, and 'expand()' is called recursively. On * return from this recursive call, the arguments are released and the * ala stack is popped. Upon encountering a formal parameter, * 'expand()' is simply called recursively to expand the actual parameter * corresponding to the formal. Note that the buffer length check is * performed only upon encountering the end of the current definition; * meanwhile excess characters are flushed. Thus only one check is * necessary and only one error is reported. To prevent cyclical * definitions, each symbol table entry has a flag which is set whenever * a call on this macro is encountered. This flag is checked for a zero * value before expansion of a macro begins; if it is set, the error * return is taken. This prevents circular definitions from causing * problems. */ int expand(src, dst, dstmax, argcnt) register char *src; char **dst, *dstmax; int argcnt; { register char *p; register struct sym *sym; char idbuf[MAXIDLEN+1]; for (;;) { src = skipq(src, dst, dstmax); if (c_alpha(*src)) { p = get_id(src, idbuf); if ((sym = lookup(idbuf)) == NIL) { while (src < p) putch(*src++, dst, dstmax); } else { src = p; if (sym->ref != 0) exp_err("Illegal recursive expansion", argcnt); else { if (push(sym, &refstack) == ERROR) exp_err("Exceeded def nest", argcnt); else sym->ref = 1; } push_ala(argcnt); if ((src = actual(src, sym->nargs)) == ERROR) exp_err(NIL, 0); expand(sym->defptr, dst, dstmax, sym->nargs); rlse_ala(sym->nargs-1); pop_ala(argcnt); } } else { if ((*src & 0200) != 0) { expand(call_ala[*src++ & 0177], dst, dstmax, argcnt); } else { if (*src == EOS) { if (!empty(&refstack)) ((struct sym *) pop(&refstack))->ref = 0; return(OK); } else if (putch(*src++, dst, dstmax) == ERROR) exp_err("expansion length exceeded", argcnt); } } } } /* * Error routine for expand(). If 'msg' is non-NIL, the message pointed * to by 'msg' is printed on the standard error output. The second * argument specifies the number of currently-active entries in the call * ala -- that is, the number of ala entries that must be freed up before * aborting. After the ala has been released, the ala stack is purged to * release the memory used by suspended calls to expand(). */ exp_err(msg, ala_cnt) register char *msg; int ala_cnt; { if (msg != NIL) printerr(msg); while (!empty(&refstack)) ((struct sym *) pop(&refstack))->ref = 0; rlse_ala(ala_cnt-1); /* Release current ala entries */ while (!empty(&callstk)) free(pop(&callstk)); /* Purge stack */ longjmp(jump_env, -1); } /* * Routine to push the contents of the call ala onto the stack. */ push_ala(numargs) register int numargs; { register int i; for (i=0; i= 0; i--) call_ala[i] = pop(&callstk); return; } #ifndef decus exits(v) int v; /* * Exit with status */ { if (v <= 1) exit(); /* No parameters */ else exit(v); /* "error message" hopefully */ } #endif -h- mpexpr.c Wed Sep 05 09:43:08 1984 MPEXPR.C;17 /* * Constant expression evaluator -- performs a standard recursive * descent parse to evaluate any legal C constant expression. All * operators without side-effects are implemented (thus ++, --, and all * assignment operators are omitted). * * The macro expansion routine is called to process the expression * before it is evaluated. All macros used in the expression must * ultimately evaluate to legal C constants. This code was originally * written by Mike Lutz and was modified by Bob Harper to fit into this * program and to conform to C language standards. */ /* * Include global constant defintions. */ #include "mpdefs.h" /* * Variables global to expr */ static char *nxtch = EOS; /* Parser scan pointer */ static char expbuf[EXPBUFSZ]; /* Buffer for expansion routine */ /* * For longjmp */ #include static jmp_buf exp_jump; expr(exptr) char *exptr; { char *dst; setjmp(exp_jump); dst = expbuf; if (setjmp(exp_jump) != 0) return (FALSE); if (expand(exptr, &dst, expbuf+EXPBUFSZ-2, 0) == ERROR) return(FALSE); *dst = EOS; nxtch = expbuf; if (setjmp(exp_jump) != 0) return (FALSE); return(eval()); } /* ::= */ eval() { register int rval; rval = query() ; if ( skipws() == EOS ) return( rval ) ; experr( "Ill-formed expression" ) ; } /* ::= | '?' ':' */ query() { register int bool, true_val, false_val ; bool = lor() ; if ( skipws() != '?' ) { ungetch() ; return( bool ) ; } true_val = query() ; if ( skipws() != ':' ) experr( "Bad query" ) ; false_val = query() ; return( bool ? true_val : false_val ) ; } /* ::= { '||' } */ lor() { register int c, vl, vr ; vl = land() ; while ( (c = skipws()) == '|' && getch() == '|' ) { vr = land() ; vl = vl || vr ; } if ( c == '|' ) ungetch() ; ungetch() ; return( vl ) ; } /* ::= { '&&' } */ land() { register int c, vl, vr ; vl = bor() ; while ( (c = skipws()) == '&' && getch() == '&' ) { vr = bor() ; vl = vl && vr ; } if ( c == '&' ) ungetch() ; ungetch() ; return( vl ) ; } /* ::= { '|' } */ bor() { register int vl, vr, c ; vl = bxor() ; while ( (c = skipws()) == '|' && getch() != '|' ) { ungetch() ; vr = bxor() ; vl |= vr ; } if ( c == '|' ) ungetch() ; ungetch() ; return( vl ) ; } /* ::= { '^' } */ bxor() { register int vl, vr ; vl = band() ; while ( skipws() == '^' ) { vr = band() ; vl ^= vr ; } ungetch() ; return( vl ) ; } /* ::= { '&' } */ band() { register int vl, vr, c ; vl = eql() ; while ( (c = skipws()) == '&' && getch() != '&' ) { ungetch() ; vr = eql() ; vl &= vr ; } if ( c == '&' ) ungetch() ; ungetch() ; return( vl ) ; } /* ::= { } */ eql() { register int vl, vr, rel ; vl = relat() ; while ( (rel = geteql()) != -1 ) { vr = relat() ; switch ( rel ) { case EQL: vl = (vl == vr) ; break ; case NEQ: vl = (vl != vr) ; break ; } } return( vl ) ; } /* ::= { } */ relat() { register int vl, vr, rel ; vl = shift() ; while ( (rel = getrel()) != -1 ) { vr = shift() ; switch ( rel ) { case LEQ: vl = (vl <= vr) ; break ; case LSS: vl = (vl < vr) ; break ; case GTR: vl = (vl > vr) ; break ; case GEQ: vl = (vl >= vr) ; break ; } } return( vl ) ; } /* ::= { } */ shift() { register int vl, vr, c ; vl = primary() ; while ( ( (c = skipws()) == '<' || c == '>' ) && c == getch() ) { vr = primary() ; if ( c == '<' ) vl <<= vr ; else vl >>= vr ; } if ( c == '<' || c == '>' ) ungetch() ; ungetch() ; return( vl ) ; } /* ::= { } */ primary() { register int c, vl, vr ; vl = term() ; while ( (c = skipws()) == '+' || c == '-' ) { vr = term() ; if ( c == '+' ) vl += vr ; else vl -= vr ; } ungetch() ; return( vl ) ; } /* := { } */ term() { register int c, vl, vr ; vl = unary() ; while ( (c = skipws()) == '*' || c == '/' || c == '%' ) { vr = unary() ; switch ( c ) { case '*': vl *= vr ; break ; case '/': vl /= vr ; break ; case '%': vl %= vr ; break ; } } ungetch() ; return( vl ) ; } /* ::= | */ unary() { register int val, c ; if ( (c = skipws()) == '!' || c == '~' || c == '-' ) { val = unary() ; switch ( c ) { case '!': return( ! val ) ; case '~': return( ~ val ) ; case '-': return( - val ) ; } } ungetch() ; return( factor() ) ; } /* ::= | '(' ')' */ factor() { register int val ; if ( skipws() == '(' ) { val = query() ; if ( skipws() != ')' ) experr( "Bad factor" ) ; return( val ) ; } ungetch() ; return( const() ) ; } /* ::= | '' */ const() { /* * Note: const() handles multi-byte constants */ register int i; register int value; register char c; int v[sizeof (int)]; if (skipws() != '\'') { ungetch(); return(num(0)); } for (i = 0; i < sizeof(int); i++) { if ((c = getch()) == '\'') { ungetch(); break; } if (c == '\\') { switch ( c = getch() ) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ungetch(); c = num(1); break; case 'n': c = 012; break; case 'r': c = 015; break; case 't': c = 011; break; case 'b': c = 010; break; case 'f': c = 014; break; } } v[i] = c; } if (i == 0 || getch() != '\'') experr("Illegal character constant"); for (value = 0; --i >= 0;) { value <<= 8; value += v[i]; } return(value); } /* ::= | */ num(flag) int flag; /* if flag is zero, the first byte selects conversion */ { register int rval, c, base; int ndig; c = skipws(); base = (flag || c == '0') ? OCTAL : DECIMAL; rval = 0 ; ndig = 0 ; while ( c >= '0' && c <= (base == OCTAL ? '7' : '9') ) { rval *= base ; rval += (c - '0') ; c = getch() ; ndig++ ; } ungetch() ; if ( ndig ) return( rval ) ; experr( "Bad constant" ) ; } /* ::= '=' | '==' | '!=' */ geteql() { register int c1, c2 ; c1 = skipws() ; c2 = getch() ; switch ( c1 ) { case '=': if ( c2 != '=' ) ungetch() ; return( EQL ) ; case '!': if ( c2 == '=' ) return( NEQ ) ; ungetch() ; ungetch() ; return( -1 ) ; default: ungetch() ; ungetch() ; return( -1 ) ; } } /* ::= '<' | '>' | '<=' | '>=' */ getrel() { register int c1, c2; c1 = skipws(); c2 = getch() ; switch ( c1 ) { case '<': if ( c2 == '=' ) return( LEQ ) ; ungetch() ; return( LSS ) ; case '>': if ( c2 == '=' ) return( GEQ ) ; ungetch() ; return( GTR ) ; default: ungetch() ; ungetch() ; return( -1 ) ; } } /* return next character from the expression string. */ getch() { return(*nxtch++); } /* Put back the last character examined. */ ungetch() { return( *--nxtch ) ; } /* Skip over any white space and return terminating char. */ skipws() { register char c; while ( (c = getch()) <= ' ' && c > EOS ) ; return( c ) ; } /* * Error handler - resets environment to eval(), prints an error, * and returns FALSE */ experr(msg) char *msg; { printerr(msg); longjmp(exp_jump, -1); /* Force eval() to return FALSE */ } -h- mpsym.c Wed Sep 05 09:43:08 1984 MPSYM.C;13 /* * Symbol table manipulation routines for mp. The general symbol table * organization is linked list representation with bucketing to * decrease search time. The buckets are determined by the first * character of the symbol (and thus 54 buckets are needed). * * If this implementation turns out to be a bottleneck, some other type * of bucket mapping function may be defined to reduce crowding. If this * fails to be adequate, then a completely different symbol table * mechanism could be tried (but remember that we need the ability to * delete symbols, which makes a simple hasing technique untenable). */ /* * Include global constant definitions */ #include "mpdefs.h" struct sym *sym_bkts[54]; /* Symbol table buckets */ /* * Symbol table lookup */ struct sym *lookup(symbol) register char *symbol; { register struct sym *ptr; for (ptr = sym_bkts[bkt_map(*symbol)]; ptr != NIL; ptr = ptr->next) if (lexeq(symbol, ptr->name)) break; return(ptr); } /* * Enter a symbol into the symbol table */ struct sym *sym_enter(symbol, nargs, defptr) char *symbol, *defptr; int nargs; { register int bucket; register struct sym *p, *tp; bucket = bkt_map(*symbol); if ((p = lookup(symbol)) != NIL) free(p->defptr); /* free old definition buffer */ else { /* * Allocate a new symbol table node */ p = get_mem(sizeof *sym_bkts[0]); p->next = NIL; if(sym_bkts[bucket] == NIL) sym_bkts[bucket] = p; else { for (tp = sym_bkts[bucket]; tp->next != NIL; tp = tp->next); tp->next = p; } } strcpy(p->name, symbol); /* Copy name into entry */ p->nargs = nargs; /* Set number of parameters */ p->defptr = defptr; /* Set ptr to definition block */ p->ref = 0; /* Clear reference flag */ return(p); } /* * Delete a symbol from the symbol table */ int sym_del(symbol) char *symbol; { register struct sym *p, *tp; register int bucket; bucket = bkt_map(*symbol); for (p = sym_bkts[bucket]; p != NIL; tp = p, p = p->next) if (lexeq(symbol, p->name)) { free(p->defptr); /* Free old definition */ if (p == sym_bkts[bucket]) sym_bkts[bucket] = p->next; else tp->next = p->next; free(p); /* And release node */ return(OK); } return(ERROR); } /* * Map an alphabetic character into a symbol table bucket index */ int bkt_map(c) register char c; { if(!c_alpha(c)) screech("Invalid call to bkt_map()"); if (c == '_') return(52); else if (c == '$') return(53); else if (c >= 'A' && c <= 'Z') return(c - 'A'); else return(c - ('a' - 26)); } /* Dump contents of symbol table */ sym_print() { register int i; register struct sym *p; register char *cp; printf("\n\nSymbol table dump follows:\n"); for (i = 0; i < 54; i++) for (p = sym_bkts[i]; p != NIL; p = p->next) { printf("%s<%d,%d>=", p->name, p->nargs, p->ref); for (cp = p->defptr; *cp != EOS; cp++) if (*cp & 0200) printf("#%c",(*cp & 0177)+'0'); else printf("%c", *cp); printf("\n"); } return; } /* Initialize symbol table buckets */ sym_init() { register int i; for (i = 0; i < 54; i++) sym_bkts[i] = NIL; return; } -h- mputil.c Wed Sep 05 09:43:08 1984 MPUTIL.C;17 /* * Utility routines for 'mp', the C macro preprocessor. */ /* * Include compile-time constants, and external and structure defintions. */ #ifdef decus #define stdio #define Digital #endif #ifdef vms #define stdio #define Digital #endif #ifdef stdio #include #else #define NULL (0) #endif #include "mpdefs.h" /* * These are defined here for error messages. */ extern int lineno[]; /* Line number stack */ extern int pageno[]; /* Page number stack */ extern char fname[][40]; /* File name stack */ extern int inclvl; /* #include depth */ extern int nocomp; /* Flag: don't do auto #LINE's */ extern int linefeeds; /* Running count of LF's in output */ extern int in_com; /* If TRUE, currently inside a comment */ /* * Print error message on standard output */ printerr(msg) char *msg; { extern int err_cnt; /* Error count */ register int i, j; for (i = 0; i <= inclvl; i++) { for (j = 0; j < i; j++) putchar('\t'); printf("Page %d, line %d %s\n", pageno[i], lineno[i], fname[i]); } printf("\t%s\n", msg); err_cnt++; return; } /* * Print diagnostic message on standard output */ diagnostic(msg) char *msg; { extern int err_cnt; /* Error count */ printerr(msg); err_cnt--; /* Undo printerr's count */ return; } /* * Internal bug catcher */ screech(msg) char *msg; { printerr("ANTI-BUG CHECK FAILURE:"); printerr(msg); abort(); } /* * Skip a string of blanks and tabs */ char *skipblnk(ptr) register char *ptr; { while (*ptr == ' ' || *ptr == '\t') ptr++; return(ptr); } /* * String comparison -- replace with "streq()" */ lexeq(cp1, cp2) register char *cp1, *cp2; { while (*cp1 != EOS && *cp2 != EOS) { if (*cp1++ != *cp2++) return(FALSE); } return(*cp1 == *cp2); } /* * get_mem - Allocate 'nbytes' of dynamic memory. If none is available, report * an error and exit with nonzero status; otherwise return pointer to * the node. */ char *get_mem(nbytes) int nbytes; { register char *p; if ((p = malloc(nbytes)) == NULL) { printerr("No dynamic memory available"); exits(4); } else return(p); } /* * Concatenate two strings, return pointer to result in local static buffer * replace with strcat(). */ char *concat(str1, str2) register char *str1; char *str2; { register int limit; register char *t; static char buffer[CATMAX]; limit = buffer+CATMAX-1; /* Buffer limit */ for (t = buffer; t <= limit && (*t = *str1) != EOS; t++, str1++) ; while (t <= limit && (*t = *str2) != EOS) { t++; str2++; } if (t > limit) screech("Insufficient space in concat()"); else return(buffer); } /* * Push an item on the stack specified */ int push(item, stkaddr) int item; register struct stack *stkaddr; { if (stkaddr->spcnt == 0) return(ERROR); stkaddr->spcnt-- , stkaddr->usecnt++; *stkaddr->stackptr++ = item; return(OK); } /* * Pop an item off of the stack */ int pop(stkaddr) register struct stack *stkaddr; { if (empty(stkaddr)) screech("Stack underflow!"); stkaddr->spcnt++, stkaddr->usecnt--; return(*--stkaddr->stackptr); } /* * Test whether a stack is empty */ int empty(stkaddr) struct stack *stkaddr; { return(stkaddr->usecnt == 0); } /* * skipq - Routine to return pointer to next unquoted character starting from * "src". All quoted text is flushed to either a text buffer or the * output file, as determined by "dst". If "dst" == NIL, quoted text * is written to "outfil"; if "dst" is non-NIL, then any bypassed * characters are moved to the buffer specified. The parameter "dstmax" * specified the last address into which a character may be deposited. * All comments are also flushed here. For multi-line comments, a * pointer to the end of line is returned. When end of comment is * encountered, scanning is resumed immediately afterward. */ int unmagic = FALSE; char *skipq(src, dst, dstmax) register char *src; char **dst, *dstmax; { register char quote; register int full; extern char *skipcom(); if (in_com) src = skipcom(src); switch(*src) { case '\\': unmagic = !unmagic; return(src); case '\'': case '"': if (unmagic) break; putch(quote = *src++, dst, dstmax); for(;;) { if (*src == EOS) { printerr("Unterminated quoted string"); break; } if (*src == '\\') { putch(*src++, dst, dstmax); unmagic = !unmagic; continue; } full = putch(*src, dst, dstmax); if (*src++ == quote && !unmagic) break; unmagic = FALSE; } return((full == ERROR) ? (char *) ERROR : src); case '/': if (*(src+1) == '*') { in_com = TRUE; return(skipcom(src+2)); } else break; } unmagic = FALSE; return(src); } /* * skipcom - Routine to skip past any comment characters, until either end of * comment or end of line. If end of comment is encountered, in_com is set to * FALSE. */ char *skipcom(src) register char *src; { while ((*src != '*' || *(src+1) != '/')) { if (*src == EOS) return(src); if ((*src == '/' && *(src+1) == '*')) diagnostic("Warning: nested comments"); src++; } src += 2; in_com = FALSE; return(src); } /* * Routine to emit a flushed character to either the current output * file or to the buffer whose address is specified. Returns ERROR if * the buffer is overrun. */ int putch(ch, where, limit) register char ch; char **where, *limit; { #ifdef stdio extern FILE *outfil; #else extern struct buf *outfil; #endif if (where == NIL) { if ((ch == '\n') & !nocomp) { if (linefeeds++ == 0) putc(ch, outfil); } else { if (linefeeds > 1) { if (linefeeds == 2) putc('\n', outfil); else { #ifdef UNIMATION #ifdef PREPRE fprintf(outfil, "#define pg #%d p.%d %s\npg\n", #else fprintf(outfil,"#%d p.%d %s\n", #endif lineno[inclvl], pageno[inclvl], fname[inclvl]); #else fprintf(outfil, "#%d %s\n", lineno[inclvl], fname[inclvl]); #endif } } linefeeds = 0; putc(ch, outfil); } return; } else { if (*where > limit) return(ERROR); else *(*where)++ = ch; return(OK); } } /* * Routine to release entries in the call_ala. The last entry to free * is specified by 'last'. */ rlse_ala(last) register int last; { register int i; extern char *call_ala[]; for(i = 0; i <= last; i++) free(call_ala[i]); return; } /* * get_id - Get an identifier (1-MAXIDLEN alphanumerics + '_', must start with * an alpha). Note that alpha's include '_' and (in Dec-land) '$'. */ char *get_id(src, dst) register char *src, *dst; { register int i; for(i = 0; i < MAXIDLEN && c_alnum(*src); i++) { *dst++ = *src++; } while (c_alnum(*src)) src++; *dst = EOS; return(src); } /* * Increment the character string representation of a number. First * argument specifies the address of the string, second argument * specifies the index in the string of the character to be incremented. * Calls itself recursively if we carry to the next digit. Wraps around * to 0 if the largest unsigned number is incremented. */ charincr(string, index) register char string[]; register int index; { if (index <= 0) return; /* Allow wrap-around */ switch (string[index]) { case ' ': string[index] = '1'; /* ' ' == '0' */ break; case '9': string[index] = '0'; charincr(string, index-1); break; default: string[index] += 1; break; } return; } c_alpha(c) register int c; /* * return(TRUE if c is a C-language alpha (A-Z,a-z, "_", "$") */ { return (isalpha(c) || c == '_' || c == '$'); } c_alnum(c) register int c; /* * Return TRUE if c is a C-language alpha (A-Z, a-z, "_", "$") * or a digit. */ { return (isalnum(c) || c == '_' || c == '$'); }