main.c revision 1.34 1 /* $NetBSD: main.c,v 1.34 2003/08/07 11:14:32 agc Exp $ */
2 /* $OpenBSD: main.c,v 1.51 2001/10/06 10:52:25 espie Exp $ */
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. Neither the name of the University nor the names of its contributors
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 #include <sys/cdefs.h>
37 #if defined(__COPYRIGHT) && !defined(lint)
38 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #if defined(__RCSID) && !defined(lint)
43 #if 0
44 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
45 #else
46 __RCSID("$NetBSD: main.c,v 1.34 2003/08/07 11:14:32 agc Exp $");
47 #endif
48 #endif /* not lint */
49
50 /*
51 * main.c
52 * Facility: m4 macro processor
53 * by: oz
54 */
55
56 #include <sys/types.h>
57 #include <assert.h>
58 #include <ctype.h>
59 #include <errno.h>
60 #include <signal.h>
61 #include <stddef.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include "mdef.h"
66 #include "stdd.h"
67 #include "extern.h"
68 #include "pathnames.h"
69
70 ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
71 stae *mstack; /* stack of m4 machine */
72 char *sstack; /* shadow stack, for string space extension */
73 static size_t STACKMAX; /* current maximum size of stack */
74 int sp; /* current m4 stack pointer */
75 int fp; /* m4 call frame pointer */
76 struct input_file infile[MAXINP];/* input file stack (0=stdin) */
77 FILE **outfile; /* diversion array(0=bitbucket)*/
78 int maxout;
79 FILE *active; /* active output file pointer */
80 int ilevel = 0; /* input file stack pointer */
81 int oindex = 0; /* diversion index.. */
82 char *null = ""; /* as it says.. just a null.. */
83 char *m4wraps = ""; /* m4wrap string default.. */
84 int m4prefix = 0; /* prefix keywords with m4_ */
85 char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
86 char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
87 char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
88 char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
89
90 struct keyblk keywrds[] = { /* m4 keywords to be installed */
91 { "include", INCLTYPE },
92 { "sinclude", SINCTYPE },
93 { "define", DEFITYPE },
94 { "defn", DEFNTYPE },
95 { "divert", DIVRTYPE | NOARGS },
96 { "expr", EXPRTYPE },
97 { "eval", EXPRTYPE },
98 { "substr", SUBSTYPE },
99 { "ifelse", IFELTYPE },
100 { "ifdef", IFDFTYPE },
101 { "len", LENGTYPE },
102 { "incr", INCRTYPE },
103 { "decr", DECRTYPE },
104 { "dnl", DNLNTYPE | NOARGS },
105 { "changequote", CHNQTYPE | NOARGS },
106 { "changecom", CHNCTYPE | NOARGS },
107 { "index", INDXTYPE },
108 #ifdef EXTENDED
109 { "paste", PASTTYPE },
110 { "spaste", SPASTYPE },
111 /* Newer extensions, needed to handle gnu-m4 scripts */
112 { "indir", INDIRTYPE},
113 { "builtin", BUILTINTYPE},
114 { "patsubst", PATSTYPE},
115 { "regexp", REGEXPTYPE},
116 { "esyscmd", ESYSCMDTYPE},
117 { "__file__", FILENAMETYPE | NOARGS},
118 { "__line__", LINETYPE | NOARGS},
119 #endif
120 { "popdef", POPDTYPE },
121 { "pushdef", PUSDTYPE },
122 { "dumpdef", DUMPTYPE | NOARGS },
123 { "shift", SHIFTYPE | NOARGS },
124 { "translit", TRNLTYPE },
125 { "undefine", UNDFTYPE },
126 { "undivert", UNDVTYPE | NOARGS },
127 { "divnum", DIVNTYPE | NOARGS },
128 { "maketemp", MKTMTYPE },
129 { "errprint", ERRPTYPE | NOARGS },
130 { "m4wrap", M4WRTYPE | NOARGS },
131 { "m4exit", EXITTYPE | NOARGS },
132 { "syscmd", SYSCTYPE },
133 { "sysval", SYSVTYPE | NOARGS },
134 { "traceon", TRACEONTYPE | NOARGS },
135 { "traceoff", TRACEOFFTYPE | NOARGS },
136
137 #if defined(unix) || defined(__unix__)
138 { "unix", SELFTYPE | NOARGS },
139 #else
140 #ifdef vms
141 { "vms", SELFTYPE | NOARGS },
142 #endif
143 #endif
144 };
145
146 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
147
148 extern int optind;
149 extern char *optarg;
150
151 #define MAXRECORD 50
152 static struct position {
153 char *name;
154 unsigned long line;
155 } quotes[MAXRECORD], paren[MAXRECORD];
156
157 static void record __P((struct position *, int));
158 static void dump_stack __P((struct position *, int));
159
160 static void macro __P((void));
161 static void initkwds __P((void));
162 static ndptr inspect __P((int, char *));
163 static int do_look_ahead __P((int, const char *));
164
165 static void enlarge_stack __P((void));
166
167 int main __P((int, char *[]));
168
169 int
170 main(argc,argv)
171 int argc;
172 char *argv[];
173 {
174 int c;
175 int n;
176 char *p;
177
178 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
179 signal(SIGINT, onintr);
180
181 /*
182 * We need to know if -P is there before checking -D and -U.
183 */
184 while ((c = getopt(argc, argv, "D:I:PU:d:go:t:")) != -1)
185 if (c == 'P')
186 m4prefix = 1;
187 optind = 1;
188
189 initkwds();
190 initspaces();
191 STACKMAX = INITSTACKMAX;
192
193 mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
194 sstack = (char *)xalloc(STACKMAX);
195
196 maxout = 0;
197 outfile = NULL;
198 resizedivs(MAXOUT);
199
200 while ((c = getopt(argc, argv, "D:I:PU:d:go:t:")) != -1)
201 switch (c) {
202 case 'D': /* define something..*/
203 for (p = optarg; *p; p++)
204 if (*p == '=')
205 break;
206 if (*p)
207 *p++ = EOS;
208 dodefine(optarg, p);
209 break;
210 case 'I':
211 addtoincludepath(optarg);
212 break;
213 case 'P':
214 break;
215 case 'U': /* undefine... */
216 remhash(optarg, TOP);
217 break;
218 case 'd':
219 set_trace_flags(optarg);
220 break;
221 case 'g':
222 mimic_gnu = 1;
223 break;
224 case 'o':
225 trace_file(optarg);
226 break;
227 case 't':
228 mark_traced(optarg, 1);
229 break;
230 case '?':
231 default:
232 usage(argv[0]);
233 }
234
235 argc -= optind;
236 argv += optind;
237
238 active = stdout; /* default active output */
239 bbase[0] = bufbase;
240 if (!argc) {
241 sp = -1; /* stack pointer initialized */
242 fp = 0; /* frame pointer initialized */
243 set_input(infile+0, stdin, "stdin");
244 /* default input (naturally) */
245 macro();
246 } else
247 for (; argc--; ++argv) {
248 p = *argv;
249 if (p[0] == '-' && p[1] == EOS)
250 set_input(infile, stdin, "stdin");
251 else if (fopen_trypath(infile, p) == NULL)
252 err(1, "%s", p);
253 sp = -1;
254 fp = 0;
255 macro();
256 release_input(infile);
257 }
258
259 if (*m4wraps) { /* anything for rundown ?? */
260 ilevel = 0; /* in case m4wrap includes.. */
261 bufbase = bp = buf; /* use the entire buffer */
262 pbstr(m4wraps); /* user-defined wrapup act */
263 macro(); /* last will and testament */
264 }
265
266 if (active != stdout)
267 active = stdout; /* reset output just in case */
268 for (n = 1; n < maxout; n++) /* default wrap-up: undivert */
269 if (outfile[n] != NULL)
270 getdiv(n);
271 /* remove bitbucket if used */
272 if (outfile[0] != NULL) {
273 (void) fclose(outfile[0]);
274 }
275
276 return 0;
277 }
278
279 /*
280 * Look ahead for `token'.
281 * (on input `t == token[0]')
282 * Used for comment and quoting delimiters.
283 * Returns 1 if `token' present; copied to output.
284 * 0 if `token' not found; all characters pushed back
285 */
286 static int
287 do_look_ahead(t, token)
288 int t;
289 const char *token;
290 {
291 int i;
292
293 assert((unsigned char)t == (unsigned char)token[0]);
294
295 for (i = 1; *++token; i++) {
296 t = gpbc();
297 if (t == EOF || (unsigned char)t != (unsigned char)*token) {
298 putback(t);
299 while (--i)
300 putback(*--token);
301 return 0;
302 }
303 }
304 return 1;
305 }
306
307 #define LOOK_AHEAD(t, token) (t != EOF && \
308 (unsigned char)(t)==(unsigned char)(token)[0] && \
309 do_look_ahead(t,token))
310
311 /*
312 * macro - the work horse..
313 */
314 static void
315 macro()
316 {
317 char token[MAXTOK+1];
318 int t, l;
319 ndptr p;
320 int nlpar;
321
322 cycle {
323 t = gpbc();
324 if (t == '_' || isalpha(t)) {
325 p = inspect(t, token);
326 if (p != nil)
327 putback(l = gpbc());
328 if (p == nil || (l != LPAREN &&
329 (p->type & NEEDARGS) != 0))
330 outputstr(token);
331 else {
332 /*
333 * real thing.. First build a call frame:
334 */
335 pushf(fp); /* previous call frm */
336 pushf(p->type); /* type of the call */
337 pushf(0); /* parenthesis level */
338 fp = sp; /* new frame pointer */
339 /*
340 * now push the string arguments:
341 */
342 pushs1(p->defn); /* defn string */
343 pushs1(p->name); /* macro name */
344 pushs(ep); /* start next..*/
345
346 if (l != LPAREN && PARLEV == 0) {
347 /* no bracks */
348 chrsave(EOS);
349
350 if (sp == STACKMAX)
351 errx(1, "internal stack overflow");
352 eval((const char **) mstack+fp+1, 2,
353 CALTYP);
354
355 ep = PREVEP; /* flush strspace */
356 sp = PREVSP; /* previous sp.. */
357 fp = PREVFP; /* rewind stack...*/
358 }
359 }
360 } else if (t == EOF) {
361 if (sp > -1) {
362 warnx( "unexpected end of input, unclosed parenthesis:");
363 dump_stack(paren, PARLEV);
364 exit(1);
365 }
366 if (ilevel <= 0)
367 break; /* all done thanks.. */
368 release_input(infile+ilevel--);
369 bufbase = bbase[ilevel];
370 continue;
371 }
372 /*
373 * non-alpha token possibly seen..
374 * [the order of else if .. stmts is important.]
375 */
376 else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
377 nlpar = 0;
378 record(quotes, nlpar++);
379 /*
380 * Opening quote: scan forward until matching
381 * closing quote has been found.
382 */
383 do {
384
385 l = gpbc();
386 if (LOOK_AHEAD(l,rquote)) {
387 if (--nlpar > 0)
388 outputstr(rquote);
389 } else if (LOOK_AHEAD(l,lquote)) {
390 record(quotes, nlpar++);
391 outputstr(lquote);
392 } else if (l == EOF) {
393 if (nlpar == 1)
394 warnx("unclosed quote:");
395 else
396 warnx("%d unclosed quotes:", nlpar);
397 dump_stack(quotes, nlpar);
398 exit(1);
399 } else {
400 if (nlpar > 0) {
401 if (sp < 0)
402 putc(l, active);
403 else
404 CHRSAVE(l);
405 }
406 }
407 }
408 while (nlpar != 0);
409 }
410
411 else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
412 fputs(scommt, active);
413
414 for(;;) {
415 t = gpbc();
416 if (LOOK_AHEAD(t, ecommt)) {
417 fputs(ecommt, active);
418 break;
419 }
420 if (t == EOF)
421 break;
422 putc(t, active);
423 }
424 }
425
426 else if (sp < 0) { /* not in a macro at all */
427 putc(t, active); /* output directly.. */
428 }
429
430 else switch(t) {
431
432 case LPAREN:
433 if (PARLEV > 0)
434 chrsave(t);
435 while (isspace(l = gpbc()))
436 ; /* skip blank, tab, nl.. */
437 putback(l);
438 record(paren, PARLEV++);
439 break;
440
441 case RPAREN:
442 if (--PARLEV > 0)
443 chrsave(t);
444 else { /* end of argument list */
445 chrsave(EOS);
446
447 if (sp == STACKMAX)
448 errx(1, "internal stack overflow");
449
450 eval((const char **) mstack+fp+1, sp-fp,
451 CALTYP);
452
453 ep = PREVEP; /* flush strspace */
454 sp = PREVSP; /* previous sp.. */
455 fp = PREVFP; /* rewind stack...*/
456 }
457 break;
458
459 case COMMA:
460 if (PARLEV == 1) {
461 chrsave(EOS); /* new argument */
462 while (isspace(l = gpbc()))
463 ;
464 putback(l);
465 pushs(ep);
466 } else
467 chrsave(t);
468 break;
469
470 default:
471 if (LOOK_AHEAD(t, scommt)) {
472 char *p;
473 for (p = scommt; *p; p++)
474 chrsave(*p);
475 for(;;) {
476 t = gpbc();
477 if (LOOK_AHEAD(t, ecommt)) {
478 for (p = ecommt; *p; p++)
479 chrsave(*p);
480 break;
481 }
482 if (t == EOF)
483 break;
484 CHRSAVE(t);
485 }
486 } else
487 CHRSAVE(t); /* stack the char */
488 break;
489 }
490 }
491 }
492
493 /*
494 * output string directly, without pushing it for reparses.
495 */
496 void
497 outputstr(s)
498 const char *s;
499 {
500 if (sp < 0)
501 while (*s)
502 putc(*s++, active);
503 else
504 while (*s)
505 CHRSAVE(*s++);
506 }
507
508 /*
509 * build an input token..
510 * consider only those starting with _ or A-Za-z. This is a
511 * combo with lookup to speed things up.
512 */
513 static ndptr
514 inspect(c, tp)
515 int c;
516 char *tp;
517 {
518 char *name = tp;
519 char *etp = tp+MAXTOK;
520 ndptr p;
521 unsigned int h;
522
523 h = *tp++ = c;
524
525 while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
526 h = (h << 5) + h + (*tp++ = c);
527 if (c != EOF)
528 PUTBACK(c);
529 *tp = EOS;
530 /* token is too long, it won't match anything, but it can still
531 * be output. */
532 if (tp == ep) {
533 outputstr(name);
534 while (isalnum(c = gpbc()) || c == '_') {
535 if (sp < 0)
536 putc(c, active);
537 else
538 CHRSAVE(c);
539 }
540 *name = EOS;
541 return nil;
542 }
543
544 for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
545 if (h == p->hv && STREQ(name, p->name))
546 break;
547 return p;
548 }
549
550 /*
551 * initkwds - initialise m4 keywords as fast as possible.
552 * This very similar to install, but without certain overheads,
553 * such as calling lookup. Malloc is not used for storing the
554 * keyword strings, since we simply use the static pointers
555 * within keywrds block.
556 */
557 static void
558 initkwds()
559 {
560 size_t i;
561 unsigned int h;
562 ndptr p;
563 char *k;
564
565 for (i = 0; i < MAXKEYS; i++) {
566 k = (char *)keywrds[i].knam;
567 if (m4prefix) {
568 size_t klen = strlen(k);
569 char *newk = malloc(klen + 4);
570
571 if (snprintf(newk, klen+4, "m4_%s", k) == -1)
572 err(1, "snprintf");
573 keywrds[i].knam = newk;
574 k = newk;
575 }
576 h = hash(k);
577 p = (ndptr) xalloc(sizeof(struct ndblock));
578 p->nxtptr = hashtab[h % HASHSIZE];
579 hashtab[h % HASHSIZE] = p;
580 p->name = xstrdup(keywrds[i].knam);
581 p->defn = null;
582 p->hv = h;
583 p->type = keywrds[i].ktyp & TYPEMASK;
584 if ((keywrds[i].ktyp & NOARGS) == 0)
585 p->type |= NEEDARGS;
586 }
587 }
588
589 /* Look up a builtin type, even if overridden by the user */
590 int
591 builtin_type(key)
592 const char *key;
593 {
594 int i;
595
596 for (i = 0; i != MAXKEYS; i++)
597 if (STREQ(keywrds[i].knam, key))
598 return keywrds[i].ktyp;
599 return -1;
600 }
601
602 const char *
603 builtin_realname(n)
604 int n;
605 {
606 int i;
607
608 for (i = 0; i != MAXKEYS; i++)
609 if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0)
610 return keywrds[i].knam;
611 return NULL;
612 }
613
614 static void
615 record(t, lev)
616 struct position *t;
617 int lev;
618 {
619 if (lev < MAXRECORD) {
620 t[lev].name = CURRENT_NAME;
621 t[lev].line = CURRENT_LINE;
622 }
623 }
624
625 static void
626 dump_stack(t, lev)
627 struct position *t;
628 int lev;
629 {
630 int i;
631
632 for (i = 0; i < lev; i++) {
633 if (i == MAXRECORD) {
634 fprintf(stderr, " ...\n");
635 break;
636 }
637 fprintf(stderr, " %s at line %lu\n",
638 t[i].name, t[i].line);
639 }
640 }
641
642
643 static void
644 enlarge_stack()
645 {
646 STACKMAX *= 2;
647 mstack = realloc(mstack, sizeof(stae) * STACKMAX);
648 sstack = realloc(sstack, STACKMAX);
649 if (mstack == NULL || sstack == NULL)
650 errx(1, "Evaluation stack overflow (%lu)",
651 (unsigned long)STACKMAX);
652 }
653