main.c revision 1.12 1 1.12 cgd /* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */
2 1.9 tls
3 1.8 glass /*-
4 1.8 glass * Copyright (c) 1989, 1993
5 1.8 glass * The Regents of the University of California. All rights reserved.
6 1.8 glass *
7 1.8 glass * This code is derived from software contributed to Berkeley by
8 1.8 glass * Ozan Yigit at York University.
9 1.8 glass *
10 1.8 glass * Redistribution and use in source and binary forms, with or without
11 1.8 glass * modification, are permitted provided that the following conditions
12 1.8 glass * are met:
13 1.8 glass * 1. Redistributions of source code must retain the above copyright
14 1.8 glass * notice, this list of conditions and the following disclaimer.
15 1.8 glass * 2. Redistributions in binary form must reproduce the above copyright
16 1.8 glass * notice, this list of conditions and the following disclaimer in the
17 1.8 glass * documentation and/or other materials provided with the distribution.
18 1.8 glass * 3. All advertising materials mentioning features or use of this software
19 1.8 glass * must display the following acknowledgement:
20 1.8 glass * This product includes software developed by the University of
21 1.8 glass * California, Berkeley and its contributors.
22 1.8 glass * 4. Neither the name of the University nor the names of its contributors
23 1.8 glass * may be used to endorse or promote products derived from this software
24 1.8 glass * without specific prior written permission.
25 1.8 glass *
26 1.8 glass * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 1.8 glass * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 1.8 glass * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 1.8 glass * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 1.8 glass * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 1.8 glass * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 1.8 glass * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 1.8 glass * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 1.8 glass * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 1.8 glass * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 1.8 glass * SUCH DAMAGE.
37 1.8 glass */
38 1.1 cgd
39 1.8 glass #ifndef lint
40 1.8 glass static char copyright[] =
41 1.8 glass "@(#) Copyright (c) 1989, 1993\n\
42 1.8 glass The Regents of the University of California. All rights reserved.\n";
43 1.8 glass #endif /* not lint */
44 1.8 glass
45 1.8 glass #ifndef lint
46 1.9 tls #if 0
47 1.8 glass static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
48 1.9 tls #else
49 1.12 cgd static char rcsid[] = "$NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $";
50 1.9 tls #endif
51 1.8 glass #endif /* not lint */
52 1.1 cgd
53 1.1 cgd /*
54 1.8 glass * main.c
55 1.8 glass * Facility: m4 macro processor
56 1.8 glass * by: oz
57 1.1 cgd */
58 1.1 cgd
59 1.8 glass #include <sys/types.h>
60 1.8 glass #include <signal.h>
61 1.8 glass #include <errno.h>
62 1.8 glass #include <unistd.h>
63 1.8 glass #include <stdio.h>
64 1.8 glass #include <ctype.h>
65 1.8 glass #include <string.h>
66 1.8 glass #include "mdef.h"
67 1.8 glass #include "stdd.h"
68 1.8 glass #include "extern.h"
69 1.8 glass #include "pathnames.h"
70 1.8 glass
71 1.8 glass ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
72 1.1 cgd char buf[BUFSIZE]; /* push-back buffer */
73 1.8 glass char *bufbase = buf; /* the base for current ilevel */
74 1.8 glass char *bbase[MAXINP]; /* the base for each ilevel */
75 1.1 cgd char *bp = buf; /* first available character */
76 1.1 cgd char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
77 1.1 cgd stae mstack[STACKMAX+1]; /* stack of m4 machine */
78 1.1 cgd char strspace[STRSPMAX+1]; /* string space for evaluation */
79 1.1 cgd char *ep = strspace; /* first free char in strspace */
80 1.1 cgd char *endest= strspace+STRSPMAX;/* end of string space */
81 1.1 cgd int sp; /* current m4 stack pointer */
82 1.1 cgd int fp; /* m4 call frame pointer */
83 1.1 cgd FILE *infile[MAXINP]; /* input file stack (0=stdin) */
84 1.1 cgd FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
85 1.1 cgd FILE *active; /* active output file pointer */
86 1.8 glass char *m4temp; /* filename for diversions */
87 1.1 cgd int ilevel = 0; /* input file stack pointer */
88 1.1 cgd int oindex = 0; /* diversion index.. */
89 1.1 cgd char *null = ""; /* as it says.. just a null.. */
90 1.1 cgd char *m4wraps = ""; /* m4wrap string default.. */
91 1.8 glass char *progname; /* name of this program */
92 1.11 pk char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
93 1.11 pk char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
94 1.11 pk char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
95 1.11 pk char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
96 1.2 glass
97 1.8 glass struct keyblk keywrds[] = { /* m4 keywords to be installed */
98 1.8 glass "include", INCLTYPE,
99 1.8 glass "sinclude", SINCTYPE,
100 1.8 glass "define", DEFITYPE,
101 1.8 glass "defn", DEFNTYPE,
102 1.8 glass "divert", DIVRTYPE,
103 1.8 glass "expr", EXPRTYPE,
104 1.8 glass "eval", EXPRTYPE,
105 1.8 glass "substr", SUBSTYPE,
106 1.8 glass "ifelse", IFELTYPE,
107 1.8 glass "ifdef", IFDFTYPE,
108 1.8 glass "len", LENGTYPE,
109 1.8 glass "incr", INCRTYPE,
110 1.8 glass "decr", DECRTYPE,
111 1.8 glass "dnl", DNLNTYPE,
112 1.8 glass "changequote", CHNQTYPE,
113 1.8 glass "changecom", CHNCTYPE,
114 1.8 glass "index", INDXTYPE,
115 1.8 glass #ifdef EXTENDED
116 1.8 glass "paste", PASTTYPE,
117 1.8 glass "spaste", SPASTYPE,
118 1.8 glass #endif
119 1.8 glass "popdef", POPDTYPE,
120 1.8 glass "pushdef", PUSDTYPE,
121 1.8 glass "dumpdef", DUMPTYPE,
122 1.8 glass "shift", SHIFTYPE,
123 1.8 glass "translit", TRNLTYPE,
124 1.8 glass "undefine", UNDFTYPE,
125 1.8 glass "undivert", UNDVTYPE,
126 1.8 glass "divnum", DIVNTYPE,
127 1.8 glass "maketemp", MKTMTYPE,
128 1.8 glass "errprint", ERRPTYPE,
129 1.8 glass "m4wrap", M4WRTYPE,
130 1.8 glass "m4exit", EXITTYPE,
131 1.8 glass "syscmd", SYSCTYPE,
132 1.8 glass "sysval", SYSVTYPE,
133 1.2 glass
134 1.10 cgd #if defined(unix) || defined(__NetBSD__)
135 1.8 glass "unix", MACRTYPE,
136 1.2 glass #else
137 1.8 glass #ifdef vms
138 1.8 glass "vms", MACRTYPE,
139 1.1 cgd #endif
140 1.1 cgd #endif
141 1.8 glass };
142 1.8 glass
143 1.8 glass #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
144 1.1 cgd
145 1.8 glass extern int optind;
146 1.8 glass extern char *optarg;
147 1.1 cgd
148 1.8 glass void macro();
149 1.8 glass void initkwds();
150 1.8 glass extern int getopt();
151 1.1 cgd
152 1.8 glass int
153 1.8 glass main(argc,argv)
154 1.8 glass int argc;
155 1.8 glass char *argv[];
156 1.8 glass {
157 1.2 glass register int c;
158 1.8 glass register int n;
159 1.8 glass char *p;
160 1.8 glass register FILE *ifp;
161 1.8 glass
162 1.8 glass progname = basename(argv[0]);
163 1.8 glass
164 1.8 glass if (signal(SIGINT, SIG_IGN) != SIG_IGN)
165 1.8 glass signal(SIGINT, onintr);
166 1.1 cgd
167 1.8 glass initkwds();
168 1.1 cgd
169 1.8 glass while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
170 1.8 glass switch(c) {
171 1.1 cgd
172 1.8 glass case 'D': /* define something..*/
173 1.8 glass for (p = optarg; *p; p++)
174 1.8 glass if (*p == '=')
175 1.8 glass break;
176 1.8 glass if (*p)
177 1.8 glass *p++ = EOS;
178 1.8 glass dodefine(optarg, p);
179 1.8 glass break;
180 1.8 glass case 'U': /* undefine... */
181 1.8 glass remhash(optarg, TOP);
182 1.8 glass break;
183 1.8 glass case 'o': /* specific output */
184 1.8 glass case '?':
185 1.8 glass usage();
186 1.2 glass }
187 1.1 cgd
188 1.8 glass argc -= optind;
189 1.8 glass argv += optind;
190 1.2 glass
191 1.8 glass active = stdout; /* default active output */
192 1.8 glass /* filename for diversions */
193 1.8 glass m4temp = mktemp(xstrdup(_PATH_DIVNAME));
194 1.2 glass
195 1.8 glass bbase[0] = bufbase;
196 1.8 glass if (!argc) {
197 1.8 glass sp = -1; /* stack pointer initialized */
198 1.8 glass fp = 0; /* frame pointer initialized */
199 1.8 glass infile[0] = stdin; /* default input (naturally) */
200 1.8 glass macro();
201 1.8 glass } else
202 1.8 glass for (; argc--; ++argv) {
203 1.8 glass p = *argv;
204 1.8 glass if (p[0] == '-' && p[1] == '\0')
205 1.8 glass ifp = stdin;
206 1.8 glass else if ((ifp = fopen(p, "r")) == NULL)
207 1.8 glass oops("%s: %s", p, strerror(errno));
208 1.8 glass sp = -1;
209 1.8 glass fp = 0;
210 1.8 glass infile[0] = ifp;
211 1.8 glass macro();
212 1.8 glass if (ifp != stdin)
213 1.8 glass (void)fclose(ifp);
214 1.8 glass }
215 1.2 glass
216 1.8 glass if (*m4wraps) { /* anything for rundown ?? */
217 1.8 glass ilevel = 0; /* in case m4wrap includes.. */
218 1.8 glass bufbase = bp = buf; /* use the entire buffer */
219 1.8 glass putback(EOF); /* eof is a must !! */
220 1.8 glass pbstr(m4wraps); /* user-defined wrapup act */
221 1.8 glass macro(); /* last will and testament */
222 1.8 glass }
223 1.2 glass
224 1.8 glass if (active != stdout)
225 1.8 glass active = stdout; /* reset output just in case */
226 1.8 glass for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */
227 1.8 glass if (outfile[n] != NULL)
228 1.8 glass getdiv(n);
229 1.8 glass /* remove bitbucket if used */
230 1.8 glass if (outfile[0] != NULL) {
231 1.8 glass (void) fclose(outfile[0]);
232 1.8 glass m4temp[UNIQUE] = '0';
233 1.8 glass #ifdef vms
234 1.8 glass (void) remove(m4temp);
235 1.8 glass #else
236 1.8 glass (void) unlink(m4temp);
237 1.2 glass #endif
238 1.8 glass }
239 1.8 glass
240 1.8 glass return 0;
241 1.8 glass }
242 1.2 glass
243 1.8 glass ndptr inspect();
244 1.1 cgd
245 1.8 glass /*
246 1.11 pk * Look ahead (at most MAXCCHARS characters) for `token'.
247 1.11 pk * (on input `t == token[0]')
248 1.11 pk * Used for comment and quoting delimiters.
249 1.11 pk * Returns 1 if `token' present; copied to output.
250 1.11 pk * 0 if `token' not found; all characters pushed back
251 1.11 pk */
252 1.11 pk int
253 1.11 pk do_look_ahead(t, token)
254 1.11 pk int t;
255 1.11 pk char *token;
256 1.11 pk {
257 1.11 pk int i;
258 1.11 pk
259 1.11 pk if (t != token[0])
260 1.11 pk oops("internal error", "");
261 1.11 pk
262 1.11 pk for (i = 1; *++token; i++) {
263 1.11 pk t = gpbc();
264 1.11 pk if (t == EOF || t != *token) {
265 1.11 pk if (t != EOF)
266 1.11 pk putback(t);
267 1.11 pk while (--i)
268 1.11 pk putback(*--token);
269 1.11 pk return 0;
270 1.11 pk }
271 1.11 pk }
272 1.11 pk return 1;
273 1.11 pk }
274 1.11 pk
275 1.11 pk #define LOOK_AHEAD(t, token) ((t)==(token)[0] && do_look_ahead(t,token))
276 1.11 pk
277 1.11 pk /*
278 1.8 glass * macro - the work horse..
279 1.8 glass */
280 1.8 glass void
281 1.8 glass macro() {
282 1.12 cgd char token[MAXTOK], chars[2];
283 1.8 glass register char *s;
284 1.8 glass register int t, l;
285 1.8 glass register ndptr p;
286 1.8 glass register int nlpar;
287 1.1 cgd
288 1.8 glass cycle {
289 1.11 pk t = gpbc();
290 1.11 pk if (t == '_' || isalpha(t)) {
291 1.8 glass putback(t);
292 1.8 glass if ((p = inspect(s = token)) == nil) {
293 1.8 glass if (sp < 0)
294 1.8 glass while (*s)
295 1.8 glass putc(*s++, active);
296 1.8 glass else
297 1.8 glass while (*s)
298 1.8 glass chrsave(*s++);
299 1.8 glass }
300 1.8 glass else {
301 1.8 glass /*
302 1.8 glass * real thing.. First build a call frame:
303 1.8 glass */
304 1.8 glass pushf(fp); /* previous call frm */
305 1.8 glass pushf(p->type); /* type of the call */
306 1.8 glass pushf(0); /* parenthesis level */
307 1.8 glass fp = sp; /* new frame pointer */
308 1.8 glass /*
309 1.8 glass * now push the string arguments:
310 1.8 glass */
311 1.8 glass pushs(p->defn); /* defn string */
312 1.8 glass pushs(p->name); /* macro name */
313 1.8 glass pushs(ep); /* start next..*/
314 1.8 glass
315 1.8 glass putback(l = gpbc());
316 1.8 glass if (l != LPAREN) { /* add bracks */
317 1.8 glass putback(RPAREN);
318 1.8 glass putback(LPAREN);
319 1.2 glass }
320 1.8 glass }
321 1.8 glass }
322 1.8 glass else if (t == EOF) {
323 1.8 glass if (sp > -1)
324 1.8 glass oops("unexpected end of input", "");
325 1.8 glass if (ilevel <= 0)
326 1.8 glass break; /* all done thanks.. */
327 1.8 glass --ilevel;
328 1.8 glass (void) fclose(infile[ilevel+1]);
329 1.8 glass bufbase = bbase[ilevel];
330 1.8 glass continue;
331 1.8 glass }
332 1.8 glass /*
333 1.12 cgd * non-alpha token possibly seen..
334 1.8 glass * [the order of else if .. stmts is important.]
335 1.8 glass */
336 1.11 pk else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
337 1.8 glass nlpar = 1;
338 1.8 glass do {
339 1.12 cgd
340 1.11 pk l = gpbc();
341 1.12 cgd if (LOOK_AHEAD(l,rquote)) {
342 1.8 glass nlpar--;
343 1.12 cgd s = rquote;
344 1.12 cgd } else if (LOOK_AHEAD(l,lquote)) {
345 1.8 glass nlpar++;
346 1.12 cgd s = lquote;
347 1.12 cgd } else if (l == EOF)
348 1.8 glass oops("missing right quote", "");
349 1.12 cgd else {
350 1.12 cgd chars[0] = l;
351 1.12 cgd chars[1] = '\0';
352 1.12 cgd s = chars;
353 1.12 cgd }
354 1.8 glass if (nlpar > 0) {
355 1.8 glass if (sp < 0)
356 1.12 cgd while (*s)
357 1.12 cgd putc(*s++, active);
358 1.8 glass else
359 1.12 cgd while (*s)
360 1.12 cgd chrsave(*s++);
361 1.2 glass }
362 1.8 glass }
363 1.8 glass while (nlpar != 0);
364 1.8 glass }
365 1.1 cgd
366 1.11 pk else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
367 1.11 pk int i;
368 1.11 pk for (i = 0; i < MAXCCHARS && scommt[i]; i++)
369 1.11 pk putc(scommt[i], active);
370 1.11 pk
371 1.11 pk for(;;) {
372 1.11 pk t = gpbc();
373 1.11 pk if (LOOK_AHEAD(t, ecommt)) {
374 1.11 pk for (i = 0; i < MAXCCHARS && ecommt[i];
375 1.11 pk i++)
376 1.11 pk putc(ecommt[i], active);
377 1.11 pk break;
378 1.11 pk }
379 1.11 pk if (t == EOF)
380 1.11 pk break;
381 1.8 glass putc(t, active);
382 1.1 cgd }
383 1.11 pk }
384 1.11 pk
385 1.11 pk else if (sp < 0) { /* not in a macro at all */
386 1.8 glass putc(t, active); /* output directly.. */
387 1.8 glass }
388 1.1 cgd
389 1.8 glass else switch(t) {
390 1.2 glass
391 1.8 glass case LPAREN:
392 1.8 glass if (PARLEV > 0)
393 1.8 glass chrsave(t);
394 1.8 glass while (isspace(l = gpbc()))
395 1.8 glass ; /* skip blank, tab, nl.. */
396 1.8 glass putback(l);
397 1.8 glass PARLEV++;
398 1.8 glass break;
399 1.1 cgd
400 1.8 glass case RPAREN:
401 1.8 glass if (--PARLEV > 0)
402 1.8 glass chrsave(t);
403 1.8 glass else { /* end of argument list */
404 1.8 glass chrsave(EOS);
405 1.8 glass
406 1.8 glass if (sp == STACKMAX)
407 1.8 glass oops("internal stack overflow", "");
408 1.8 glass
409 1.8 glass if (CALTYP == MACRTYPE)
410 1.8 glass expand((char **) mstack+fp+1, sp-fp);
411 1.8 glass else
412 1.8 glass eval((char **) mstack+fp+1, sp-fp, CALTYP);
413 1.8 glass
414 1.8 glass ep = PREVEP; /* flush strspace */
415 1.8 glass sp = PREVSP; /* previous sp.. */
416 1.8 glass fp = PREVFP; /* rewind stack...*/
417 1.8 glass }
418 1.8 glass break;
419 1.1 cgd
420 1.8 glass case COMMA:
421 1.8 glass if (PARLEV == 1) {
422 1.8 glass chrsave(EOS); /* new argument */
423 1.8 glass while (isspace(l = gpbc()))
424 1.8 glass ;
425 1.8 glass putback(l);
426 1.8 glass pushs(ep);
427 1.8 glass } else
428 1.8 glass chrsave(t);
429 1.8 glass break;
430 1.2 glass
431 1.2 glass default:
432 1.8 glass chrsave(t); /* stack the char */
433 1.8 glass break;
434 1.8 glass }
435 1.2 glass }
436 1.8 glass }
437 1.2 glass
438 1.8 glass /*
439 1.8 glass * build an input token..
440 1.8 glass * consider only those starting with _ or A-Za-z. This is a
441 1.8 glass * combo with lookup to speed things up.
442 1.8 glass */
443 1.8 glass ndptr
444 1.8 glass inspect(tp)
445 1.8 glass register char *tp;
446 1.8 glass {
447 1.8 glass register char c;
448 1.8 glass register char *name = tp;
449 1.8 glass register char *etp = tp+MAXTOK;
450 1.8 glass register ndptr p;
451 1.8 glass register unsigned long h = 0;
452 1.2 glass
453 1.8 glass while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
454 1.8 glass h = (h << 5) + h + (*tp++ = c);
455 1.8 glass putback(c);
456 1.8 glass if (tp == etp)
457 1.8 glass oops("token too long", "");
458 1.1 cgd
459 1.8 glass *tp = EOS;
460 1.1 cgd
461 1.8 glass for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
462 1.8 glass if (STREQ(name, p->name))
463 1.8 glass break;
464 1.8 glass return p;
465 1.8 glass }
466 1.7 cgd
467 1.8 glass /*
468 1.8 glass * initkwds - initialise m4 keywords as fast as possible.
469 1.8 glass * This very similar to install, but without certain overheads,
470 1.8 glass * such as calling lookup. Malloc is not used for storing the
471 1.8 glass * keyword strings, since we simply use the static pointers
472 1.8 glass * within keywrds block.
473 1.8 glass */
474 1.8 glass void
475 1.8 glass initkwds() {
476 1.8 glass register int i;
477 1.8 glass register int h;
478 1.8 glass register ndptr p;
479 1.1 cgd
480 1.8 glass for (i = 0; i < MAXKEYS; i++) {
481 1.8 glass h = hash(keywrds[i].knam);
482 1.8 glass p = (ndptr) xalloc(sizeof(struct ndblock));
483 1.8 glass p->nxtptr = hashtab[h];
484 1.8 glass hashtab[h] = p;
485 1.8 glass p->name = keywrds[i].knam;
486 1.8 glass p->defn = null;
487 1.8 glass p->type = keywrds[i].ktyp | STATIC;
488 1.1 cgd }
489 1.8 glass }
490