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