Commit | Line | Data |
---|---|---|
78ed81a3 | 1 | /* File : main.c |
2 | Author : Ozan Yigit | |
3 | Updated: 4 May 1992 | |
4 | Defines: M4 macro processor. | |
5 | */ | |
15637ed4 | 6 | |
78ed81a3 | 7 | #include "mdef.h" |
8 | #include "extr.h" | |
9 | #include "ourlims.h" | |
15637ed4 | 10 | |
78ed81a3 | 11 | char chtype[1 - EOF + UCHAR_MAX]; |
15637ed4 | 12 | |
78ed81a3 | 13 | #define is_sym1(c) (chtype[(c)-EOF] > 10) |
14 | #define is_sym2(c) (chtype[(c)-EOF] > 0) | |
15 | #define is_blnk(c) ((unsigned)((c)-1) < ' ') | |
15637ed4 RG |
16 | |
17 | /* | |
18 | * m4 - macro processor | |
19 | * | |
20 | * PD m4 is based on the macro tool distributed with the software | |
21 | * tools (VOS) package, and described in the "SOFTWARE TOOLS" and | |
22 | * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include | |
23 | * most of the command set of SysV m4, the standard UN*X macro processor. | |
24 | * | |
25 | * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro, | |
26 | * there may be certain implementation similarities between | |
27 | * the two. The PD m4 was produced without ANY references to m4 | |
28 | * sources. | |
29 | * | |
30 | * References: | |
31 | * | |
32 | * Software Tools distribution: macro | |
33 | * | |
34 | * Kernighan, Brian W. and P. J. Plauger, SOFTWARE | |
35 | * TOOLS IN PASCAL, Addison-Wesley, Mass. 1981 | |
36 | * | |
37 | * Kernighan, Brian W. and P. J. Plauger, SOFTWARE | |
38 | * TOOLS, Addison-Wesley, Mass. 1976 | |
39 | * | |
40 | * Kernighan, Brian W. and Dennis M. Ritchie, | |
41 | * THE M4 MACRO PROCESSOR, Unix Programmer's Manual, | |
42 | * Seventh Edition, Vol. 2, Bell Telephone Labs, 1979 | |
43 | * | |
44 | * System V man page for M4 | |
45 | * | |
46 | * Modification History: | |
47 | * | |
78ed81a3 | 48 | * Mar 26 1992 RAOK 1. Eliminated magic numbers 8, 255, 256 in favour |
49 | * of the standard limits CHAR_BIT, UCHAR_MAX, which | |
50 | * are in the new header ourlims.h. This is part of | |
51 | * the "8-bit-clean M4" project. To the best of my | |
52 | * belief, all of the code should work in EBCDIC, | |
53 | * ASCII, DEC MNCS, ISO 8859/n, or the Mac character | |
54 | * set, as long as chars are unsigned. There are | |
55 | * still some places where signed bytes can cause | |
56 | * trouble. | |
57 | * | |
58 | * 2. Changed expr() to use long int rather than int. | |
59 | * This is so that we'd get 32-bit arithmetic on a Sun, | |
60 | * Encore, PC, Mac &c. As part of this, the code for | |
61 | * shifts has been elaborated to yield signed shifts | |
62 | * on all machines. The charcon() function didn't work | |
63 | * with multi-character literals, although it was meant | |
64 | * to. Now it does. pbrad() has been changed so that | |
65 | * eval('abcd',0) => abcd, not dcba, which was useless. | |
66 | * | |
67 | * 3. I finally got sick of the fact that &&, ||, and | |
68 | * ?: always evaluate all their arguments. This is | |
69 | * consistent with UNIX System V Release 3, but I for | |
70 | * one don't see anything to gain by having eval(0&&1/0) | |
71 | * crash when it would simply yield 0 in C. Now these | |
72 | * operators are more consistent with the C preprocessor. | |
73 | * | |
74 | * Nov 13 1992 RAOK Added the quoter facility. The purpose of this is | |
75 | * to make it easier to generate data for a variety of | |
76 | * programming languages, including sh, awk, Lisp, C. | |
77 | * There are two holes in the implementation: dumpdef | |
78 | * prints junk and undefine doesn't release everything. | |
79 | * This was mainly intended as a prototype to show that | |
80 | * it could be done. | |
81 | * | |
82 | * Jun 16 1992 RAOK Added vquote and gave changequote a 3rd argument. | |
83 | * The idea of this is to make it possible to quote | |
84 | * ANY string, including one with unbalanced ` or '. | |
85 | * I also made eval(c,0) convert decimal->ASCII, so | |
86 | * that eval(39,0) yields ' and eval(96,0) yields `. | |
87 | * | |
88 | * Apr 28 1992 RAOK Used gcc to find and fix ANSI clashes, so that | |
89 | * PD M4 could be ported to MS-DOS (Turbo C 3). | |
90 | * Main known remaining problem: use of mktemp(). | |
91 | * Also, command line handling needs to be worked out. | |
92 | * | |
93 | * Mar 26 1992 RAOK PD M4 now accepts file names on the command line | |
94 | * just like UNIX M4. Warning: macro calls must NOT | |
95 | * cross file boundaries. UNIX M4 doesn't mind; | |
96 | * (m4 a b c) and (cat a b c | m4) are just the same | |
97 | * except for error messages. PD M4 will report an | |
98 | * unexpected EOF if a file ends while a macro call or | |
99 | * string is still being parsed. When there is one | |
100 | * file name argument, or none, you can't tell the | |
101 | * difference, and that's all I need. | |
102 | * | |
103 | * May 15 1991 RAOK DIVNAM was a string constant, but was changed! | |
104 | * Fixed that and a couple of other things to make | |
105 | * GCC happy. (Also made "foo$bar" get through.) | |
106 | * | |
107 | * Apr 17 1991 RAOK There was a major mistake. If you did | |
108 | * define(foo, `1 include(bar) 2') where | |
109 | * file bar held "-bar-" you would naturally | |
110 | * expect "1 -bar- 2" as the output, but you | |
111 | * got "1 2-bar-". That is, include file | |
112 | * processing was postponed until all macros | |
113 | * had been expanded. The macro gpbc() was | |
114 | * at fault. I added bb, bbstack[], and the | |
115 | * code in main.c and serv.c that maintains | |
116 | * them, in order to work around this bug. | |
117 | * | |
118 | * Apr 12 1991 RAOK inspect() didn't handle overflow well. | |
119 | * Added the automatically maintained macro | |
120 | * __FILE__, just as in C. To suppress it, | |
121 | * define NO__FILE. At some point, $# had | |
122 | * been made to return a value that was off | |
123 | * by one; it now agrees with SysV M4. | |
124 | * | |
125 | * Aug 13 1990 RAOK The System V expr() has three arguments: | |
126 | * expression [, radix:10 [, mindigits: 1]] | |
127 | * Brought in my int2str() and wrote pbrad() | |
128 | * to make this work here. With the wrong # | |
129 | * of args, acts like System V. | |
130 | * | |
131 | * Aug 11 1990 RAOK Told expr.c about the Pascal operators | |
132 | * not, div, mod, and, or | |
133 | * so that Pascal constant expressions could | |
134 | * be evaluated. (It still doesn't handle | |
135 | * floats.) Fixed a mistake in 'character's. | |
136 | * | |
137 | * Apr 23 1988 RAOK Sped it up, mainly by making putback() and | |
138 | * chrsave() into macros. | |
139 | * Finished the -o option (was half done). | |
140 | * Added the System V -e (interactive) option. | |
141 | * | |
15637ed4 RG |
142 | * Jan 28 1986 Oz Break the whole thing into little |
143 | * pieces, for easier (?) maintenance. | |
144 | * | |
145 | * Dec 12 1985 Oz Optimize the code, try to squeeze | |
78ed81a3 | 146 | * few microseconds out.. [didn't try very hard] |
15637ed4 RG |
147 | * |
148 | * Dec 05 1985 Oz Add getopt interface, define (-D), | |
149 | * undefine (-U) options. | |
150 | * | |
151 | * Oct 21 1985 Oz Clean up various bugs, add comment handling. | |
152 | * | |
153 | * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef, | |
154 | * popdef, decr, shift etc.). | |
155 | * | |
156 | * June 5 1985 Oz Initial cut. | |
157 | * | |
158 | * Implementation Notes: | |
159 | * | |
160 | * [1] PD m4 uses a different (and simpler) stack mechanism than the one | |
161 | * described in Software Tools and Software Tools in Pascal books. | |
162 | * The triple stack nonsense is replaced with a single stack containing | |
163 | * the call frames and the arguments. Each frame is back-linked to a | |
164 | * previous stack frame, which enables us to rewind the stack after | |
165 | * each nested call is completed. Each argument is a character pointer | |
166 | * to the beginning of the argument string within the string space. | |
167 | * The only exceptions to this are (*) arg 0 and arg 1, which are | |
168 | * the macro definition and macro name strings, stored dynamically | |
169 | * for the hash table. | |
170 | * | |
171 | * . . | |
172 | * | . | <-- sp | . | | |
173 | * +-------+ +-----+ | |
174 | * | arg 3 ------------------------------->| str | | |
175 | * +-------+ | . | | |
176 | * | arg 2 --------------+ . | |
177 | * +-------+ | | |
178 | * * | | | | |
179 | * +-------+ | +-----+ | |
180 | * | plev | <-- fp +---------------->| str | | |
181 | * +-------+ | . | | |
182 | * | type | . | |
183 | * +-------+ | |
184 | * | prcf -----------+ plev: paren level | |
185 | * +-------+ | type: call type | |
186 | * | . | | prcf: prev. call frame | |
187 | * . | | |
188 | * +-------+ | | |
189 | * | <----------+ | |
190 | * +-------+ | |
191 | * | |
192 | * [2] We have three types of null values: | |
193 | * | |
194 | * nil - nodeblock pointer type 0 | |
195 | * null - null string ("") | |
196 | * NULL - Stdio-defined NULL | |
197 | * | |
198 | */ | |
199 | ||
15637ed4 RG |
200 | char buf[BUFSIZE]; /* push-back buffer */ |
201 | char *bp = buf; /* first available character */ | |
78ed81a3 | 202 | char *bb = buf; /* buffer beginning */ |
15637ed4 RG |
203 | char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ |
204 | stae mstack[STACKMAX+1]; /* stack of m4 machine */ | |
205 | char strspace[STRSPMAX+1]; /* string space for evaluation */ | |
206 | char *ep = strspace; /* first free char in strspace */ | |
207 | char *endest= strspace+STRSPMAX;/* end of string space */ | |
208 | int sp; /* current m4 stack pointer */ | |
209 | int fp; /* m4 call frame pointer */ | |
78ed81a3 | 210 | char *bbstack[MAXINP]; /* stack where bb is saved */ |
15637ed4 RG |
211 | FILE *infile[MAXINP]; /* input file stack (0=stdin) */ |
212 | FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ | |
213 | FILE *active; /* active output file pointer */ | |
15637ed4 RG |
214 | int ilevel = 0; /* input file stack pointer */ |
215 | int oindex = 0; /* diversion index.. */ | |
216 | char *null = ""; /* as it says.. just a null.. */ | |
217 | char *m4wraps = ""; /* m4wrap string default.. */ | |
218 | char lquote = LQUOTE; /* left quote character (`) */ | |
219 | char rquote = RQUOTE; /* right quote character (') */ | |
78ed81a3 | 220 | char vquote = VQUOTE; /* verbatim quote character ^V */ |
15637ed4 RG |
221 | char scommt = SCOMMT; /* start character for comment */ |
222 | char ecommt = ECOMMT; /* end character for comment */ | |
78ed81a3 | 223 | int strip = 0; /* throw away comments? */ |
224 | ||
225 | /* Definitions of diversion files. The last 6 characters MUST be | |
226 | "XXXXXX" -- that is a requirement of mktemp(). The character | |
227 | '0' is to be replaced by the diversion number; we assume here | |
228 | that it is just before the Xs. If not, you will have to alter | |
229 | the definition of UNIQUE. | |
230 | */ | |
231 | ||
232 | #if unix | |
233 | #include <sys/param.h> | |
234 | #ifdef BSD | |
235 | #include <paths.h> | |
236 | #if __STDC__ | |
237 | static char DIVNAM[] = _PATH_VARTMP "m40XXXXXX"; | |
238 | #else | |
239 | static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; | |
15637ed4 | 240 | #endif |
78ed81a3 | 241 | #else |
242 | static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; | |
15637ed4 | 243 | #endif |
78ed81a3 | 244 | #else |
245 | #if vms | |
246 | static char DIVNAM[] = "sys$login:m40XXXXXX"; | |
247 | #else | |
248 | static char DIVNAM[] = "M40XXXXXX"; /* was \M4, should it be \\M4? */ | |
249 | #endif | |
250 | #endif | |
251 | int UNIQUE = sizeof DIVNAM - 7; /* where to change m4temp. */ | |
252 | char *m4temp; /* filename for diversions */ | |
253 | extern char *mktemp(); | |
15637ed4 | 254 | |
15637ed4 | 255 | |
78ed81a3 | 256 | void cantread(s) |
257 | char *s; | |
258 | { | |
259 | fprintf(stderr, "m4: %s: ", s); | |
260 | error("cannot open for input."); | |
261 | } | |
15637ed4 | 262 | |
15637ed4 | 263 | |
78ed81a3 | 264 | /* initkwds() |
265 | initialises the hash table to contain all the m4 built-in functions. | |
266 | The original version breached module boundaries, but there did not | |
267 | seem to be any benefit in that. | |
268 | */ | |
269 | static void initkwds() | |
270 | { | |
271 | register int i; | |
272 | static struct { char *name; int type; } keyword[] = | |
273 | { | |
274 | "include", INCLTYPE, | |
275 | "sinclude", SINCTYPE, | |
276 | "define", DEFITYPE, | |
277 | "defn", DEFNTYPE, | |
278 | "divert", DIVRTYPE, | |
279 | "expr", EXPRTYPE, | |
280 | "eval", EXPRTYPE, | |
281 | "substr", SUBSTYPE, | |
282 | "ifelse", IFELTYPE, | |
283 | "ifdef", IFDFTYPE, | |
284 | "len", LENGTYPE, | |
285 | "incr", INCRTYPE, | |
286 | "decr", DECRTYPE, | |
287 | "dnl", DNLNTYPE, | |
288 | "changequote", CHNQTYPE, | |
289 | "changecom", CHNCTYPE, | |
290 | "index", INDXTYPE, | |
291 | #ifdef EXTENDED | |
292 | "paste", PASTTYPE, | |
293 | "spaste", SPASTYPE, | |
294 | "m4trim", TRIMTYPE, | |
295 | "defquote", DEFQTYPE, | |
296 | #endif | |
297 | "popdef", POPDTYPE, | |
298 | "pushdef", PUSDTYPE, | |
299 | "dumpdef", DUMPTYPE, | |
300 | "shift", SHIFTYPE, | |
301 | "translit", TRNLTYPE, | |
302 | "undefine", UNDFTYPE, | |
303 | "undivert", UNDVTYPE, | |
304 | "divnum", DIVNTYPE, | |
305 | "maketemp", MKTMTYPE, | |
306 | "errprint", ERRPTYPE, | |
307 | "m4wrap", M4WRTYPE, | |
308 | "m4exit", EXITTYPE, | |
309 | #if unix || vms | |
310 | "syscmd", SYSCTYPE, | |
311 | "sysval", SYSVTYPE, | |
312 | #endif | |
313 | #if unix | |
314 | "unix", MACRTYPE, | |
315 | #else | |
316 | #if vms | |
317 | "vms", MACRTYPE, | |
318 | #endif | |
319 | #endif | |
320 | (char*)0, 0 | |
321 | }; | |
322 | ||
323 | for (i = 0; keyword[i].type != 0; i++) | |
324 | addkywd(keyword[i].name, keyword[i].type); | |
325 | } | |
326 | ||
327 | ||
328 | /* inspect(Name) | |
329 | Build an input token.., considering only those which start with | |
330 | [A-Za-z_]. This is fused with lookup() to speed things up. | |
331 | name must point to an array of at least MAXTOK characters. | |
332 | */ | |
333 | ndptr inspect(name) | |
334 | char *name; | |
335 | { | |
336 | register char *tp = name; | |
337 | register char *etp = name+(MAXTOK-1); | |
338 | register int c; | |
339 | register unsigned long h = 0; | |
340 | register ndptr p; | |
15637ed4 | 341 | |
78ed81a3 | 342 | while (is_sym2(c = gpbc())) { |
343 | if (tp == etp) error("m4: token too long"); | |
344 | *tp++ = c, h = (h << 5) + h + c; | |
15637ed4 | 345 | } |
78ed81a3 | 346 | putback(c); |
347 | *tp = EOS; | |
348 | for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) | |
349 | if (strcmp(name, p->name) == 0) | |
350 | return p; | |
351 | return nil; | |
352 | } | |
15637ed4 | 353 | |
15637ed4 RG |
354 | |
355 | /* | |
356 | * macro - the work horse.. | |
357 | * | |
358 | */ | |
78ed81a3 | 359 | void macro() |
360 | { | |
15637ed4 | 361 | char token[MAXTOK]; |
78ed81a3 | 362 | register int t; |
363 | register FILE *op = active; | |
364 | static char ovmsg[] = "m4: internal stack overflow"; | |
365 | ||
366 | for (;;) { | |
367 | t = gpbc(); | |
368 | if (is_sym1(t)) { | |
369 | register char *s; | |
370 | register ndptr p; | |
371 | ||
372 | putback(t); | |
373 | if ((p = inspect(s = token)) == nil) { | |
374 | if (sp < 0) { | |
375 | while (t = *s++) putc(t, op); | |
376 | } else { | |
377 | while (t = *s++) chrsave(t); | |
378 | } | |
379 | } else { | |
380 | /* real thing.. First build a call frame */ | |
381 | if (sp >= STACKMAX-6) error(ovmsg); | |
382 | mstack[1+sp].sfra = fp; /* previous call frm */ | |
383 | mstack[2+sp].sfra = p->type; /* type of the call */ | |
384 | mstack[3+sp].sfra = 0; /* parenthesis level */ | |
385 | fp = sp+3; /* new frame pointer */ | |
386 | /* now push the string arguments */ | |
387 | mstack[4+sp].sstr = p->defn; /* defn string */ | |
388 | mstack[5+sp].sstr = p->name; /* macro name */ | |
389 | mstack[6+sp].sstr = ep; /* start next.. */ | |
390 | sp += 6; | |
391 | ||
392 | t = gpbc(); | |
393 | putback(t); | |
394 | if (t != LPAREN) { putback(RPAREN); putback(LPAREN); } | |
395 | } | |
396 | } else | |
397 | if (t == EOF) { | |
398 | if (sp >= 0) error("m4: unexpected end of input"); | |
399 | if (--ilevel < 0) break; /* all done thanks */ | |
400 | #ifndef NO__FILE | |
401 | remhash("__FILE__", TOP); | |
402 | #endif | |
403 | bb = bbstack[ilevel+1]; | |
404 | (void) fclose(infile[ilevel+1]); | |
405 | } else | |
406 | /* non-alpha single-char token seen.. | |
407 | [the order of else if .. stmts is important.] | |
408 | */ | |
409 | if (t == lquote) { /* strip quotes */ | |
410 | register int nlpar; | |
411 | ||
412 | for (nlpar = 1; ; ) { | |
413 | t = gpbc(); | |
414 | if (t == rquote) { | |
415 | if (--nlpar == 0) break; | |
416 | } else | |
417 | if (t == lquote) { | |
418 | nlpar++; | |
419 | } else { | |
420 | if (t == vquote) t = gpbc(); | |
421 | if (t == EOF) { | |
422 | error("m4: missing right quote"); | |
15637ed4 | 423 | } |
78ed81a3 | 424 | } |
425 | if (sp < 0) { | |
426 | putc(t, op); | |
427 | } else { | |
428 | chrsave(t); | |
429 | } | |
15637ed4 | 430 | } |
78ed81a3 | 431 | } else |
432 | if (sp < 0) { /* not in a macro at all */ | |
433 | if (t != scommt) { /* not a comment, so */ | |
434 | putc(t, op); /* copy it to output */ | |
435 | } else | |
436 | if (strip) { /* discard a comment */ | |
437 | do { | |
438 | t = gpbc(); | |
439 | } while (t != ecommt && t != EOF); | |
440 | } else { /* copy comment to output */ | |
441 | do { | |
442 | putc(t, op); | |
443 | t = gpbc(); | |
444 | } while (t != ecommt && t != EOF); | |
445 | putc(t, op); | |
446 | /* A note on comment handling: this is NOT robust. | |
447 | | We should do something safe with comments that | |
448 | | are missing their ecommt termination. | |
449 | */ | |
15637ed4 | 450 | } |
78ed81a3 | 451 | } else |
452 | switch (t) { | |
453 | /* There is a peculiar detail to notice here. | |
454 | Layout is _always_ discarded after left parentheses, | |
455 | but it is only discarded after commas if they separate | |
456 | arguments. For example, | |
457 | define(foo,`|$1|$2|') | |
458 | foo( a, b) => |a|b| | |
459 | foo(( a ), ( b )) => |(a )|(b )| | |
460 | foo((a, x), (b, y)) => |(a, x)|(b, y)| | |
461 | I find this counter-intuitive, and would expect the code | |
462 | for LPAREN to read something like this: | |
463 | ||
464 | if (PARLEV == 0) { | |
465 | (* top level left parenthesis: skip layout *) | |
466 | do t = gpbc(); while (is_blnk(t)); | |
467 | putback(t); | |
468 | } else { | |
469 | (* left parenthesis inside an argument *) | |
470 | chrsave(t); | |
471 | } | |
472 | PARLEV++; | |
473 | ||
474 | However, it turned out that Oz wrote the actual code | |
475 | very carefully to mimic the behaviour of "real" m4; | |
476 | UNIX m4 really does skip layout after all left parens | |
477 | but only some commas in just this fashion. Sigh. | |
478 | */ | |
479 | case LPAREN: | |
480 | if (PARLEV > 0) chrsave(t); | |
481 | do t = gpbc(); while (is_blnk(t)); /* skip layout */ | |
482 | putback(t); | |
483 | PARLEV++; | |
484 | break; | |
485 | ||
486 | case COMMA: | |
487 | if (PARLEV == 1) { | |
488 | chrsave(EOS); /* new argument */ | |
489 | if (sp >= STACKMAX) error(ovmsg); | |
490 | do t = gpbc(); while (is_blnk(t)); /* skip layout */ | |
491 | putback(t); | |
492 | mstack[++sp].sstr = ep; | |
493 | } else { | |
494 | chrsave(t); | |
495 | } | |
496 | break; | |
497 | ||
498 | case RPAREN: | |
499 | if (--PARLEV > 0) { | |
500 | chrsave(t); | |
501 | } else { | |
502 | char **argv = (char **)(mstack+fp+1); | |
503 | int argc = sp-fp; | |
504 | #if unix | vms | |
505 | static int sysval; | |
506 | #endif | |
507 | ||
508 | chrsave(EOS); /* last argument */ | |
509 | if (sp >= STACKMAX) error(ovmsg); | |
510 | #ifdef DEBUG | |
511 | fprintf(stderr, "argc = %d\n", argc); | |
512 | for (t = 0; t < argc; t++) | |
513 | fprintf(stderr, "argv[%d] = %s\n", t, argv[t]); | |
514 | #endif | |
515 | /* If argc == 3 and argv[2] is null, then we | |
516 | have a call like `macro_or_builtin()'. We | |
517 | adjust argc to avoid further checking.. | |
518 | */ | |
519 | if (argc == 3 && !argv[2][0]) argc--; | |
520 | ||
521 | switch (CALTYP & ~STATIC) { | |
522 | case MACRTYPE: | |
523 | expand(argv, argc); | |
524 | break; | |
525 | ||
526 | case DEFITYPE: /* define(..) */ | |
527 | for (; argc > 2; argc -= 2, argv += 2) | |
528 | dodefine(argv[2], argc > 3 ? argv[3] : null); | |
529 | break; | |
530 | ||
531 | case PUSDTYPE: /* pushdef(..) */ | |
532 | for (; argc > 2; argc -= 2, argv += 2) | |
533 | dopushdef(argv[2], argc > 3 ? argv[3] : null); | |
534 | break; | |
535 | ||
536 | case DUMPTYPE: | |
537 | dodump(argv, argc); | |
538 | break; | |
539 | ||
540 | case EXPRTYPE: /* eval(Expr) */ | |
541 | { /* evaluate arithmetic expression */ | |
542 | /* eval([val: 0[, radix:10 [,min: 1]]]) */ | |
543 | /* excess arguments are ignored */ | |
544 | /* eval() with no arguments returns 0 */ | |
545 | /* this is based on V.3 behaviour */ | |
546 | int min_digits = 1; | |
547 | int radix = 10; | |
548 | long int value = 0; | |
549 | ||
550 | switch (argc) { | |
551 | default: | |
552 | /* ignore excess arguments */ | |
553 | case 5: | |
554 | min_digits = expr(argv[4]); | |
555 | case 4: | |
556 | radix = expr(argv[3]); | |
557 | case 3: | |
558 | value = expr(argv[2]); | |
559 | case 2: | |
560 | break; | |
561 | } | |
562 | pbrad(value, radix, min_digits); | |
15637ed4 | 563 | } |
78ed81a3 | 564 | break; |
565 | ||
566 | case IFELTYPE: /* ifelse(X,Y,IFX=Y,Else) */ | |
567 | doifelse(argv, argc); | |
568 | break; | |
569 | ||
570 | case IFDFTYPE: /* ifdef(Mac,IfDef[,IfNotDef]) */ | |
571 | /* select one of two alternatives based on the existence */ | |
572 | /* of another definition */ | |
573 | if (argc > 3) { | |
574 | if (lookup(argv[2]) != nil) { | |
575 | pbstr(argv[3]); | |
576 | } else | |
577 | if (argc > 4) { | |
578 | pbstr(argv[4]); | |
579 | } | |
580 | } | |
581 | break; | |
582 | ||
583 | case LENGTYPE: /* len(Arg) */ | |
584 | /* find the length of the argument */ | |
585 | pbnum(argc > 2 ? strlen(argv[2]) : 0); | |
586 | break; | |
587 | ||
588 | case INCRTYPE: /* incr(Expr) */ | |
589 | /* increment the value of the argument */ | |
590 | if (argc > 2) pbnum(expr(argv[2]) + 1); | |
591 | break; | |
592 | ||
593 | case DECRTYPE: /* decr(Expr) */ | |
594 | /* decrement the value of the argument */ | |
595 | if (argc > 2) pbnum(expr(argv[2]) - 1); | |
596 | break; | |
597 | ||
598 | #if unix || vms | |
599 | case SYSCTYPE: /* syscmd(Command) */ | |
600 | /* execute system command */ | |
601 | /* Make sure m4 output is NOT interrupted */ | |
602 | fflush(stdout); | |
603 | fflush(stderr); | |
604 | ||
605 | if (argc > 2) sysval = system(argv[2]); | |
606 | break; | |
607 | ||
608 | case SYSVTYPE: /* sysval() */ | |
609 | /* return value of the last system call. */ | |
610 | pbnum(sysval); | |
611 | break; | |
612 | #endif | |
15637ed4 | 613 | |
78ed81a3 | 614 | case INCLTYPE: /* include(File) */ |
615 | for (t = 2; t < argc; t++) | |
616 | if (!doincl(argv[t])) cantread(argv[t]); | |
617 | break; | |
15637ed4 | 618 | |
78ed81a3 | 619 | case SINCTYPE: /* sinclude(File) */ |
620 | for (t = 2; t < argc; t++) | |
621 | (void) doincl(argv[t]); | |
622 | break; | |
15637ed4 | 623 | |
78ed81a3 | 624 | #ifdef EXTENDED |
625 | case PASTTYPE: /* paste(File) */ | |
626 | for (t = 2; t < argc; t++) | |
627 | if (!dopaste(argv[t])) cantread(argv[t]); | |
628 | break; | |
629 | ||
630 | case SPASTYPE: /* spaste(File) */ | |
631 | for (t = 2; t < argc; t++) | |
632 | (void) dopaste(argv[t]); | |
633 | break; | |
634 | ||
635 | case TRIMTYPE: /* m4trim(Source,..) */ | |
636 | if (argc > 2) m4trim(argv, argc); | |
637 | break; | |
638 | ||
639 | case DEFQTYPE: /* defquote(Mac,...) */ | |
640 | dodefqt(argv, argc); | |
641 | break; | |
642 | ||
643 | case QUTRTYPE: /* <quote>(text...) */ | |
644 | doqutr(argv, argc); | |
645 | break; | |
646 | #endif | |
15637ed4 | 647 | |
78ed81a3 | 648 | case CHNQTYPE: /* changequote([Left[,Right]]) */ |
649 | dochq(argv, argc); | |
650 | break; | |
651 | ||
652 | case CHNCTYPE: /* changecom([Left[,Right]]) */ | |
653 | dochc(argv, argc); | |
654 | break; | |
655 | ||
656 | case SUBSTYPE: /* substr(Source[,Offset[,Length]]) */ | |
657 | /* select substring */ | |
658 | if (argc > 3) dosub(argv, argc); | |
659 | break; | |
660 | ||
661 | case SHIFTYPE: /* shift(~args~) */ | |
662 | /* push back all arguments except the first one */ | |
663 | /* (i.e. skip argv[2]) */ | |
664 | if (argc > 3) { | |
665 | for (t = argc-1; t > 3; t--) { | |
666 | pbqtd(argv[t]); | |
667 | putback(','); | |
668 | } | |
669 | pbqtd(argv[3]); | |
670 | } | |
671 | break; | |
672 | ||
673 | case DIVRTYPE: /* divert(N) */ | |
674 | if (argc > 2 && (t = expr(argv[2])) != 0) { | |
675 | dodiv(t); | |
676 | } else { | |
677 | active = stdout; | |
678 | oindex = 0; | |
679 | } | |
680 | op = active; | |
681 | break; | |
682 | ||
683 | case UNDVTYPE: /* undivert(N...) */ | |
684 | doundiv(argv, argc); | |
685 | op = active; | |
686 | break; | |
687 | ||
688 | case DIVNTYPE: /* divnum() */ | |
689 | /* return the number of current output diversion */ | |
690 | pbnum(oindex); | |
691 | break; | |
692 | ||
693 | case UNDFTYPE: /* undefine(..) */ | |
694 | /* undefine a previously defined macro(s) or m4 keyword(s). */ | |
695 | for (t = 2; t < argc; t++) remhash(argv[t], ALL); | |
696 | break; | |
697 | ||
698 | case POPDTYPE: /* popdef(Mac...) */ | |
699 | /* remove the topmost definitions of macro(s) or m4 keyword(s). */ | |
700 | for (t = 2; t < argc; t++) remhash(argv[t], TOP); | |
701 | break; | |
702 | ||
703 | case MKTMTYPE: /* maketemp(Pattern) */ | |
704 | /* create a temporary file */ | |
705 | if (argc > 2) pbstr(mktemp(argv[2])); | |
706 | break; | |
707 | ||
708 | case TRNLTYPE: /* translit(Source,Dom,Rng) */ | |
709 | /* replace all characters in the source string that */ | |
710 | /* appears in the "from" string with the corresponding */ | |
711 | /* characters in the "to" string. */ | |
712 | ||
713 | if (argc > 3) { | |
714 | char temp[MAXTOK]; | |
715 | ||
716 | if (argc > 4) | |
717 | map(temp, argv[2], argv[3], argv[4]); | |
718 | else | |
719 | map(temp, argv[2], argv[3], null); | |
720 | pbstr(temp); | |
721 | } else if (argc > 2) | |
722 | pbstr(argv[2]); | |
723 | break; | |
724 | ||
725 | case INDXTYPE: /* index(Source,Target) */ | |
726 | /* find the index of the second argument string in */ | |
727 | /* the first argument string. -1 if not present. */ | |
728 | pbnum(argc > 3 ? indx(argv[2], argv[3]) : -1); | |
729 | break; | |
730 | ||
731 | case ERRPTYPE: /* errprint(W,...,W) */ | |
732 | /* print the arguments to stderr file */ | |
733 | for (t = 2; t < argc; t++) fprintf(stderr, "%s ", argv[t]); | |
734 | fprintf(stderr, "\n"); | |
735 | break; | |
736 | ||
737 | case DNLNTYPE: /* dnl() */ | |
738 | /* eat upto and including newline */ | |
739 | while ((t = gpbc()) != '\n' && t != EOF) ; | |
740 | break; | |
741 | ||
742 | case M4WRTYPE: /* m4wrap(AtExit) */ | |
743 | /* set up for wrap-up/wind-down activity. */ | |
744 | /* NB: if there are several calls to m4wrap */ | |
745 | /* only the last is effective; strange, but */ | |
746 | /* that's what System V does. */ | |
747 | m4wraps = argc > 2 ? strsave(argv[2]) : null; | |
748 | break; | |
749 | ||
750 | case EXITTYPE: /* m4exit(Expr) */ | |
751 | /* immediate exit from m4. */ | |
752 | killdiv(); /* mustn't forget that one! */ | |
753 | exit(argc > 2 ? expr(argv[2]) : 0); | |
754 | break; | |
755 | ||
756 | case DEFNTYPE: /* defn(Mac) */ | |
757 | for (t = 2; t < argc; t++) | |
758 | dodefn(argv[t]); | |
759 | break; | |
760 | ||
761 | default: | |
762 | error("m4: major botch in eval."); | |
763 | break; | |
15637ed4 | 764 | } |
15637ed4 | 765 | |
78ed81a3 | 766 | ep = PREVEP; /* flush strspace */ |
767 | sp = PREVSP; /* previous sp.. */ | |
768 | fp = PREVFP; /* rewind stack... */ | |
769 | } | |
770 | break; | |
771 | ||
15637ed4 | 772 | default: |
78ed81a3 | 773 | chrsave(t); /* stack the char */ |
774 | break; | |
775 | } | |
15637ed4 | 776 | } |
78ed81a3 | 777 | } |
15637ed4 RG |
778 | |
779 | ||
78ed81a3 | 780 | int main(argc, argv) |
781 | int argc; | |
782 | char **argv; | |
783 | { | |
784 | register int c; | |
785 | register int n; | |
786 | char *p; | |
15637ed4 | 787 | |
78ed81a3 | 788 | #ifdef SIGINT |
789 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | |
790 | signal(SIGINT, onintr); | |
791 | #endif | |
792 | ||
793 | /* Initialise the chtype[] table. | |
794 | '0' .. '9' -> 1..10 | |
795 | 'A' .. 'Z' -> 11..37 | |
796 | 'a' .. 'z' -> 11..37 | |
797 | '_' -> 38 | |
798 | all other characters -> 0 | |
799 | */ | |
800 | for (c = EOF; c <= UCHAR_MAX; c++) chtype[c - EOF] = 0; | |
801 | for (c = 1, p = "0123456789"; *p; p++, c++) | |
802 | chtype[*(unsigned char *)p - EOF] = c; | |
803 | for (c = 11, p = "abcdefghijklmnopqrstuvwxyz"; *p; p++, c++) | |
804 | chtype[*(unsigned char *)p - EOF] = c; | |
805 | for (c = 11, p = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; *p; p++, c++) | |
806 | chtype[*(unsigned char *)p - EOF] = c; | |
807 | chtype['_' - EOF] = 38; | |
15637ed4 RG |
808 | |
809 | #ifdef NONZEROPAGES | |
78ed81a3 | 810 | /* If your system does not initialise global variables to */ |
811 | /* 0 bits, do it here. */ | |
812 | for (n = 0; n < HASHSIZE; n++) hashtab[n] = nil; | |
813 | for (n = 0; n < MAXOUT; n++) outfile[n] = NULL; | |
814 | #endif | |
815 | initkwds(); | |
15637ed4 | 816 | |
78ed81a3 | 817 | while ((c = getopt(argc, argv, "cetD:U:o:B:H:S:T:")) != EOF) { |
818 | switch (c) { | |
819 | #if 0 | |
820 | case 's': /* enable #line sync in output */ | |
821 | fprintf(stderr, "m4: this version does not support -s\n"); | |
822 | exit(2); | |
15637ed4 RG |
823 | #endif |
824 | ||
78ed81a3 | 825 | case 'c': /* strip comments */ |
826 | strip ^= 1; | |
827 | break; | |
828 | ||
829 | case 'e': /* interactive */ | |
830 | (void) signal(SIGINT, SIG_IGN); | |
831 | setbuf(stdout, NULL); | |
832 | break; | |
833 | ||
834 | case 'D': /* define something..*/ | |
835 | for (p = optarg; *p && *p != '='; p++) ; | |
836 | if (*p) *p++ = EOS; | |
837 | dodefine(optarg, p); | |
838 | break; | |
839 | ||
840 | case 'U': /* undefine... */ | |
841 | remhash(optarg, TOP); | |
842 | break; | |
15637ed4 | 843 | |
78ed81a3 | 844 | case 'B': case 'H': /* System V compatibility */ |
845 | case 'S': case 'T': /* ignore them */ | |
846 | break; | |
847 | ||
848 | case 'o': /* specific output */ | |
849 | if (!freopen(optarg, "w", stdout)) { | |
850 | perror(optarg); | |
851 | exit(1); | |
852 | } | |
853 | break; | |
854 | ||
855 | case '?': | |
856 | default: | |
857 | usage(); | |
858 | } | |
15637ed4 | 859 | } |
78ed81a3 | 860 | |
861 | active = stdout; /* default active output */ | |
862 | m4temp = mktemp(DIVNAM); /* filename for diversions */ | |
863 | ||
864 | sp = -1; /* stack pointer initialized */ | |
865 | fp = 0; /* frame pointer initialized */ | |
866 | ||
867 | if (optind == argc) { /* no more args; read stdin */ | |
868 | infile[0] = stdin; /* default input (naturally) */ | |
869 | #ifndef NO__FILE | |
870 | dodefine("__FILE__", "-"); /* Helas */ | |
871 | #endif | |
872 | macro(); /* process that file */ | |
873 | } else /* file names in commandline */ | |
874 | for (; optind < argc; optind++) { | |
875 | char *name = argv[optind]; /* next file name */ | |
876 | infile[0] = fopen(name, "r"); | |
877 | if (!infile[0]) cantread(name); | |
878 | #ifndef NO__FILE | |
879 | dodefine("__FILE__", name); | |
880 | #endif | |
881 | macro(); | |
882 | fclose(infile[0]); | |
883 | } | |
884 | ||
885 | if (*m4wraps) { /* anything for rundown ?? */ | |
886 | ilevel = 0; /* in case m4wrap includes.. */ | |
887 | putback(EOF); /* eof is a must !! */ | |
888 | pbstr(m4wraps); /* user-defined wrapup act */ | |
889 | macro(); /* last will and testament */ | |
890 | } else { /* default wrap-up: undivert */ | |
891 | for (n = 1; n < MAXOUT; n++) | |
892 | if (outfile[n] != NULL) getdiv(n); | |
893 | } | |
894 | ||
895 | if (outfile[0] != NULL) { /* remove bitbucket if used */ | |
896 | (void) fclose(outfile[0]); | |
897 | m4temp[UNIQUE] = '0'; | |
898 | #if unix | |
899 | (void) unlink(m4temp); | |
900 | #else | |
901 | (void) remove(m4temp); | |
902 | #endif | |
903 | } | |
904 | exit(0); | |
905 | return 0; | |
906 | } | |
907 |