Annotation of src/usr.bin/m4/main.c, Revision 1.34
1.34 ! espie 1: /* $OpenBSD: main.c,v 1.33 2000/07/02 01:13:07 espie Exp $ */
1.7 deraadt 2: /* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */
1.1 deraadt 3:
4: /*-
5: * Copyright (c) 1989, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Ozan Yigit at York University.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the University of
22: * California, Berkeley and its contributors.
23: * 4. Neither the name of the University nor the names of its contributors
24: * may be used to endorse or promote products derived from this software
25: * without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37: * SUCH DAMAGE.
38: */
39:
40: #ifndef lint
41: static char copyright[] =
42: "@(#) Copyright (c) 1989, 1993\n\
43: The Regents of the University of California. All rights reserved.\n";
44: #endif /* not lint */
45:
46: #ifndef lint
47: #if 0
48: static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
49: #else
1.34 ! espie 50: static char rcsid[] = "$OpenBSD: main.c,v 1.33 2000/07/02 01:13:07 espie Exp $";
1.1 deraadt 51: #endif
52: #endif /* not lint */
53:
54: /*
55: * main.c
56: * Facility: m4 macro processor
57: * by: oz
58: */
59:
60: #include <sys/types.h>
1.21 espie 61: #include <assert.h>
1.1 deraadt 62: #include <signal.h>
63: #include <errno.h>
64: #include <unistd.h>
65: #include <stdio.h>
66: #include <ctype.h>
67: #include <string.h>
1.13 espie 68: #include <stddef.h>
1.34 ! espie 69: #include <stdlib.h>
1.11 espie 70: #include <err.h>
1.1 deraadt 71: #include "mdef.h"
72: #include "stdd.h"
73: #include "extern.h"
74: #include "pathnames.h"
75:
76: ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
1.34 ! espie 77: stae *mstack; /* stack of m4 machine */
! 78: char *sstack; /* shadow stack, for string space extension */
! 79: static size_t STACKMAX; /* current maximum size of stack */
1.1 deraadt 80: int sp; /* current m4 stack pointer */
81: int fp; /* m4 call frame pointer */
1.26 espie 82: struct input_file infile[MAXINP];/* input file stack (0=stdin) */
1.1 deraadt 83: FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
84: FILE *active; /* active output file pointer */
85: int ilevel = 0; /* input file stack pointer */
86: int oindex = 0; /* diversion index.. */
87: char *null = ""; /* as it says.. just a null.. */
88: char *m4wraps = ""; /* m4wrap string default.. */
1.2 deraadt 89: char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
90: char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
91: char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
92: char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
1.1 deraadt 93:
94: struct keyblk keywrds[] = { /* m4 keywords to be installed */
1.8 millert 95: { "include", INCLTYPE },
96: { "sinclude", SINCTYPE },
97: { "define", DEFITYPE },
98: { "defn", DEFNTYPE },
1.24 espie 99: { "divert", DIVRTYPE | NOARGS },
1.8 millert 100: { "expr", EXPRTYPE },
101: { "eval", EXPRTYPE },
102: { "substr", SUBSTYPE },
103: { "ifelse", IFELTYPE },
104: { "ifdef", IFDFTYPE },
105: { "len", LENGTYPE },
106: { "incr", INCRTYPE },
107: { "decr", DECRTYPE },
1.24 espie 108: { "dnl", DNLNTYPE | NOARGS },
109: { "changequote", CHNQTYPE | NOARGS },
110: { "changecom", CHNCTYPE | NOARGS },
1.8 millert 111: { "index", INDXTYPE },
1.1 deraadt 112: #ifdef EXTENDED
1.8 millert 113: { "paste", PASTTYPE },
114: { "spaste", SPASTYPE },
1.31 espie 115: /* Newer extensions, needed to handle gnu-m4 scripts */
116: { "indir", INDIRTYPE},
117: { "builtin", BUILTINTYPE},
118: { "patsubst", PATSTYPE},
119: { "regexp", REGEXPTYPE},
120: { "__file__", FILENAMETYPE | NOARGS},
121: { "__line__", LINETYPE | NOARGS},
1.1 deraadt 122: #endif
1.8 millert 123: { "popdef", POPDTYPE },
124: { "pushdef", PUSDTYPE },
1.24 espie 125: { "dumpdef", DUMPTYPE | NOARGS },
126: { "shift", SHIFTYPE | NOARGS },
1.8 millert 127: { "translit", TRNLTYPE },
128: { "undefine", UNDFTYPE },
1.24 espie 129: { "undivert", UNDVTYPE | NOARGS },
130: { "divnum", DIVNTYPE | NOARGS },
1.8 millert 131: { "maketemp", MKTMTYPE },
1.24 espie 132: { "errprint", ERRPTYPE | NOARGS },
133: { "m4wrap", M4WRTYPE | NOARGS },
134: { "m4exit", EXITTYPE | NOARGS },
1.8 millert 135: { "syscmd", SYSCTYPE },
1.24 espie 136: { "sysval", SYSVTYPE | NOARGS },
1.1 deraadt 137:
1.24 espie 138: #if defined(unix) || defined(__unix__)
139: { "unix", SELFTYPE | NOARGS },
1.1 deraadt 140: #else
141: #ifdef vms
1.24 espie 142: { "vms", SELFTYPE | NOARGS },
1.1 deraadt 143: #endif
144: #endif
145: };
146:
147: #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
148:
149: extern int optind;
150: extern char *optarg;
151:
1.27 espie 152: #define MAXRECORD 50
153: static struct position {
154: char *name;
155: unsigned long line;
156: } quotes[MAXRECORD], paren[MAXRECORD];
157:
158: static void record __P((struct position *, int));
159: static void dump_stack __P((struct position *, int));
160:
1.18 espie 161: static void macro __P((void));
162: static void initkwds __P((void));
1.25 espie 163: static ndptr inspect __P((char, char *));
1.18 espie 164: static int do_look_ahead __P((int, const char *));
165:
1.34 ! espie 166: static void enlarge_stack __P((void));
! 167:
1.18 espie 168: int main __P((int, char *[]));
1.1 deraadt 169:
170: int
171: main(argc,argv)
172: int argc;
173: char *argv[];
174: {
1.17 espie 175: int c;
176: int n;
1.1 deraadt 177: char *p;
1.17 espie 178: FILE *ifp;
1.1 deraadt 179:
180: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
181: signal(SIGINT, onintr);
182:
183: initkwds();
1.14 espie 184: initspaces();
1.34 ! espie 185: STACKMAX = INITSTACKMAX;
! 186:
! 187: mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
! 188: sstack = (char *)xalloc(STACKMAX);
1.1 deraadt 189:
1.32 espie 190: while ((c = getopt(argc, argv, "gtD:U:o:I:")) != -1)
1.1 deraadt 191: switch(c) {
192:
193: case 'D': /* define something..*/
194: for (p = optarg; *p; p++)
195: if (*p == '=')
196: break;
197: if (*p)
198: *p++ = EOS;
199: dodefine(optarg, p);
1.16 espie 200: break;
201: case 'I':
202: addtoincludepath(optarg);
1.1 deraadt 203: break;
204: case 'U': /* undefine... */
205: remhash(optarg, TOP);
1.32 espie 206: break;
207: case 'g':
208: mimic_gnu = 1;
1.1 deraadt 209: break;
210: case 'o': /* specific output */
211: case '?':
212: usage();
213: }
214:
215: argc -= optind;
216: argv += optind;
217:
218: active = stdout; /* default active output */
219: bbase[0] = bufbase;
220: if (!argc) {
221: sp = -1; /* stack pointer initialized */
222: fp = 0; /* frame pointer initialized */
1.26 espie 223: set_input(infile+0, stdin, "stdin");
224: /* default input (naturally) */
1.1 deraadt 225: macro();
226: } else
227: for (; argc--; ++argv) {
228: p = *argv;
1.13 espie 229: if (p[0] == '-' && p[1] == EOS)
1.26 espie 230: set_input(infile, stdin, "stdin");
231: else if (fopen_trypath(infile, p) == NULL)
1.11 espie 232: err(1, "%s", p);
1.1 deraadt 233: sp = -1;
234: fp = 0;
235: macro();
1.26 espie 236: release_input(infile);
1.1 deraadt 237: }
238:
239: if (*m4wraps) { /* anything for rundown ?? */
240: ilevel = 0; /* in case m4wrap includes.. */
241: bufbase = bp = buf; /* use the entire buffer */
242: pbstr(m4wraps); /* user-defined wrapup act */
243: macro(); /* last will and testament */
244: }
245:
246: if (active != stdout)
247: active = stdout; /* reset output just in case */
248: for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */
249: if (outfile[n] != NULL)
250: getdiv(n);
251: /* remove bitbucket if used */
252: if (outfile[0] != NULL) {
253: (void) fclose(outfile[0]);
254: }
255:
256: return 0;
257: }
258:
259: /*
1.21 espie 260: * Look ahead for `token'.
1.2 deraadt 261: * (on input `t == token[0]')
262: * Used for comment and quoting delimiters.
263: * Returns 1 if `token' present; copied to output.
264: * 0 if `token' not found; all characters pushed back
265: */
1.18 espie 266: static int
1.2 deraadt 267: do_look_ahead(t, token)
268: int t;
1.18 espie 269: const char *token;
1.2 deraadt 270: {
271: int i;
272:
1.21 espie 273: assert(t == token[0]);
1.2 deraadt 274:
275: for (i = 1; *++token; i++) {
276: t = gpbc();
277: if (t == EOF || t != *token) {
1.28 espie 278: putback(t);
1.2 deraadt 279: while (--i)
280: putback(*--token);
281: return 0;
282: }
283: }
284: return 1;
285: }
286:
287: #define LOOK_AHEAD(t, token) ((t)==(token)[0] && do_look_ahead(t,token))
288:
289: /*
1.1 deraadt 290: * macro - the work horse..
291: */
1.18 espie 292: static void
1.17 espie 293: macro()
294: {
1.34 ! espie 295: char token[MAXTOK+1];
1.17 espie 296: int t, l;
297: ndptr p;
298: int nlpar;
1.1 deraadt 299:
300: cycle {
1.2 deraadt 301: t = gpbc();
302: if (t == '_' || isalpha(t)) {
1.29 espie 303: p = inspect(t, token);
1.24 espie 304: if (p != nil)
305: putback(l = gpbc());
306: if (p == nil || (l != LPAREN &&
307: (p->type & NEEDARGS) != 0))
1.29 espie 308: outputstr(token);
1.1 deraadt 309: else {
310: /*
311: * real thing.. First build a call frame:
312: */
313: pushf(fp); /* previous call frm */
314: pushf(p->type); /* type of the call */
315: pushf(0); /* parenthesis level */
316: fp = sp; /* new frame pointer */
317: /*
318: * now push the string arguments:
319: */
1.34 ! espie 320: pushs1(p->defn); /* defn string */
! 321: pushs1(p->name); /* macro name */
! 322: pushs(ep); /* start next..*/
1.1 deraadt 323:
324: if (l != LPAREN) { /* add bracks */
325: putback(RPAREN);
326: putback(LPAREN);
327: }
328: }
329: }
330: else if (t == EOF) {
1.27 espie 331: if (sp > -1) {
332: warnx( "unexpected end of input, unclosed parenthesis:");
333: dump_stack(paren, PARLEV);
334: exit(1);
335: }
1.1 deraadt 336: if (ilevel <= 0)
337: break; /* all done thanks.. */
1.26 espie 338: release_input(infile+ilevel--);
1.1 deraadt 339: bufbase = bbase[ilevel];
340: continue;
341: }
342: /*
1.7 deraadt 343: * non-alpha token possibly seen..
1.1 deraadt 344: * [the order of else if .. stmts is important.]
345: */
1.2 deraadt 346: else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
1.27 espie 347: nlpar = 0;
348: record(quotes, nlpar++);
1.30 espie 349: /*
350: * Opening quote: scan forward until matching
351: * closing quote has been found.
352: */
1.1 deraadt 353: do {
1.7 deraadt 354:
1.2 deraadt 355: l = gpbc();
1.7 deraadt 356: if (LOOK_AHEAD(l,rquote)) {
1.29 espie 357: if (--nlpar > 0)
358: outputstr(rquote);
1.7 deraadt 359: } else if (LOOK_AHEAD(l,lquote)) {
1.27 espie 360: record(quotes, nlpar++);
1.29 espie 361: outputstr(lquote);
1.17 espie 362: } else if (l == EOF) {
363: if (nlpar == 1)
1.27 espie 364: warnx("unclosed quote:");
1.17 espie 365: else
1.27 espie 366: warnx("%d unclosed quotes:", nlpar);
367: dump_stack(quotes, nlpar);
368: exit(1);
1.17 espie 369: } else {
1.29 espie 370: if (nlpar > 0) {
371: if (sp < 0)
372: putc(l, active);
373: else
374: chrsave(l);
375: }
1.7 deraadt 376: }
1.1 deraadt 377: }
378: while (nlpar != 0);
379: }
380:
1.2 deraadt 381: else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
1.21 espie 382: fputs(scommt, active);
1.2 deraadt 383:
384: for(;;) {
385: t = gpbc();
386: if (LOOK_AHEAD(t, ecommt)) {
1.21 espie 387: fputs(ecommt, active);
1.2 deraadt 388: break;
389: }
390: if (t == EOF)
391: break;
1.1 deraadt 392: putc(t, active);
393: }
1.2 deraadt 394: }
395:
396: else if (sp < 0) { /* not in a macro at all */
1.1 deraadt 397: putc(t, active); /* output directly.. */
398: }
399:
400: else switch(t) {
401:
402: case LPAREN:
403: if (PARLEV > 0)
404: chrsave(t);
405: while (isspace(l = gpbc()))
406: ; /* skip blank, tab, nl.. */
407: putback(l);
1.27 espie 408: record(paren, PARLEV++);
1.1 deraadt 409: break;
410:
411: case RPAREN:
412: if (--PARLEV > 0)
413: chrsave(t);
414: else { /* end of argument list */
415: chrsave(EOS);
416:
417: if (sp == STACKMAX)
1.11 espie 418: errx(1, "internal stack overflow");
1.1 deraadt 419:
420: if (CALTYP == MACRTYPE)
1.18 espie 421: expand((const char **) mstack+fp+1, sp-fp);
1.1 deraadt 422: else
1.18 espie 423: eval((const char **) mstack+fp+1, sp-fp, CALTYP);
1.1 deraadt 424:
425: ep = PREVEP; /* flush strspace */
426: sp = PREVSP; /* previous sp.. */
427: fp = PREVFP; /* rewind stack...*/
428: }
429: break;
430:
431: case COMMA:
432: if (PARLEV == 1) {
433: chrsave(EOS); /* new argument */
434: while (isspace(l = gpbc()))
435: ;
436: putback(l);
437: pushs(ep);
438: } else
439: chrsave(t);
440: break;
441:
442: default:
1.22 espie 443: if (LOOK_AHEAD(t, scommt)) {
444: char *p;
445: for (p = scommt; *p; p++)
446: chrsave(*p);
447: for(;;) {
448: t = gpbc();
449: if (LOOK_AHEAD(t, ecommt)) {
450: for (p = ecommt; *p; p++)
451: chrsave(*p);
452: break;
453: }
454: if (t == EOF)
455: break;
456: chrsave(t);
457: }
458: } else
459: chrsave(t); /* stack the char */
1.1 deraadt 460: break;
461: }
462: }
463: }
464:
1.24 espie 465: /*
466: * output string directly, without pushing it for reparses.
467: */
468: void
469: outputstr(s)
470: const char *s;
471: {
472: if (sp < 0)
473: while (*s)
474: putc(*s++, active);
475: else
476: while (*s)
477: chrsave(*s++);
478: }
479:
1.1 deraadt 480: /*
481: * build an input token..
482: * consider only those starting with _ or A-Za-z. This is a
483: * combo with lookup to speed things up.
484: */
1.18 espie 485: static ndptr
1.25 espie 486: inspect(c, tp)
487: char c;
1.17 espie 488: char *tp;
1.1 deraadt 489: {
1.17 espie 490: char *name = tp;
491: char *etp = tp+MAXTOK;
492: ndptr p;
1.25 espie 493: unsigned int h;
494:
495: h = *tp++ = c;
1.1 deraadt 496:
497: while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
498: h = (h << 5) + h + (*tp++ = c);
499: putback(c);
500: *tp = EOS;
1.33 espie 501: /* token is too long, it won't match anything, but it can still
502: * be output. */
503: if (tp == ep) {
504: outputstr(name);
505: while (isalnum(c = gpbc()) || c == '_') {
506: if (sp < 0)
507: putc(c, active);
508: else
509: chrsave(c);
510: }
511: *name = EOS;
512: return nil;
513: }
1.1 deraadt 514:
1.19 espie 515: for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
516: if (h == p->hv && STREQ(name, p->name))
1.1 deraadt 517: break;
518: return p;
519: }
520:
521: /*
522: * initkwds - initialise m4 keywords as fast as possible.
523: * This very similar to install, but without certain overheads,
524: * such as calling lookup. Malloc is not used for storing the
1.17 espie 525: * keyword strings, since we simply use the static pointers
1.1 deraadt 526: * within keywrds block.
527: */
1.18 espie 528: static void
1.17 espie 529: initkwds()
530: {
531: size_t i;
1.20 millert 532: unsigned int h;
1.17 espie 533: ndptr p;
1.1 deraadt 534:
535: for (i = 0; i < MAXKEYS; i++) {
536: h = hash(keywrds[i].knam);
537: p = (ndptr) xalloc(sizeof(struct ndblock));
1.19 espie 538: p->nxtptr = hashtab[h % HASHSIZE];
539: hashtab[h % HASHSIZE] = p;
1.1 deraadt 540: p->name = keywrds[i].knam;
541: p->defn = null;
1.19 espie 542: p->hv = h;
1.24 espie 543: p->type = (keywrds[i].ktyp & TYPEMASK) | STATIC;
544: if ((keywrds[i].ktyp & NOARGS) == 0)
545: p->type |= NEEDARGS;
1.1 deraadt 546: }
547: }
1.31 espie 548:
549: /* Look up a builtin type, even if overridden by the user */
550: int
551: builtin_type(key)
552: const char *key;
553: {
554: int i;
555:
556: for (i = 0; i != MAXKEYS; i++)
557: if (STREQ(keywrds[i].knam, key))
558: return keywrds[i].ktyp;
559: return -1;
560: }
561:
1.17 espie 562:
1.27 espie 563: static void
564: record(t, lev)
565: struct position *t;
566: int lev;
567: {
568: if (lev < MAXRECORD) {
569: t[lev].name = CURRENT_NAME;
570: t[lev].line = CURRENT_LINE;
571: }
572: }
573:
574: static void
575: dump_stack(t, lev)
576: struct position *t;
577: int lev;
578: {
579: int i;
580:
581: for (i = 0; i < lev; i++) {
582: if (i == MAXRECORD) {
583: fprintf(stderr, " ...\n");
584: break;
585: }
586: fprintf(stderr, " %s at line %lu\n",
587: t[i].name, t[i].line);
588: }
1.34 ! espie 589: }
! 590:
! 591:
! 592: static void
! 593: enlarge_stack()
! 594: {
! 595: STACKMAX *= 2;
! 596: fprintf(stderr, "%u\n", STACKMAX);
! 597: mstack = realloc(mstack, sizeof(stae) * STACKMAX);
! 598: sstack = realloc(sstack, STACKMAX);
! 599: if (mstack == NULL || sstack == NULL)
! 600: errx(1, "Evaluation stack overflow (%lu)",
! 601: (unsigned long)STACKMAX);
1.27 espie 602: }