xref: /openbsd/usr.bin/m4/eval.c (revision b81b15b2)
1*b81b15b2Sespie /*	$OpenBSD: eval.c,v 1.21 1999/11/30 22:19:50 espie Exp $	*/
2aa676ce1Smillert /*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1989, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Ozan Yigit at York University.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
19df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
20df930be7Sderaadt  *    must display the following acknowledgement:
21df930be7Sderaadt  *	This product includes software developed by the University of
22df930be7Sderaadt  *	California, Berkeley and its contributors.
23df930be7Sderaadt  * 4. Neither the name of the University nor the names of its contributors
24df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
25df930be7Sderaadt  *    without specific prior written permission.
26df930be7Sderaadt  *
27df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37df930be7Sderaadt  * SUCH DAMAGE.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt #ifndef lint
41df930be7Sderaadt #if 0
42df930be7Sderaadt static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
43df930be7Sderaadt #else
44*b81b15b2Sespie static char rcsid[] = "$OpenBSD: eval.c,v 1.21 1999/11/30 22:19:50 espie Exp $";
45df930be7Sderaadt #endif
46df930be7Sderaadt #endif /* not lint */
47df930be7Sderaadt 
48df930be7Sderaadt /*
49df930be7Sderaadt  * eval.c
50df930be7Sderaadt  * Facility: m4 macro processor
51df930be7Sderaadt  * by: oz
52df930be7Sderaadt  */
53df930be7Sderaadt 
54df930be7Sderaadt #include <sys/types.h>
55df930be7Sderaadt #include <errno.h>
56df930be7Sderaadt #include <unistd.h>
57df930be7Sderaadt #include <stdio.h>
58df930be7Sderaadt #include <stdlib.h>
593f42598dSespie #include <stddef.h>
60df930be7Sderaadt #include <string.h>
61445b77f7Smillert #include <fcntl.h>
6281c2181eSespie #include <err.h>
63df930be7Sderaadt #include "mdef.h"
64df930be7Sderaadt #include "stdd.h"
65df930be7Sderaadt #include "extern.h"
66df930be7Sderaadt #include "pathnames.h"
67df930be7Sderaadt 
68bb34cd6cSespie static void	dodefn __P((const char *));
69bb34cd6cSespie static void	dopushdef __P((const char *, const char *));
70bb34cd6cSespie static void	dodump __P((const char *[], int));
71bb34cd6cSespie static void	doifelse __P((const char *[], int));
72bb34cd6cSespie static int	doincl __P((const char *));
73bb34cd6cSespie static int	dopaste __P((const char *));
74bb34cd6cSespie static void	dochq __P((const char *[], int));
75bb34cd6cSespie static void	dochc __P((const char *[], int));
76bb34cd6cSespie static void	dodiv __P((int));
77bb34cd6cSespie static void	doundiv __P((const char *[], int));
78bb34cd6cSespie static void	dosub __P((const char *[], int));
79bb34cd6cSespie static void	map __P((char *, const char *, const char *, const char *));
80df930be7Sderaadt /*
81df930be7Sderaadt  * eval - evaluate built-in macros.
82df930be7Sderaadt  *	  argc - number of elements in argv.
83df930be7Sderaadt  *	  argv - element vector :
84df930be7Sderaadt  *			argv[0] = definition of a user
85df930be7Sderaadt  *				  macro or nil if built-in.
86df930be7Sderaadt  *			argv[1] = name of the macro or
87df930be7Sderaadt  *				  built-in.
88df930be7Sderaadt  *			argv[2] = parameters to user-defined
89df930be7Sderaadt  *			   .	  macro or built-in.
90df930be7Sderaadt  *			   .
91df930be7Sderaadt  *
92df930be7Sderaadt  * Note that the minimum value for argc is 3. A call in the form
93df930be7Sderaadt  * of macro-or-builtin() will result in:
94df930be7Sderaadt  *			argv[0] = nullstr
95df930be7Sderaadt  *			argv[1] = macro-or-builtin
96df930be7Sderaadt  *			argv[2] = nullstr
97df930be7Sderaadt  */
98df930be7Sderaadt 
99df930be7Sderaadt void
100df930be7Sderaadt eval(argv, argc, td)
101bb34cd6cSespie 	const char *argv[];
10253f6f6bfSespie 	int argc;
10353f6f6bfSespie 	int td;
104df930be7Sderaadt {
10553f6f6bfSespie 	int c, n;
106df930be7Sderaadt 	static int sysval = 0;
107df930be7Sderaadt 
108df930be7Sderaadt #ifdef DEBUG
109df930be7Sderaadt 	printf("argc = %d\n", argc);
110df930be7Sderaadt 	for (n = 0; n < argc; n++)
111df930be7Sderaadt 		printf("argv[%d] = %s\n", n, argv[n]);
112df930be7Sderaadt #endif
113df930be7Sderaadt  /*
114df930be7Sderaadt   * if argc == 3 and argv[2] is null, then we
115df930be7Sderaadt   * have macro-or-builtin() type call. We adjust
116df930be7Sderaadt   * argc to avoid further checking..
117df930be7Sderaadt   */
118df930be7Sderaadt 	if (argc == 3 && !*(argv[2]))
119df930be7Sderaadt 		argc--;
120df930be7Sderaadt 
121df930be7Sderaadt 	switch (td & ~STATIC) {
122df930be7Sderaadt 
123df930be7Sderaadt 	case DEFITYPE:
124df930be7Sderaadt 		if (argc > 2)
125df930be7Sderaadt 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
126df930be7Sderaadt 		break;
127df930be7Sderaadt 
128df930be7Sderaadt 	case PUSDTYPE:
129df930be7Sderaadt 		if (argc > 2)
130df930be7Sderaadt 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
131df930be7Sderaadt 		break;
132df930be7Sderaadt 
133df930be7Sderaadt 	case DUMPTYPE:
134df930be7Sderaadt 		dodump(argv, argc);
135df930be7Sderaadt 		break;
136df930be7Sderaadt 
137df930be7Sderaadt 	case EXPRTYPE:
138df930be7Sderaadt 	/*
139df930be7Sderaadt 	 * doexpr - evaluate arithmetic
140df930be7Sderaadt 	 * expression
141df930be7Sderaadt 	 */
142df930be7Sderaadt 		if (argc > 2)
143df930be7Sderaadt 			pbnum(expr(argv[2]));
144df930be7Sderaadt 		break;
145df930be7Sderaadt 
146df930be7Sderaadt 	case IFELTYPE:
147df930be7Sderaadt 		if (argc > 4)
148df930be7Sderaadt 			doifelse(argv, argc);
149df930be7Sderaadt 		break;
150df930be7Sderaadt 
151df930be7Sderaadt 	case IFDFTYPE:
152df930be7Sderaadt 	/*
153df930be7Sderaadt 	 * doifdef - select one of two
154df930be7Sderaadt 	 * alternatives based on the existence of
155df930be7Sderaadt 	 * another definition
156df930be7Sderaadt 	 */
157df930be7Sderaadt 		if (argc > 3) {
158df930be7Sderaadt 			if (lookup(argv[2]) != nil)
159df930be7Sderaadt 				pbstr(argv[3]);
160df930be7Sderaadt 			else if (argc > 4)
161df930be7Sderaadt 				pbstr(argv[4]);
162df930be7Sderaadt 		}
163df930be7Sderaadt 		break;
164df930be7Sderaadt 
165df930be7Sderaadt 	case LENGTYPE:
166df930be7Sderaadt 	/*
167df930be7Sderaadt 	 * dolen - find the length of the
168df930be7Sderaadt 	 * argument
169df930be7Sderaadt 	 */
170df930be7Sderaadt 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
171df930be7Sderaadt 		break;
172df930be7Sderaadt 
173df930be7Sderaadt 	case INCRTYPE:
174df930be7Sderaadt 	/*
175df930be7Sderaadt 	 * doincr - increment the value of the
176df930be7Sderaadt 	 * argument
177df930be7Sderaadt 	 */
178df930be7Sderaadt 		if (argc > 2)
179df930be7Sderaadt 			pbnum(atoi(argv[2]) + 1);
180df930be7Sderaadt 		break;
181df930be7Sderaadt 
182df930be7Sderaadt 	case DECRTYPE:
183df930be7Sderaadt 	/*
184df930be7Sderaadt 	 * dodecr - decrement the value of the
185df930be7Sderaadt 	 * argument
186df930be7Sderaadt 	 */
187df930be7Sderaadt 		if (argc > 2)
188df930be7Sderaadt 			pbnum(atoi(argv[2]) - 1);
189df930be7Sderaadt 		break;
190df930be7Sderaadt 
191df930be7Sderaadt 	case SYSCTYPE:
192df930be7Sderaadt 	/*
193df930be7Sderaadt 	 * dosys - execute system command
194df930be7Sderaadt 	 */
195df930be7Sderaadt 		if (argc > 2)
196df930be7Sderaadt 			sysval = system(argv[2]);
197df930be7Sderaadt 		break;
198df930be7Sderaadt 
199df930be7Sderaadt 	case SYSVTYPE:
200df930be7Sderaadt 	/*
201df930be7Sderaadt 	 * dosysval - return value of the last
202df930be7Sderaadt 	 * system call.
203df930be7Sderaadt 	 *
204df930be7Sderaadt 	 */
205df930be7Sderaadt 		pbnum(sysval);
206df930be7Sderaadt 		break;
207df930be7Sderaadt 
208df930be7Sderaadt 	case INCLTYPE:
209df930be7Sderaadt 		if (argc > 2)
210df930be7Sderaadt 			if (!doincl(argv[2]))
21181c2181eSespie 				err(1, "%s", argv[2]);
212df930be7Sderaadt 		break;
213df930be7Sderaadt 
214df930be7Sderaadt 	case SINCTYPE:
215df930be7Sderaadt 		if (argc > 2)
216df930be7Sderaadt 			(void) doincl(argv[2]);
217df930be7Sderaadt 		break;
218df930be7Sderaadt #ifdef EXTENDED
219df930be7Sderaadt 	case PASTTYPE:
220df930be7Sderaadt 		if (argc > 2)
221df930be7Sderaadt 			if (!dopaste(argv[2]))
22281c2181eSespie 				err(1, "%s", argv[2]);
223df930be7Sderaadt 		break;
224df930be7Sderaadt 
225df930be7Sderaadt 	case SPASTYPE:
226df930be7Sderaadt 		if (argc > 2)
227df930be7Sderaadt 			(void) dopaste(argv[2]);
228df930be7Sderaadt 		break;
229df930be7Sderaadt #endif
230df930be7Sderaadt 	case CHNQTYPE:
231df930be7Sderaadt 		dochq(argv, argc);
232df930be7Sderaadt 		break;
233df930be7Sderaadt 
234df930be7Sderaadt 	case CHNCTYPE:
235df930be7Sderaadt 		dochc(argv, argc);
236df930be7Sderaadt 		break;
237df930be7Sderaadt 
238df930be7Sderaadt 	case SUBSTYPE:
239df930be7Sderaadt 	/*
240df930be7Sderaadt 	 * dosub - select substring
241df930be7Sderaadt 	 *
242df930be7Sderaadt 	 */
243df930be7Sderaadt 		if (argc > 3)
244df930be7Sderaadt 			dosub(argv, argc);
245df930be7Sderaadt 		break;
246df930be7Sderaadt 
247df930be7Sderaadt 	case SHIFTYPE:
248df930be7Sderaadt 	/*
249df930be7Sderaadt 	 * doshift - push back all arguments
250df930be7Sderaadt 	 * except the first one (i.e. skip
251df930be7Sderaadt 	 * argv[2])
252df930be7Sderaadt 	 */
253df930be7Sderaadt 		if (argc > 3) {
254df930be7Sderaadt 			for (n = argc - 1; n > 3; n--) {
2553a73db8cSderaadt 				pbstr(rquote);
256df930be7Sderaadt 				pbstr(argv[n]);
2573a73db8cSderaadt 				pbstr(lquote);
258aa676ce1Smillert 				putback(COMMA);
259df930be7Sderaadt 			}
2603a73db8cSderaadt 			pbstr(rquote);
261df930be7Sderaadt 			pbstr(argv[3]);
2623a73db8cSderaadt 			pbstr(lquote);
263df930be7Sderaadt 		}
264df930be7Sderaadt 		break;
265df930be7Sderaadt 
266df930be7Sderaadt 	case DIVRTYPE:
267df930be7Sderaadt 		if (argc > 2 && (n = atoi(argv[2])) != 0)
268df930be7Sderaadt 			dodiv(n);
269df930be7Sderaadt 		else {
270df930be7Sderaadt 			active = stdout;
271df930be7Sderaadt 			oindex = 0;
272df930be7Sderaadt 		}
273df930be7Sderaadt 		break;
274df930be7Sderaadt 
275df930be7Sderaadt 	case UNDVTYPE:
276df930be7Sderaadt 		doundiv(argv, argc);
277df930be7Sderaadt 		break;
278df930be7Sderaadt 
279df930be7Sderaadt 	case DIVNTYPE:
280df930be7Sderaadt 	/*
281df930be7Sderaadt 	 * dodivnum - return the number of
282df930be7Sderaadt 	 * current output diversion
283df930be7Sderaadt 	 */
284df930be7Sderaadt 		pbnum(oindex);
285df930be7Sderaadt 		break;
286df930be7Sderaadt 
287df930be7Sderaadt 	case UNDFTYPE:
288df930be7Sderaadt 	/*
289df930be7Sderaadt 	 * doundefine - undefine a previously
290df930be7Sderaadt 	 * defined macro(s) or m4 keyword(s).
291df930be7Sderaadt 	 */
292df930be7Sderaadt 		if (argc > 2)
293df930be7Sderaadt 			for (n = 2; n < argc; n++)
294df930be7Sderaadt 				remhash(argv[n], ALL);
295df930be7Sderaadt 		break;
296df930be7Sderaadt 
297df930be7Sderaadt 	case POPDTYPE:
298df930be7Sderaadt 	/*
299df930be7Sderaadt 	 * dopopdef - remove the topmost
300df930be7Sderaadt 	 * definitions of macro(s) or m4
301df930be7Sderaadt 	 * keyword(s).
302df930be7Sderaadt 	 */
303df930be7Sderaadt 		if (argc > 2)
304df930be7Sderaadt 			for (n = 2; n < argc; n++)
305df930be7Sderaadt 				remhash(argv[n], TOP);
306df930be7Sderaadt 		break;
307df930be7Sderaadt 
308df930be7Sderaadt 	case MKTMTYPE:
309df930be7Sderaadt 	/*
310df930be7Sderaadt 	 * dotemp - create a temporary file
311df930be7Sderaadt 	 */
31201e71e69Sespie 		if (argc > 2) {
31301e71e69Sespie 			int fd;
314bb34cd6cSespie 			char *temp;
31501e71e69Sespie 
316bb34cd6cSespie 			temp = xstrdup(argv[2]);
317bb34cd6cSespie 
318bb34cd6cSespie 			fd = mkstemp(temp);
31901e71e69Sespie 			if (fd == -1)
32001e71e69Sespie 				err(1, "couldn't make temp file %s", argv[2]);
32101e71e69Sespie 			close(fd);
322bb34cd6cSespie 			pbstr(temp);
323bb34cd6cSespie 			free(temp);
32401e71e69Sespie 		}
325df930be7Sderaadt 		break;
326df930be7Sderaadt 
327df930be7Sderaadt 	case TRNLTYPE:
328df930be7Sderaadt 	/*
329df930be7Sderaadt 	 * dotranslit - replace all characters in
330df930be7Sderaadt 	 * the source string that appears in the
331df930be7Sderaadt 	 * "from" string with the corresponding
332df930be7Sderaadt 	 * characters in the "to" string.
333df930be7Sderaadt 	 */
334df930be7Sderaadt 		if (argc > 3) {
3357d3e0b6bSderaadt 			char temp[STRSPMAX+1];
336df930be7Sderaadt 			if (argc > 4)
337df930be7Sderaadt 				map(temp, argv[2], argv[3], argv[4]);
338df930be7Sderaadt 			else
339df930be7Sderaadt 				map(temp, argv[2], argv[3], null);
340df930be7Sderaadt 			pbstr(temp);
3417d3e0b6bSderaadt 		} else if (argc > 2)
342df930be7Sderaadt 			pbstr(argv[2]);
343df930be7Sderaadt 		break;
344df930be7Sderaadt 
345df930be7Sderaadt 	case INDXTYPE:
346df930be7Sderaadt 	/*
347df930be7Sderaadt 	 * doindex - find the index of the second
348df930be7Sderaadt 	 * argument string in the first argument
349df930be7Sderaadt 	 * string. -1 if not present.
350df930be7Sderaadt 	 */
351df930be7Sderaadt 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
352df930be7Sderaadt 		break;
353df930be7Sderaadt 
354df930be7Sderaadt 	case ERRPTYPE:
355df930be7Sderaadt 	/*
356df930be7Sderaadt 	 * doerrp - print the arguments to stderr
357df930be7Sderaadt 	 * file
358df930be7Sderaadt 	 */
359df930be7Sderaadt 		if (argc > 2) {
360df930be7Sderaadt 			for (n = 2; n < argc; n++)
361df930be7Sderaadt 				fprintf(stderr, "%s ", argv[n]);
362df930be7Sderaadt 			fprintf(stderr, "\n");
363df930be7Sderaadt 		}
364df930be7Sderaadt 		break;
365df930be7Sderaadt 
366df930be7Sderaadt 	case DNLNTYPE:
367df930be7Sderaadt 	/*
368df930be7Sderaadt 	 * dodnl - eat-up-to and including
369df930be7Sderaadt 	 * newline
370df930be7Sderaadt 	 */
371df930be7Sderaadt 		while ((c = gpbc()) != '\n' && c != EOF)
372df930be7Sderaadt 			;
373df930be7Sderaadt 		break;
374df930be7Sderaadt 
375df930be7Sderaadt 	case M4WRTYPE:
376df930be7Sderaadt 	/*
377df930be7Sderaadt 	 * dom4wrap - set up for
378df930be7Sderaadt 	 * wrap-up/wind-down activity
379df930be7Sderaadt 	 */
380df930be7Sderaadt 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
381df930be7Sderaadt 		break;
382df930be7Sderaadt 
383df930be7Sderaadt 	case EXITTYPE:
384df930be7Sderaadt 	/*
385df930be7Sderaadt 	 * doexit - immediate exit from m4.
386df930be7Sderaadt 	 */
387df930be7Sderaadt 		killdiv();
388df930be7Sderaadt 		exit((argc > 2) ? atoi(argv[2]) : 0);
389df930be7Sderaadt 		break;
390df930be7Sderaadt 
391df930be7Sderaadt 	case DEFNTYPE:
392df930be7Sderaadt 		if (argc > 2)
393df930be7Sderaadt 			for (n = 2; n < argc; n++)
394df930be7Sderaadt 				dodefn(argv[n]);
395df930be7Sderaadt 		break;
396df930be7Sderaadt 
397df930be7Sderaadt 	default:
39881c2181eSespie 		errx(1, "eval: major botch.");
399df930be7Sderaadt 		break;
400df930be7Sderaadt 	}
401df930be7Sderaadt }
402df930be7Sderaadt 
403df930be7Sderaadt char *dumpfmt = "`%s'\t`%s'\n";	       /* format string for dumpdef   */
404df930be7Sderaadt 
405df930be7Sderaadt /*
406df930be7Sderaadt  * expand - user-defined macro expansion
407df930be7Sderaadt  */
408df930be7Sderaadt void
409df930be7Sderaadt expand(argv, argc)
410bb34cd6cSespie 	const char *argv[];
41153f6f6bfSespie 	int argc;
412df930be7Sderaadt {
413bb34cd6cSespie 	const char *t;
414bb34cd6cSespie 	const char *p;
41553f6f6bfSespie 	int n;
41653f6f6bfSespie 	int argno;
417df930be7Sderaadt 
418df930be7Sderaadt 	t = argv[0];		       /* defn string as a whole */
419df930be7Sderaadt 	p = t;
420df930be7Sderaadt 	while (*p)
421df930be7Sderaadt 		p++;
422df930be7Sderaadt 	p--;			       /* last character of defn */
423df930be7Sderaadt 	while (p > t) {
424df930be7Sderaadt 		if (*(p - 1) != ARGFLAG)
425df930be7Sderaadt 			putback(*p);
426df930be7Sderaadt 		else {
427df930be7Sderaadt 			switch (*p) {
428df930be7Sderaadt 
429df930be7Sderaadt 			case '#':
430df930be7Sderaadt 				pbnum(argc - 2);
431df930be7Sderaadt 				break;
432df930be7Sderaadt 			case '0':
433df930be7Sderaadt 			case '1':
434df930be7Sderaadt 			case '2':
435df930be7Sderaadt 			case '3':
436df930be7Sderaadt 			case '4':
437df930be7Sderaadt 			case '5':
438df930be7Sderaadt 			case '6':
439df930be7Sderaadt 			case '7':
440df930be7Sderaadt 			case '8':
441df930be7Sderaadt 			case '9':
442df930be7Sderaadt 				if ((argno = *p - '0') < argc - 1)
443df930be7Sderaadt 					pbstr(argv[argno + 1]);
444df930be7Sderaadt 				break;
445df930be7Sderaadt 			case '*':
446df930be7Sderaadt 				for (n = argc - 1; n > 2; n--) {
447df930be7Sderaadt 					pbstr(argv[n]);
448aa676ce1Smillert 					putback(COMMA);
449df930be7Sderaadt 				}
450df930be7Sderaadt 				pbstr(argv[2]);
451df930be7Sderaadt 				break;
452aa676ce1Smillert                         case '@':
453aa676ce1Smillert                                 for (n = argc - 1; n > 2; n--) {
454aa676ce1Smillert                                         pbstr(rquote);
455aa676ce1Smillert                                         pbstr(argv[n]);
456aa676ce1Smillert                                         pbstr(lquote);
457aa676ce1Smillert 					putback(COMMA);
458aa676ce1Smillert                                 }
459aa676ce1Smillert 				pbstr(rquote);
460aa676ce1Smillert                                 pbstr(argv[2]);
461aa676ce1Smillert 				pbstr(lquote);
462aa676ce1Smillert                                 break;
463df930be7Sderaadt 			default:
464df930be7Sderaadt 				putback(*p);
465df930be7Sderaadt 				putback('$');
466df930be7Sderaadt 				break;
467df930be7Sderaadt 			}
468df930be7Sderaadt 			p--;
469df930be7Sderaadt 		}
470df930be7Sderaadt 		p--;
471df930be7Sderaadt 	}
472df930be7Sderaadt 	if (p == t)		       /* do last character */
473df930be7Sderaadt 		putback(*p);
474df930be7Sderaadt }
475df930be7Sderaadt 
476df930be7Sderaadt /*
477df930be7Sderaadt  * dodefine - install definition in the table
478df930be7Sderaadt  */
479df930be7Sderaadt void
480df930be7Sderaadt dodefine(name, defn)
481bb34cd6cSespie 	const char *name;
482bb34cd6cSespie 	const char *defn;
483df930be7Sderaadt {
48453f6f6bfSespie 	ndptr p;
485df930be7Sderaadt 
486df930be7Sderaadt 	if (!*name)
48781c2181eSespie 		errx(1, "null definition.");
488df930be7Sderaadt 	if (STREQ(name, defn))
48981c2181eSespie 		errx(1, "%s: recursive definition.", name);
490df930be7Sderaadt 	if ((p = lookup(name)) == nil)
491df930be7Sderaadt 		p = addent(name);
492df930be7Sderaadt 	else if (p->defn != null)
493df930be7Sderaadt 		free((char *) p->defn);
494df930be7Sderaadt 	if (!*defn)
495df930be7Sderaadt 		p->defn = null;
496df930be7Sderaadt 	else
497df930be7Sderaadt 		p->defn = xstrdup(defn);
498df930be7Sderaadt 	p->type = MACRTYPE;
499df930be7Sderaadt }
500df930be7Sderaadt 
501df930be7Sderaadt /*
502df930be7Sderaadt  * dodefn - push back a quoted definition of
503df930be7Sderaadt  *      the given name.
504df930be7Sderaadt  */
505bb34cd6cSespie static void
506df930be7Sderaadt dodefn(name)
507bb34cd6cSespie 	const char *name;
508df930be7Sderaadt {
50953f6f6bfSespie 	ndptr p;
510df930be7Sderaadt 
511df930be7Sderaadt 	if ((p = lookup(name)) != nil && p->defn != null) {
5123a73db8cSderaadt 		pbstr(rquote);
513df930be7Sderaadt 		pbstr(p->defn);
5143a73db8cSderaadt 		pbstr(lquote);
515df930be7Sderaadt 	}
516df930be7Sderaadt }
517df930be7Sderaadt 
518df930be7Sderaadt /*
519df930be7Sderaadt  * dopushdef - install a definition in the hash table
520df930be7Sderaadt  *      without removing a previous definition. Since
521df930be7Sderaadt  *      each new entry is entered in *front* of the
522df930be7Sderaadt  *      hash bucket, it hides a previous definition from
523df930be7Sderaadt  *      lookup.
524df930be7Sderaadt  */
525bb34cd6cSespie static void
526df930be7Sderaadt dopushdef(name, defn)
527bb34cd6cSespie 	const char *name;
528bb34cd6cSespie 	const char *defn;
529df930be7Sderaadt {
53053f6f6bfSespie 	ndptr p;
531df930be7Sderaadt 
532df930be7Sderaadt 	if (!*name)
53381c2181eSespie 		errx(1, "null definition");
534df930be7Sderaadt 	if (STREQ(name, defn))
53581c2181eSespie 		errx(1, "%s: recursive definition.", name);
536df930be7Sderaadt 	p = addent(name);
537df930be7Sderaadt 	if (!*defn)
538df930be7Sderaadt 		p->defn = null;
539df930be7Sderaadt 	else
540df930be7Sderaadt 		p->defn = xstrdup(defn);
541df930be7Sderaadt 	p->type = MACRTYPE;
542df930be7Sderaadt }
543df930be7Sderaadt 
544df930be7Sderaadt /*
545df930be7Sderaadt  * dodumpdef - dump the specified definitions in the hash
546df930be7Sderaadt  *      table to stderr. If nothing is specified, the entire
547df930be7Sderaadt  *      hash table is dumped.
548df930be7Sderaadt  */
549bb34cd6cSespie static void
550df930be7Sderaadt dodump(argv, argc)
551bb34cd6cSespie 	const char *argv[];
55253f6f6bfSespie 	int argc;
553df930be7Sderaadt {
55453f6f6bfSespie 	int n;
555df930be7Sderaadt 	ndptr p;
556df930be7Sderaadt 
557df930be7Sderaadt 	if (argc > 2) {
558df930be7Sderaadt 		for (n = 2; n < argc; n++)
559df930be7Sderaadt 			if ((p = lookup(argv[n])) != nil)
560df930be7Sderaadt 				fprintf(stderr, dumpfmt, p->name,
561df930be7Sderaadt 					p->defn);
5627d3e0b6bSderaadt 	} else {
563df930be7Sderaadt 		for (n = 0; n < HASHSIZE; n++)
564df930be7Sderaadt 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
565df930be7Sderaadt 				fprintf(stderr, dumpfmt, p->name,
566df930be7Sderaadt 					p->defn);
567df930be7Sderaadt 	}
568df930be7Sderaadt }
569df930be7Sderaadt 
570df930be7Sderaadt /*
571df930be7Sderaadt  * doifelse - select one of two alternatives - loop.
572df930be7Sderaadt  */
573bb34cd6cSespie static void
574df930be7Sderaadt doifelse(argv, argc)
575bb34cd6cSespie 	const char *argv[];
57653f6f6bfSespie 	int argc;
577df930be7Sderaadt {
578df930be7Sderaadt 	cycle {
579df930be7Sderaadt 		if (STREQ(argv[2], argv[3]))
580df930be7Sderaadt 			pbstr(argv[4]);
581df930be7Sderaadt 		else if (argc == 6)
582df930be7Sderaadt 			pbstr(argv[5]);
583df930be7Sderaadt 		else if (argc > 6) {
584df930be7Sderaadt 			argv += 3;
585df930be7Sderaadt 			argc -= 3;
586df930be7Sderaadt 			continue;
587df930be7Sderaadt 		}
588df930be7Sderaadt 		break;
589df930be7Sderaadt 	}
590df930be7Sderaadt }
591df930be7Sderaadt 
592df930be7Sderaadt /*
593df930be7Sderaadt  * doinclude - include a given file.
594df930be7Sderaadt  */
595bb34cd6cSespie static int
596df930be7Sderaadt doincl(ifile)
597bb34cd6cSespie 	const char *ifile;
598df930be7Sderaadt {
599df930be7Sderaadt 	if (ilevel + 1 == MAXINP)
60081c2181eSespie 		errx(1, "too many include files.");
6014a769388Sespie 	if ((infile[ilevel + 1] = fopen_trypath(ifile)) != NULL) {
602df930be7Sderaadt 		ilevel++;
603df930be7Sderaadt 		bbase[ilevel] = bufbase = bp;
604df930be7Sderaadt 		return (1);
6057d3e0b6bSderaadt 	} else
606df930be7Sderaadt 		return (0);
607df930be7Sderaadt }
608df930be7Sderaadt 
609df930be7Sderaadt #ifdef EXTENDED
610df930be7Sderaadt /*
611df930be7Sderaadt  * dopaste - include a given file without any
612df930be7Sderaadt  *           macro processing.
613df930be7Sderaadt  */
614bb34cd6cSespie static int
615df930be7Sderaadt dopaste(pfile)
616bb34cd6cSespie 	const char *pfile;
617df930be7Sderaadt {
618df930be7Sderaadt 	FILE *pf;
61953f6f6bfSespie 	int c;
620df930be7Sderaadt 
621df930be7Sderaadt 	if ((pf = fopen(pfile, "r")) != NULL) {
622df930be7Sderaadt 		while ((c = getc(pf)) != EOF)
623df930be7Sderaadt 			putc(c, active);
624df930be7Sderaadt 		(void) fclose(pf);
625df930be7Sderaadt 		return (1);
6267d3e0b6bSderaadt 	} else
627df930be7Sderaadt 		return (0);
628df930be7Sderaadt }
629df930be7Sderaadt #endif
630df930be7Sderaadt 
631df930be7Sderaadt /*
632df930be7Sderaadt  * dochq - change quote characters
633df930be7Sderaadt  */
634bb34cd6cSespie static void
635df930be7Sderaadt dochq(argv, argc)
636bb34cd6cSespie 	const char *argv[];
63753f6f6bfSespie 	int argc;
638df930be7Sderaadt {
639df930be7Sderaadt 	if (argc > 2) {
64018a1973bSderaadt 		if (*argv[2])
641*b81b15b2Sespie 			strlcpy(lquote, argv[2], sizeof(lquote));
64218a1973bSderaadt 		else {
64318a1973bSderaadt 			lquote[0] = LQUOTE;
644f0484631Sespie 			lquote[1] = EOS;
64518a1973bSderaadt 		}
646df930be7Sderaadt 		if (argc > 3) {
647df930be7Sderaadt 			if (*argv[3])
648*b81b15b2Sespie 				strlcpy(rquote, argv[3], sizeof(rquote));
6497d3e0b6bSderaadt 		} else
65029a0bfdcSderaadt 			strcpy(rquote, lquote);
6517d3e0b6bSderaadt 	} else {
652f0484631Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
653f0484631Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
654df930be7Sderaadt 	}
655df930be7Sderaadt }
656df930be7Sderaadt 
657df930be7Sderaadt /*
658df930be7Sderaadt  * dochc - change comment characters
659df930be7Sderaadt  */
660bb34cd6cSespie static void
661df930be7Sderaadt dochc(argv, argc)
662bb34cd6cSespie 	const char *argv[];
66353f6f6bfSespie 	int argc;
664df930be7Sderaadt {
665df930be7Sderaadt 	if (argc > 2) {
666df930be7Sderaadt 		if (*argv[2])
667*b81b15b2Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
668df930be7Sderaadt 		if (argc > 3) {
669df930be7Sderaadt 			if (*argv[3])
670*b81b15b2Sespie 				strlcpy(ecommt, argv[3], sizeof(ecommt));
671df930be7Sderaadt 		}
672df930be7Sderaadt 		else
673f0484631Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
674df930be7Sderaadt 	}
675df930be7Sderaadt 	else {
676f0484631Sespie 		scommt[0] = SCOMMT, scommt[1] = EOS;
677f0484631Sespie 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
678df930be7Sderaadt 	}
679df930be7Sderaadt }
680df930be7Sderaadt 
681df930be7Sderaadt /*
682df930be7Sderaadt  * dodivert - divert the output to a temporary file
683df930be7Sderaadt  */
684bb34cd6cSespie static void
685df930be7Sderaadt dodiv(n)
68653f6f6bfSespie 	int n;
687df930be7Sderaadt {
688445b77f7Smillert 	int fd;
689445b77f7Smillert 
6907d3e0b6bSderaadt 	oindex = n;
691df930be7Sderaadt 	if (n < 0 || n >= MAXOUT)
692df930be7Sderaadt 		n = 0;		       /* bitbucket */
693df930be7Sderaadt 	if (outfile[n] == NULL) {
6943f42598dSespie 		char fname[] = _PATH_DIVNAME;
6953f42598dSespie 
6963f42598dSespie 		if ((fd = mkstemp(fname)) < 0 ||
6973f42598dSespie 			(outfile[n] = fdopen(fd, "w+")) == NULL)
6983f42598dSespie 				err(1, "%s: cannot divert", fname);
6993f42598dSespie 		if (unlink(fname) == -1)
7003f42598dSespie 			err(1, "%s: cannot unlink", fname);
701df930be7Sderaadt 	}
702df930be7Sderaadt 	active = outfile[n];
703df930be7Sderaadt }
704df930be7Sderaadt 
705df930be7Sderaadt /*
706df930be7Sderaadt  * doundivert - undivert a specified output, or all
707df930be7Sderaadt  *              other outputs, in numerical order.
708df930be7Sderaadt  */
709bb34cd6cSespie static void
710df930be7Sderaadt doundiv(argv, argc)
711bb34cd6cSespie 	const char *argv[];
71253f6f6bfSespie 	int argc;
713df930be7Sderaadt {
71453f6f6bfSespie 	int ind;
71553f6f6bfSespie 	int n;
716df930be7Sderaadt 
717df930be7Sderaadt 	if (argc > 2) {
718df930be7Sderaadt 		for (ind = 2; ind < argc; ind++) {
719df930be7Sderaadt 			n = atoi(argv[ind]);
720df930be7Sderaadt 			if (n > 0 && n < MAXOUT && outfile[n] != NULL)
721df930be7Sderaadt 				getdiv(n);
722df930be7Sderaadt 
723df930be7Sderaadt 		}
724df930be7Sderaadt 	}
725df930be7Sderaadt 	else
726df930be7Sderaadt 		for (n = 1; n < MAXOUT; n++)
727df930be7Sderaadt 			if (outfile[n] != NULL)
728df930be7Sderaadt 				getdiv(n);
729df930be7Sderaadt }
730df930be7Sderaadt 
731df930be7Sderaadt /*
732df930be7Sderaadt  * dosub - select substring
733df930be7Sderaadt  */
734bb34cd6cSespie static void
735df930be7Sderaadt dosub(argv, argc)
736bb34cd6cSespie 	const char *argv[];
73753f6f6bfSespie 	int argc;
738df930be7Sderaadt {
739bb34cd6cSespie 	const char *ap, *fc, *k;
74053f6f6bfSespie 	int nc;
741df930be7Sderaadt 
742df930be7Sderaadt 	if (argc < 5)
743df930be7Sderaadt 		nc = MAXTOK;
744df930be7Sderaadt 	else
745df930be7Sderaadt #ifdef EXPR
746df930be7Sderaadt 		nc = expr(argv[4]);
747df930be7Sderaadt #else
748df930be7Sderaadt 		nc = atoi(argv[4]);
749df930be7Sderaadt #endif
750df930be7Sderaadt 	ap = argv[2];		       /* target string */
751df930be7Sderaadt #ifdef EXPR
752df930be7Sderaadt 	fc = ap + expr(argv[3]);       /* first char */
753df930be7Sderaadt #else
754df930be7Sderaadt 	fc = ap + atoi(argv[3]);       /* first char */
755df930be7Sderaadt #endif
756df930be7Sderaadt 	if (fc >= ap && fc < ap + strlen(ap))
757df930be7Sderaadt 		for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
758df930be7Sderaadt 			putback(*k);
759df930be7Sderaadt }
760df930be7Sderaadt 
761df930be7Sderaadt /*
762df930be7Sderaadt  * map:
763df930be7Sderaadt  * map every character of s1 that is specified in from
764df930be7Sderaadt  * into s3 and replace in s. (source s1 remains untouched)
765df930be7Sderaadt  *
766df930be7Sderaadt  * This is a standard implementation of map(s,from,to) function of ICON
767df930be7Sderaadt  * language. Within mapvec, we replace every character of "from" with
768df930be7Sderaadt  * the corresponding character in "to". If "to" is shorter than "from",
769df930be7Sderaadt  * than the corresponding entries are null, which means that those
770df930be7Sderaadt  * characters dissapear altogether. Furthermore, imagine
771df930be7Sderaadt  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
772df930be7Sderaadt  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
773df930be7Sderaadt  * ultimately maps to `*'. In order to achieve this effect in an efficient
774df930be7Sderaadt  * manner (i.e. without multiple passes over the destination string), we
775df930be7Sderaadt  * loop over mapvec, starting with the initial source character. if the
776df930be7Sderaadt  * character value (dch) in this location is different than the source
777df930be7Sderaadt  * character (sch), sch becomes dch, once again to index into mapvec, until
778df930be7Sderaadt  * the character value stabilizes (i.e. sch = dch, in other words
779df930be7Sderaadt  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
780df930be7Sderaadt  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
781df930be7Sderaadt  * end, we restore mapvec* back to normal where mapvec[n] == n for
782df930be7Sderaadt  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
783df930be7Sderaadt  * about 5 times faster than any algorithm that makes multiple passes over
784df930be7Sderaadt  * destination string.
785df930be7Sderaadt  */
786bb34cd6cSespie static void
787df930be7Sderaadt map(dest, src, from, to)
78853f6f6bfSespie 	char *dest;
789bb34cd6cSespie 	const char *src;
790bb34cd6cSespie 	const char *from;
791bb34cd6cSespie 	const char *to;
792df930be7Sderaadt {
793bb34cd6cSespie 	const char *tmp;
794ee3599c7Sespie 	unsigned char sch, dch;
795ee3599c7Sespie 	static unsigned char mapvec[256] = {
796ee3599c7Sespie 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
797ee3599c7Sespie 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
798ee3599c7Sespie 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
799ee3599c7Sespie 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
800ee3599c7Sespie 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
801ee3599c7Sespie 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
802ee3599c7Sespie 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
803ee3599c7Sespie 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
804ee3599c7Sespie 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
805ee3599c7Sespie 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
806ee3599c7Sespie 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
807ee3599c7Sespie 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
808ee3599c7Sespie 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
809ee3599c7Sespie 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
810ee3599c7Sespie 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
811ee3599c7Sespie 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
812ee3599c7Sespie 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
813ee3599c7Sespie 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
814df930be7Sderaadt 	};
815df930be7Sderaadt 
816df930be7Sderaadt 	if (*src) {
817df930be7Sderaadt 		tmp = from;
818df930be7Sderaadt 	/*
819df930be7Sderaadt 	 * create a mapping between "from" and
820df930be7Sderaadt 	 * "to"
821df930be7Sderaadt 	 */
822df930be7Sderaadt 		while (*from)
823ee3599c7Sespie 			mapvec[(unsigned char)(*from++)] = (*to) ?
824ee3599c7Sespie 				(unsigned char)(*to++) : 0;
825df930be7Sderaadt 
826df930be7Sderaadt 		while (*src) {
827ee3599c7Sespie 			sch = (unsigned char)(*src++);
828df930be7Sderaadt 			dch = mapvec[sch];
829df930be7Sderaadt 			while (dch != sch) {
830df930be7Sderaadt 				sch = dch;
831df930be7Sderaadt 				dch = mapvec[sch];
832df930be7Sderaadt 			}
833ee3599c7Sespie 			if ((*dest = (char)dch))
834df930be7Sderaadt 				dest++;
835df930be7Sderaadt 		}
836df930be7Sderaadt 	/*
837df930be7Sderaadt 	 * restore all the changed characters
838df930be7Sderaadt 	 */
839df930be7Sderaadt 		while (*tmp) {
840ee3599c7Sespie 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
841df930be7Sderaadt 			tmp++;
842df930be7Sderaadt 		}
843df930be7Sderaadt 	}
844ee3599c7Sespie 	*dest = '\0';
845df930be7Sderaadt }
846