source: trunk/minix/commands/m4/main.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 10.9 KB
Line 
1/*
2 * main.c
3 * Facility: m4 macro processor
4 * by: oz
5 */
6
7#include "mdef.h"
8
9/*
10 * m4 - macro processor
11 *
12 * PD m4 is based on the macro tool distributed with the software
13 * tools (VOS) package, and described in the "SOFTWARE TOOLS" and
14 * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include
15 * most of the command set of SysV m4, the standard UN*X macro processor.
16 *
17 * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
18 * there may be certain implementation similarities between
19 * the two. The PD m4 was produced without ANY references to m4
20 * sources.
21 *
22 * References:
23 *
24 * Software Tools distribution: macro
25 *
26 * Kernighan, Brian W. and P. J. Plauger, SOFTWARE
27 * TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
28 *
29 * Kernighan, Brian W. and P. J. Plauger, SOFTWARE
30 * TOOLS, Addison-Wesley, Mass. 1976
31 *
32 * Kernighan, Brian W. and Dennis M. Ritchie,
33 * THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
34 * Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
35 *
36 * System V man page for M4
37 *
38 * Modification History:
39 *
40 * Jan 28 1986 Oz Break the whole thing into little
41 * pieces, for easier (?) maintenance.
42 *
43 * Dec 12 1985 Oz Optimize the code, try to squeeze
44 * few microseconds out..
45 *
46 * Dec 05 1985 Oz Add getopt interface, define (-D),
47 * undefine (-U) options.
48 *
49 * Oct 21 1985 Oz Clean up various bugs, add comment handling.
50 *
51 * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef,
52 * popdef, decr, shift etc.).
53 *
54 * June 5 1985 Oz Initial cut.
55 *
56 * Implementation Notes:
57 *
58 * [1] PD m4 uses a different (and simpler) stack mechanism than the one
59 * described in Software Tools and Software Tools in Pascal books.
60 * The triple stack nonsense is replaced with a single stack containing
61 * the call frames and the arguments. Each frame is back-linked to a
62 * previous stack frame, which enables us to rewind the stack after
63 * each nested call is completed. Each argument is a character pointer
64 * to the beginning of the argument string within the string space.
65 * The only exceptions to this are (*) arg 0 and arg 1, which are
66 * the macro definition and macro name strings, stored dynamically
67 * for the hash table.
68 *
69 * . .
70 * | . | <-- sp | . |
71 * +-------+ +-----+
72 * | arg 3 ------------------------------->| str |
73 * +-------+ | . |
74 * | arg 2 --------------+ .
75 * +-------+ |
76 * * | | |
77 * +-------+ | +-----+
78 * | plev | <-- fp +---------------->| str |
79 * +-------+ | . |
80 * | type | .
81 * +-------+
82 * | prcf -----------+ plev: paren level
83 * +-------+ | type: call type
84 * | . | | prcf: prev. call frame
85 * . |
86 * +-------+ |
87 * | <----------+
88 * +-------+
89 *
90 * [2] We have three types of null values:
91 *
92 * nil - nodeblock pointer type 0
93 * null - null string ("")
94 * NULL - Stdio-defined NULL
95 *
96 */
97
98ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
99char buf[BUFSIZE]; /* push-back buffer */
100char *bp = buf; /* first available character */
101char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
102stae mstack[STACKMAX+1]; /* stack of m4 machine */
103char strspace[STRSPMAX+1]; /* string space for evaluation */
104char *ep = strspace; /* first free char in strspace */
105char *endest= strspace+STRSPMAX;/* end of string space */
106int sp; /* current m4 stack pointer */
107int fp; /* m4 call frame pointer */
108FILE *infile[MAXINP]; /* input file stack (0=stdin) */
109FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
110FILE *active; /* active output file pointer */
111char *m4temp; /* filename for diversions */
112int ilevel = 0; /* input file stack pointer */
113int oindex = 0; /* diversion index.. */
114char *null = ""; /* as it says.. just a null.. */
115char *m4wraps = ""; /* m4wrap string default.. */
116char lquote = LQUOTE; /* left quote character (`) */
117char rquote = RQUOTE; /* right quote character (') */
118char scommt = SCOMMT; /* start character for comment */
119char ecommt = ECOMMT; /* end character for comment */
120struct keyblk keywrds[] = { /* m4 keywords to be installed */
121 "include", INCLTYPE,
122 "sinclude", SINCTYPE,
123 "define", DEFITYPE,
124 "defn", DEFNTYPE,
125 "divert", DIVRTYPE,
126 "expr", EXPRTYPE,
127 "eval", EXPRTYPE,
128 "substr", SUBSTYPE,
129 "ifelse", IFELTYPE,
130 "ifdef", IFDFTYPE,
131 "len", LENGTYPE,
132 "incr", INCRTYPE,
133 "decr", DECRTYPE,
134 "dnl", DNLNTYPE,
135 "changequote", CHNQTYPE,
136 "changecom", CHNCTYPE,
137 "index", INDXTYPE,
138#ifdef EXTENDED
139 "paste", PASTTYPE,
140 "spaste", SPASTYPE,
141#endif
142 "popdef", POPDTYPE,
143 "pushdef", PUSDTYPE,
144 "dumpdef", DUMPTYPE,
145 "shift", SHIFTYPE,
146 "translit", TRNLTYPE,
147 "undefine", UNDFTYPE,
148 "undivert", UNDVTYPE,
149 "divnum", DIVNTYPE,
150 "maketemp", MKTMTYPE,
151 "errprint", ERRPTYPE,
152 "m4wrap", M4WRTYPE,
153 "m4exit", EXITTYPE,
154#if unix || vms
155 "syscmd", SYSCTYPE,
156 "sysval", SYSVTYPE,
157#endif
158#if unix
159 "unix", MACRTYPE,
160#else
161#if vms
162 "vms", MACRTYPE,
163#endif
164#endif
165};
166
167#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
168
169extern int optind;
170extern char *optarg;
171
172int main(argc,argv)
173int argc;
174char *argv[];
175{
176 register int c;
177 register int n;
178 char *p;
179 static char divnam[] = DIVNAM;
180
181 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
182 signal(SIGINT, onintr);
183#ifdef NONZEROPAGES
184 initm4();
185#endif
186 initkwds();
187
188 while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) {
189 switch(c) {
190
191 case 'D': /* define something..*/
192 for (p = optarg; *p; p++)
193 if (*p == '=')
194 break;
195 if (*p)
196 *p++ = EOS;
197 dodefine(optarg, p);
198 break;
199 case 'U': /* undefine... */
200 remhash(optarg, TOP);
201 break;
202 case 'o': /* specific output */
203 case '?':
204 default:
205 usage();
206 }
207 }
208
209 argc -= optind;
210 argv += optind;
211
212 if(argc > 1) { usage(); }
213 infile[0] = stdin; /* default input (naturally) */
214 if(argc == 1) {
215 if(!(infile[0] = fopen(argv[0], "r"))) {
216 perror(argv[0]);
217 return 1;
218 }
219 }
220
221 active = stdout; /* default active output */
222 m4temp = mktemp(divnam); /* filename for diversions */
223
224 sp = -1; /* stack pointer initialized */
225 fp = 0; /* frame pointer initialized */
226
227 macro(); /* get some work done here */
228
229 if (*m4wraps) { /* anything for rundown ?? */
230 ilevel = 0; /* in case m4wrap includes.. */
231 putback(EOF); /* eof is a must !! */
232 pbstr(m4wraps); /* user-defined wrapup act */
233 macro(); /* last will and testament */
234 }
235 else /* default wrap-up: undivert */
236 for (n = 1; n < MAXOUT; n++)
237 if (outfile[n] != NULL)
238 getdiv(n);
239
240 /* remove bitbucket if used */
241 if (outfile[0] != NULL) {
242 (void) fclose(outfile[0]);
243 m4temp[UNIQUE] = '0';
244#if vms
245 (void) remove(m4temp);
246#else
247 (void) unlink(m4temp);
248#endif
249 }
250
251 exit(0);
252}
253
254/*
255 * macro - the work horse..
256 *
257 */
258void macro() {
259 char token[MAXTOK];
260 register char *s;
261 register int t, l;
262 register ndptr p;
263 register int nlpar;
264
265 cycle {
266 if ((t = gpbc()) == '_' || isalpha(t)) {
267 putback(t);
268 if ((p = inspect(s = token)) == nil) {
269 if (sp < 0)
270 while (*s)
271 putc(*s++, active);
272 else
273 while (*s)
274 chrsave(*s++);
275 }
276 else {
277 /*
278 * real thing.. First build a call frame:
279 *
280 */
281 pushf(fp); /* previous call frm */
282 pushf(p->type); /* type of the call */
283 pushf(0); /* parenthesis level */
284 fp = sp; /* new frame pointer */
285 /*
286 * now push the string arguments:
287 *
288 */
289 pushs(p->defn); /* defn string */
290 pushs(p->name); /* macro name */
291 pushs(ep); /* start next..*/
292
293 putback(l = gpbc());
294 if (l != LPAREN) { /* add bracks */
295 putback(RPAREN);
296 putback(LPAREN);
297 }
298 }
299 }
300 else if (t == EOF) {
301 if (sp > -1)
302 error("m4: unexpected end of input");
303 if (--ilevel < 0)
304 break; /* all done thanks.. */
305 (void) fclose(infile[ilevel+1]);
306 continue;
307 }
308 /*
309 * non-alpha single-char token seen..
310 * [the order of else if .. stmts is
311 * important.]
312 *
313 */
314 else if (t == lquote) { /* strip quotes */
315 nlpar = 1;
316 do {
317 if ((l = gpbc()) == rquote)
318 nlpar--;
319 else if (l == lquote)
320 nlpar++;
321 else if (l == EOF)
322 error("m4: missing right quote");
323 if (nlpar > 0) {
324 if (sp < 0)
325 putc(l, active);
326 else
327 chrsave(l);
328 }
329 }
330 while (nlpar != 0);
331 }
332
333 else if (sp < 0) { /* not in a macro at all */
334 if (t == scommt) { /* comment handling here */
335 putc(t, active);
336 while ((t = gpbc()) != ecommt)
337 putc(t, active);
338 }
339 putc(t, active); /* output directly.. */
340 }
341
342 else switch(t) {
343
344 case LPAREN:
345 if (PARLEV > 0)
346 chrsave(t);
347 while (isspace(l = gpbc()))
348 ; /* skip blank, tab, nl.. */
349 putback(l);
350 PARLEV++;
351 break;
352
353 case RPAREN:
354 if (--PARLEV > 0)
355 chrsave(t);
356 else { /* end of argument list */
357 chrsave(EOS);
358
359 if (sp == STACKMAX)
360 error("m4: internal stack overflow");
361
362 if (CALTYP == MACRTYPE)
363 expand((char**)mstack+fp+1,(int)sp-fp);
364 else
365 eval((char**)mstack+fp+1,sp-fp,CALTYP);
366
367 ep = PREVEP; /* flush strspace */
368 sp = PREVSP; /* previous sp.. */
369 fp = PREVFP; /* rewind stack...*/
370 }
371 break;
372
373 case COMMA:
374 if (PARLEV == 1) {
375 chrsave(EOS); /* new argument */
376 while (isspace(l = gpbc()))
377 ;
378 putback(l);
379 pushs(ep);
380 }
381 break;
382 default:
383 chrsave(t); /* stack the char */
384 break;
385 }
386 }
387}
388
389
390/*
391 * build an input token..
392 * consider only those starting with _ or A-Za-z. This is a
393 * combo with lookup to speed things up.
394 */
395ndptr
396inspect(tp)
397register char *tp;
398{
399 register int h = 0;
400 register char c;
401 register char *name = tp;
402 register char *etp = tp+MAXTOK;
403 register ndptr p;
404
405 while (tp < etp && (isalnum(c = gpbc()) || c == '_'))
406 h += (*tp++ = c);
407 putback(c);
408 if (tp == etp)
409 error("m4: token too long");
410 *tp = EOS;
411 for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
412 if (strcmp(name, p->name) == 0)
413 break;
414 return(p);
415}
416
417#ifdef NONZEROPAGES
418/*
419 * initm4 - initialize various tables. Useful only if your system
420 * does not know anything about demand-zero pages.
421 *
422 */
423void initm4()
424{
425 register int i;
426
427 for (i = 0; i < HASHSIZE; i++)
428 hashtab[i] = nil;
429 for (i = 0; i < MAXOUT; i++)
430 outfile[i] = NULL;
431}
432#endif
433
434/*
435 * initkwds - initialise m4 keywords as fast as possible.
436 * This very similar to install, but without certain overheads,
437 * such as calling lookup. Malloc is not used for storing the
438 * keyword strings, since we simply use the static pointers
439 * within keywrds block. We also assume that there is enough memory
440 * to at least install the keywords (i.e. malloc won't fail).
441 *
442 */
443void initkwds() {
444 register int i;
445 register int h;
446 register ndptr p;
447
448 for (i = 0; i < MAXKEYS; i++) {
449 h = hash(keywrds[i].knam);
450 p = (ndptr) malloc(sizeof(struct ndblock));
451 p->nxtptr = hashtab[h];
452 hashtab[h] = p;
453 p->name = keywrds[i].knam;
454 p->defn = null;
455 p->type = keywrds[i].ktyp | STATIC;
456 }
457}
Note: See TracBrowser for help on using the repository browser.