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