xref: /openbsd/usr.bin/m4/eval.c (revision 423624b7)
1*423624b7Sespie /*	$OpenBSD: eval.c,v 1.23 2000/01/05 16:06:14 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*423624b7Sespie static char rcsid[] = "$OpenBSD: eval.c,v 1.23 2000/01/05 16:06:14 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
113718b194dSespie 
114718b194dSespie 	if (td & RECDEF)
115718b194dSespie 		errx(1, "expanding recursive definition for %s", argv[1]);
116df930be7Sderaadt  /*
117df930be7Sderaadt   * if argc == 3 and argv[2] is null, then we
118df930be7Sderaadt   * have macro-or-builtin() type call. We adjust
119df930be7Sderaadt   * argc to avoid further checking..
120df930be7Sderaadt   */
121df930be7Sderaadt 	if (argc == 3 && !*(argv[2]))
122df930be7Sderaadt 		argc--;
123df930be7Sderaadt 
124718b194dSespie 	switch (td & TYPEMASK) {
125df930be7Sderaadt 
126df930be7Sderaadt 	case DEFITYPE:
127df930be7Sderaadt 		if (argc > 2)
128df930be7Sderaadt 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
129df930be7Sderaadt 		break;
130df930be7Sderaadt 
131df930be7Sderaadt 	case PUSDTYPE:
132df930be7Sderaadt 		if (argc > 2)
133df930be7Sderaadt 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
134df930be7Sderaadt 		break;
135df930be7Sderaadt 
136df930be7Sderaadt 	case DUMPTYPE:
137df930be7Sderaadt 		dodump(argv, argc);
138df930be7Sderaadt 		break;
139df930be7Sderaadt 
140df930be7Sderaadt 	case EXPRTYPE:
141df930be7Sderaadt 	/*
142df930be7Sderaadt 	 * doexpr - evaluate arithmetic
143df930be7Sderaadt 	 * expression
144df930be7Sderaadt 	 */
145df930be7Sderaadt 		if (argc > 2)
146df930be7Sderaadt 			pbnum(expr(argv[2]));
147df930be7Sderaadt 		break;
148df930be7Sderaadt 
149df930be7Sderaadt 	case IFELTYPE:
150df930be7Sderaadt 		if (argc > 4)
151df930be7Sderaadt 			doifelse(argv, argc);
152df930be7Sderaadt 		break;
153df930be7Sderaadt 
154df930be7Sderaadt 	case IFDFTYPE:
155df930be7Sderaadt 	/*
156df930be7Sderaadt 	 * doifdef - select one of two
157df930be7Sderaadt 	 * alternatives based on the existence of
158df930be7Sderaadt 	 * another definition
159df930be7Sderaadt 	 */
160df930be7Sderaadt 		if (argc > 3) {
161df930be7Sderaadt 			if (lookup(argv[2]) != nil)
162df930be7Sderaadt 				pbstr(argv[3]);
163df930be7Sderaadt 			else if (argc > 4)
164df930be7Sderaadt 				pbstr(argv[4]);
165df930be7Sderaadt 		}
166df930be7Sderaadt 		break;
167df930be7Sderaadt 
168df930be7Sderaadt 	case LENGTYPE:
169df930be7Sderaadt 	/*
170df930be7Sderaadt 	 * dolen - find the length of the
171df930be7Sderaadt 	 * argument
172df930be7Sderaadt 	 */
173df930be7Sderaadt 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
174df930be7Sderaadt 		break;
175df930be7Sderaadt 
176df930be7Sderaadt 	case INCRTYPE:
177df930be7Sderaadt 	/*
178df930be7Sderaadt 	 * doincr - increment the value of the
179df930be7Sderaadt 	 * argument
180df930be7Sderaadt 	 */
181df930be7Sderaadt 		if (argc > 2)
182df930be7Sderaadt 			pbnum(atoi(argv[2]) + 1);
183df930be7Sderaadt 		break;
184df930be7Sderaadt 
185df930be7Sderaadt 	case DECRTYPE:
186df930be7Sderaadt 	/*
187df930be7Sderaadt 	 * dodecr - decrement the value of the
188df930be7Sderaadt 	 * argument
189df930be7Sderaadt 	 */
190df930be7Sderaadt 		if (argc > 2)
191df930be7Sderaadt 			pbnum(atoi(argv[2]) - 1);
192df930be7Sderaadt 		break;
193df930be7Sderaadt 
194df930be7Sderaadt 	case SYSCTYPE:
195df930be7Sderaadt 	/*
196df930be7Sderaadt 	 * dosys - execute system command
197df930be7Sderaadt 	 */
198df930be7Sderaadt 		if (argc > 2)
199df930be7Sderaadt 			sysval = system(argv[2]);
200df930be7Sderaadt 		break;
201df930be7Sderaadt 
202df930be7Sderaadt 	case SYSVTYPE:
203df930be7Sderaadt 	/*
204df930be7Sderaadt 	 * dosysval - return value of the last
205df930be7Sderaadt 	 * system call.
206df930be7Sderaadt 	 *
207df930be7Sderaadt 	 */
208df930be7Sderaadt 		pbnum(sysval);
209df930be7Sderaadt 		break;
210df930be7Sderaadt 
211df930be7Sderaadt 	case INCLTYPE:
212df930be7Sderaadt 		if (argc > 2)
213df930be7Sderaadt 			if (!doincl(argv[2]))
21481c2181eSespie 				err(1, "%s", argv[2]);
215df930be7Sderaadt 		break;
216df930be7Sderaadt 
217df930be7Sderaadt 	case SINCTYPE:
218df930be7Sderaadt 		if (argc > 2)
219df930be7Sderaadt 			(void) doincl(argv[2]);
220df930be7Sderaadt 		break;
221df930be7Sderaadt #ifdef EXTENDED
222df930be7Sderaadt 	case PASTTYPE:
223df930be7Sderaadt 		if (argc > 2)
224df930be7Sderaadt 			if (!dopaste(argv[2]))
22581c2181eSespie 				err(1, "%s", argv[2]);
226df930be7Sderaadt 		break;
227df930be7Sderaadt 
228df930be7Sderaadt 	case SPASTYPE:
229df930be7Sderaadt 		if (argc > 2)
230df930be7Sderaadt 			(void) dopaste(argv[2]);
231df930be7Sderaadt 		break;
232df930be7Sderaadt #endif
233df930be7Sderaadt 	case CHNQTYPE:
234df930be7Sderaadt 		dochq(argv, argc);
235df930be7Sderaadt 		break;
236df930be7Sderaadt 
237df930be7Sderaadt 	case CHNCTYPE:
238df930be7Sderaadt 		dochc(argv, argc);
239df930be7Sderaadt 		break;
240df930be7Sderaadt 
241df930be7Sderaadt 	case SUBSTYPE:
242df930be7Sderaadt 	/*
243df930be7Sderaadt 	 * dosub - select substring
244df930be7Sderaadt 	 *
245df930be7Sderaadt 	 */
246df930be7Sderaadt 		if (argc > 3)
247df930be7Sderaadt 			dosub(argv, argc);
248df930be7Sderaadt 		break;
249df930be7Sderaadt 
250df930be7Sderaadt 	case SHIFTYPE:
251df930be7Sderaadt 	/*
252df930be7Sderaadt 	 * doshift - push back all arguments
253df930be7Sderaadt 	 * except the first one (i.e. skip
254df930be7Sderaadt 	 * argv[2])
255df930be7Sderaadt 	 */
256df930be7Sderaadt 		if (argc > 3) {
257df930be7Sderaadt 			for (n = argc - 1; n > 3; n--) {
2583a73db8cSderaadt 				pbstr(rquote);
259df930be7Sderaadt 				pbstr(argv[n]);
2603a73db8cSderaadt 				pbstr(lquote);
261aa676ce1Smillert 				putback(COMMA);
262df930be7Sderaadt 			}
2633a73db8cSderaadt 			pbstr(rquote);
264df930be7Sderaadt 			pbstr(argv[3]);
2653a73db8cSderaadt 			pbstr(lquote);
266df930be7Sderaadt 		}
267df930be7Sderaadt 		break;
268df930be7Sderaadt 
269df930be7Sderaadt 	case DIVRTYPE:
270df930be7Sderaadt 		if (argc > 2 && (n = atoi(argv[2])) != 0)
271df930be7Sderaadt 			dodiv(n);
272df930be7Sderaadt 		else {
273df930be7Sderaadt 			active = stdout;
274df930be7Sderaadt 			oindex = 0;
275df930be7Sderaadt 		}
276df930be7Sderaadt 		break;
277df930be7Sderaadt 
278df930be7Sderaadt 	case UNDVTYPE:
279df930be7Sderaadt 		doundiv(argv, argc);
280df930be7Sderaadt 		break;
281df930be7Sderaadt 
282df930be7Sderaadt 	case DIVNTYPE:
283df930be7Sderaadt 	/*
284df930be7Sderaadt 	 * dodivnum - return the number of
285df930be7Sderaadt 	 * current output diversion
286df930be7Sderaadt 	 */
287df930be7Sderaadt 		pbnum(oindex);
288df930be7Sderaadt 		break;
289df930be7Sderaadt 
290df930be7Sderaadt 	case UNDFTYPE:
291df930be7Sderaadt 	/*
292df930be7Sderaadt 	 * doundefine - undefine a previously
293df930be7Sderaadt 	 * defined macro(s) or m4 keyword(s).
294df930be7Sderaadt 	 */
295df930be7Sderaadt 		if (argc > 2)
296df930be7Sderaadt 			for (n = 2; n < argc; n++)
297df930be7Sderaadt 				remhash(argv[n], ALL);
298df930be7Sderaadt 		break;
299df930be7Sderaadt 
300df930be7Sderaadt 	case POPDTYPE:
301df930be7Sderaadt 	/*
302df930be7Sderaadt 	 * dopopdef - remove the topmost
303df930be7Sderaadt 	 * definitions of macro(s) or m4
304df930be7Sderaadt 	 * keyword(s).
305df930be7Sderaadt 	 */
306df930be7Sderaadt 		if (argc > 2)
307df930be7Sderaadt 			for (n = 2; n < argc; n++)
308df930be7Sderaadt 				remhash(argv[n], TOP);
309df930be7Sderaadt 		break;
310df930be7Sderaadt 
311df930be7Sderaadt 	case MKTMTYPE:
312df930be7Sderaadt 	/*
313df930be7Sderaadt 	 * dotemp - create a temporary file
314df930be7Sderaadt 	 */
31501e71e69Sespie 		if (argc > 2) {
31601e71e69Sespie 			int fd;
317bb34cd6cSespie 			char *temp;
31801e71e69Sespie 
319bb34cd6cSespie 			temp = xstrdup(argv[2]);
320bb34cd6cSespie 
321bb34cd6cSespie 			fd = mkstemp(temp);
32201e71e69Sespie 			if (fd == -1)
32301e71e69Sespie 				err(1, "couldn't make temp file %s", argv[2]);
32401e71e69Sespie 			close(fd);
325bb34cd6cSespie 			pbstr(temp);
326bb34cd6cSespie 			free(temp);
32701e71e69Sespie 		}
328df930be7Sderaadt 		break;
329df930be7Sderaadt 
330df930be7Sderaadt 	case TRNLTYPE:
331df930be7Sderaadt 	/*
332df930be7Sderaadt 	 * dotranslit - replace all characters in
333df930be7Sderaadt 	 * the source string that appears in the
334df930be7Sderaadt 	 * "from" string with the corresponding
335df930be7Sderaadt 	 * characters in the "to" string.
336df930be7Sderaadt 	 */
337df930be7Sderaadt 		if (argc > 3) {
3387d3e0b6bSderaadt 			char temp[STRSPMAX+1];
339df930be7Sderaadt 			if (argc > 4)
340df930be7Sderaadt 				map(temp, argv[2], argv[3], argv[4]);
341df930be7Sderaadt 			else
342df930be7Sderaadt 				map(temp, argv[2], argv[3], null);
343df930be7Sderaadt 			pbstr(temp);
3447d3e0b6bSderaadt 		} else if (argc > 2)
345df930be7Sderaadt 			pbstr(argv[2]);
346df930be7Sderaadt 		break;
347df930be7Sderaadt 
348df930be7Sderaadt 	case INDXTYPE:
349df930be7Sderaadt 	/*
350df930be7Sderaadt 	 * doindex - find the index of the second
351df930be7Sderaadt 	 * argument string in the first argument
352df930be7Sderaadt 	 * string. -1 if not present.
353df930be7Sderaadt 	 */
354df930be7Sderaadt 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
355df930be7Sderaadt 		break;
356df930be7Sderaadt 
357df930be7Sderaadt 	case ERRPTYPE:
358df930be7Sderaadt 	/*
359df930be7Sderaadt 	 * doerrp - print the arguments to stderr
360df930be7Sderaadt 	 * file
361df930be7Sderaadt 	 */
362df930be7Sderaadt 		if (argc > 2) {
363df930be7Sderaadt 			for (n = 2; n < argc; n++)
364df930be7Sderaadt 				fprintf(stderr, "%s ", argv[n]);
365df930be7Sderaadt 			fprintf(stderr, "\n");
366df930be7Sderaadt 		}
367df930be7Sderaadt 		break;
368df930be7Sderaadt 
369df930be7Sderaadt 	case DNLNTYPE:
370df930be7Sderaadt 	/*
371df930be7Sderaadt 	 * dodnl - eat-up-to and including
372df930be7Sderaadt 	 * newline
373df930be7Sderaadt 	 */
374df930be7Sderaadt 		while ((c = gpbc()) != '\n' && c != EOF)
375df930be7Sderaadt 			;
376df930be7Sderaadt 		break;
377df930be7Sderaadt 
378df930be7Sderaadt 	case M4WRTYPE:
379df930be7Sderaadt 	/*
380df930be7Sderaadt 	 * dom4wrap - set up for
381df930be7Sderaadt 	 * wrap-up/wind-down activity
382df930be7Sderaadt 	 */
383df930be7Sderaadt 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
384df930be7Sderaadt 		break;
385df930be7Sderaadt 
386df930be7Sderaadt 	case EXITTYPE:
387df930be7Sderaadt 	/*
388df930be7Sderaadt 	 * doexit - immediate exit from m4.
389df930be7Sderaadt 	 */
390df930be7Sderaadt 		killdiv();
391df930be7Sderaadt 		exit((argc > 2) ? atoi(argv[2]) : 0);
392df930be7Sderaadt 		break;
393df930be7Sderaadt 
394df930be7Sderaadt 	case DEFNTYPE:
395df930be7Sderaadt 		if (argc > 2)
396df930be7Sderaadt 			for (n = 2; n < argc; n++)
397df930be7Sderaadt 				dodefn(argv[n]);
398df930be7Sderaadt 		break;
399df930be7Sderaadt 
400*423624b7Sespie 	case SELFTYPE:
401*423624b7Sespie 		pbstr(rquote);
402*423624b7Sespie 		pbstr(argv[1]);
403*423624b7Sespie 		pbstr(lquote);
404*423624b7Sespie 		break;
405df930be7Sderaadt 	default:
40681c2181eSespie 		errx(1, "eval: major botch.");
407df930be7Sderaadt 		break;
408df930be7Sderaadt 	}
409df930be7Sderaadt }
410df930be7Sderaadt 
411df930be7Sderaadt char *dumpfmt = "`%s'\t`%s'\n";	       /* format string for dumpdef   */
412df930be7Sderaadt 
413df930be7Sderaadt /*
414df930be7Sderaadt  * expand - user-defined macro expansion
415df930be7Sderaadt  */
416df930be7Sderaadt void
417df930be7Sderaadt expand(argv, argc)
418bb34cd6cSespie 	const char *argv[];
41953f6f6bfSespie 	int argc;
420df930be7Sderaadt {
421bb34cd6cSespie 	const char *t;
422bb34cd6cSespie 	const char *p;
42353f6f6bfSespie 	int n;
42453f6f6bfSespie 	int argno;
425df930be7Sderaadt 
426df930be7Sderaadt 	t = argv[0];		       /* defn string as a whole */
427df930be7Sderaadt 	p = t;
428df930be7Sderaadt 	while (*p)
429df930be7Sderaadt 		p++;
430df930be7Sderaadt 	p--;			       /* last character of defn */
431df930be7Sderaadt 	while (p > t) {
432df930be7Sderaadt 		if (*(p - 1) != ARGFLAG)
433df930be7Sderaadt 			putback(*p);
434df930be7Sderaadt 		else {
435df930be7Sderaadt 			switch (*p) {
436df930be7Sderaadt 
437df930be7Sderaadt 			case '#':
438df930be7Sderaadt 				pbnum(argc - 2);
439df930be7Sderaadt 				break;
440df930be7Sderaadt 			case '0':
441df930be7Sderaadt 			case '1':
442df930be7Sderaadt 			case '2':
443df930be7Sderaadt 			case '3':
444df930be7Sderaadt 			case '4':
445df930be7Sderaadt 			case '5':
446df930be7Sderaadt 			case '6':
447df930be7Sderaadt 			case '7':
448df930be7Sderaadt 			case '8':
449df930be7Sderaadt 			case '9':
450df930be7Sderaadt 				if ((argno = *p - '0') < argc - 1)
451df930be7Sderaadt 					pbstr(argv[argno + 1]);
452df930be7Sderaadt 				break;
453df930be7Sderaadt 			case '*':
454df930be7Sderaadt 				for (n = argc - 1; n > 2; n--) {
455df930be7Sderaadt 					pbstr(argv[n]);
456aa676ce1Smillert 					putback(COMMA);
457df930be7Sderaadt 				}
458df930be7Sderaadt 				pbstr(argv[2]);
459df930be7Sderaadt 				break;
460aa676ce1Smillert                         case '@':
461aa676ce1Smillert                                 for (n = argc - 1; n > 2; n--) {
462aa676ce1Smillert                                         pbstr(rquote);
463aa676ce1Smillert                                         pbstr(argv[n]);
464aa676ce1Smillert                                         pbstr(lquote);
465aa676ce1Smillert 					putback(COMMA);
466aa676ce1Smillert                                 }
467aa676ce1Smillert 				pbstr(rquote);
468aa676ce1Smillert                                 pbstr(argv[2]);
469aa676ce1Smillert 				pbstr(lquote);
470aa676ce1Smillert                                 break;
471df930be7Sderaadt 			default:
472df930be7Sderaadt 				putback(*p);
473df930be7Sderaadt 				putback('$');
474df930be7Sderaadt 				break;
475df930be7Sderaadt 			}
476df930be7Sderaadt 			p--;
477df930be7Sderaadt 		}
478df930be7Sderaadt 		p--;
479df930be7Sderaadt 	}
480df930be7Sderaadt 	if (p == t)		       /* do last character */
481df930be7Sderaadt 		putback(*p);
482df930be7Sderaadt }
483df930be7Sderaadt 
484df930be7Sderaadt /*
485df930be7Sderaadt  * dodefine - install definition in the table
486df930be7Sderaadt  */
487df930be7Sderaadt void
488df930be7Sderaadt dodefine(name, defn)
489bb34cd6cSespie 	const char *name;
490bb34cd6cSespie 	const char *defn;
491df930be7Sderaadt {
49253f6f6bfSespie 	ndptr p;
493df930be7Sderaadt 
494df930be7Sderaadt 	if (!*name)
49581c2181eSespie 		errx(1, "null definition.");
496df930be7Sderaadt 	if ((p = lookup(name)) == nil)
497df930be7Sderaadt 		p = addent(name);
498df930be7Sderaadt 	else if (p->defn != null)
499df930be7Sderaadt 		free((char *) p->defn);
500df930be7Sderaadt 	if (!*defn)
501df930be7Sderaadt 		p->defn = null;
502df930be7Sderaadt 	else
503df930be7Sderaadt 		p->defn = xstrdup(defn);
504df930be7Sderaadt 	p->type = MACRTYPE;
505718b194dSespie 	if (STREQ(name, defn))
506718b194dSespie 		p->type |= RECDEF;
507df930be7Sderaadt }
508df930be7Sderaadt 
509df930be7Sderaadt /*
510df930be7Sderaadt  * dodefn - push back a quoted definition of
511df930be7Sderaadt  *      the given name.
512df930be7Sderaadt  */
513bb34cd6cSespie static void
514df930be7Sderaadt dodefn(name)
515bb34cd6cSespie 	const char *name;
516df930be7Sderaadt {
51753f6f6bfSespie 	ndptr p;
518df930be7Sderaadt 
519df930be7Sderaadt 	if ((p = lookup(name)) != nil && p->defn != null) {
5203a73db8cSderaadt 		pbstr(rquote);
521df930be7Sderaadt 		pbstr(p->defn);
5223a73db8cSderaadt 		pbstr(lquote);
523df930be7Sderaadt 	}
524df930be7Sderaadt }
525df930be7Sderaadt 
526df930be7Sderaadt /*
527df930be7Sderaadt  * dopushdef - install a definition in the hash table
528df930be7Sderaadt  *      without removing a previous definition. Since
529df930be7Sderaadt  *      each new entry is entered in *front* of the
530df930be7Sderaadt  *      hash bucket, it hides a previous definition from
531df930be7Sderaadt  *      lookup.
532df930be7Sderaadt  */
533bb34cd6cSespie static void
534df930be7Sderaadt dopushdef(name, defn)
535bb34cd6cSespie 	const char *name;
536bb34cd6cSespie 	const char *defn;
537df930be7Sderaadt {
53853f6f6bfSespie 	ndptr p;
539df930be7Sderaadt 
540df930be7Sderaadt 	if (!*name)
54181c2181eSespie 		errx(1, "null definition");
542df930be7Sderaadt 	p = addent(name);
543df930be7Sderaadt 	if (!*defn)
544df930be7Sderaadt 		p->defn = null;
545df930be7Sderaadt 	else
546df930be7Sderaadt 		p->defn = xstrdup(defn);
547df930be7Sderaadt 	p->type = MACRTYPE;
548718b194dSespie 	if (STREQ(name, defn))
549718b194dSespie 		p->type |= RECDEF;
550df930be7Sderaadt }
551df930be7Sderaadt 
552df930be7Sderaadt /*
553df930be7Sderaadt  * dodumpdef - dump the specified definitions in the hash
554df930be7Sderaadt  *      table to stderr. If nothing is specified, the entire
555df930be7Sderaadt  *      hash table is dumped.
556df930be7Sderaadt  */
557bb34cd6cSespie static void
558df930be7Sderaadt dodump(argv, argc)
559bb34cd6cSespie 	const char *argv[];
56053f6f6bfSespie 	int argc;
561df930be7Sderaadt {
56253f6f6bfSespie 	int n;
563df930be7Sderaadt 	ndptr p;
564df930be7Sderaadt 
565df930be7Sderaadt 	if (argc > 2) {
566df930be7Sderaadt 		for (n = 2; n < argc; n++)
567df930be7Sderaadt 			if ((p = lookup(argv[n])) != nil)
568df930be7Sderaadt 				fprintf(stderr, dumpfmt, p->name,
569df930be7Sderaadt 					p->defn);
5707d3e0b6bSderaadt 	} else {
571df930be7Sderaadt 		for (n = 0; n < HASHSIZE; n++)
572df930be7Sderaadt 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
573df930be7Sderaadt 				fprintf(stderr, dumpfmt, p->name,
574df930be7Sderaadt 					p->defn);
575df930be7Sderaadt 	}
576df930be7Sderaadt }
577df930be7Sderaadt 
578df930be7Sderaadt /*
579df930be7Sderaadt  * doifelse - select one of two alternatives - loop.
580df930be7Sderaadt  */
581bb34cd6cSespie static void
582df930be7Sderaadt doifelse(argv, argc)
583bb34cd6cSespie 	const char *argv[];
58453f6f6bfSespie 	int argc;
585df930be7Sderaadt {
586df930be7Sderaadt 	cycle {
587df930be7Sderaadt 		if (STREQ(argv[2], argv[3]))
588df930be7Sderaadt 			pbstr(argv[4]);
589df930be7Sderaadt 		else if (argc == 6)
590df930be7Sderaadt 			pbstr(argv[5]);
591df930be7Sderaadt 		else if (argc > 6) {
592df930be7Sderaadt 			argv += 3;
593df930be7Sderaadt 			argc -= 3;
594df930be7Sderaadt 			continue;
595df930be7Sderaadt 		}
596df930be7Sderaadt 		break;
597df930be7Sderaadt 	}
598df930be7Sderaadt }
599df930be7Sderaadt 
600df930be7Sderaadt /*
601df930be7Sderaadt  * doinclude - include a given file.
602df930be7Sderaadt  */
603bb34cd6cSespie static int
604df930be7Sderaadt doincl(ifile)
605bb34cd6cSespie 	const char *ifile;
606df930be7Sderaadt {
607df930be7Sderaadt 	if (ilevel + 1 == MAXINP)
60881c2181eSespie 		errx(1, "too many include files.");
6094a769388Sespie 	if ((infile[ilevel + 1] = fopen_trypath(ifile)) != NULL) {
610df930be7Sderaadt 		ilevel++;
611df930be7Sderaadt 		bbase[ilevel] = bufbase = bp;
612df930be7Sderaadt 		return (1);
6137d3e0b6bSderaadt 	} else
614df930be7Sderaadt 		return (0);
615df930be7Sderaadt }
616df930be7Sderaadt 
617df930be7Sderaadt #ifdef EXTENDED
618df930be7Sderaadt /*
619df930be7Sderaadt  * dopaste - include a given file without any
620df930be7Sderaadt  *           macro processing.
621df930be7Sderaadt  */
622bb34cd6cSespie static int
623df930be7Sderaadt dopaste(pfile)
624bb34cd6cSespie 	const char *pfile;
625df930be7Sderaadt {
626df930be7Sderaadt 	FILE *pf;
62753f6f6bfSespie 	int c;
628df930be7Sderaadt 
629df930be7Sderaadt 	if ((pf = fopen(pfile, "r")) != NULL) {
630df930be7Sderaadt 		while ((c = getc(pf)) != EOF)
631df930be7Sderaadt 			putc(c, active);
632df930be7Sderaadt 		(void) fclose(pf);
633df930be7Sderaadt 		return (1);
6347d3e0b6bSderaadt 	} else
635df930be7Sderaadt 		return (0);
636df930be7Sderaadt }
637df930be7Sderaadt #endif
638df930be7Sderaadt 
639df930be7Sderaadt /*
640df930be7Sderaadt  * dochq - change quote characters
641df930be7Sderaadt  */
642bb34cd6cSespie static void
643df930be7Sderaadt dochq(argv, argc)
644bb34cd6cSespie 	const char *argv[];
64553f6f6bfSespie 	int argc;
646df930be7Sderaadt {
647df930be7Sderaadt 	if (argc > 2) {
64818a1973bSderaadt 		if (*argv[2])
649b81b15b2Sespie 			strlcpy(lquote, argv[2], sizeof(lquote));
65018a1973bSderaadt 		else {
65118a1973bSderaadt 			lquote[0] = LQUOTE;
652f0484631Sespie 			lquote[1] = EOS;
65318a1973bSderaadt 		}
654df930be7Sderaadt 		if (argc > 3) {
655df930be7Sderaadt 			if (*argv[3])
656b81b15b2Sespie 				strlcpy(rquote, argv[3], sizeof(rquote));
6577d3e0b6bSderaadt 		} else
65829a0bfdcSderaadt 			strcpy(rquote, lquote);
6597d3e0b6bSderaadt 	} else {
660f0484631Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
661f0484631Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
662df930be7Sderaadt 	}
663df930be7Sderaadt }
664df930be7Sderaadt 
665df930be7Sderaadt /*
666df930be7Sderaadt  * dochc - change comment characters
667df930be7Sderaadt  */
668bb34cd6cSespie static void
669df930be7Sderaadt dochc(argv, argc)
670bb34cd6cSespie 	const char *argv[];
67153f6f6bfSespie 	int argc;
672df930be7Sderaadt {
673df930be7Sderaadt 	if (argc > 2) {
674df930be7Sderaadt 		if (*argv[2])
675b81b15b2Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
676df930be7Sderaadt 		if (argc > 3) {
677df930be7Sderaadt 			if (*argv[3])
678b81b15b2Sespie 				strlcpy(ecommt, argv[3], sizeof(ecommt));
679df930be7Sderaadt 		}
680df930be7Sderaadt 		else
681f0484631Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
682df930be7Sderaadt 	}
683df930be7Sderaadt 	else {
684f0484631Sespie 		scommt[0] = SCOMMT, scommt[1] = EOS;
685f0484631Sespie 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
686df930be7Sderaadt 	}
687df930be7Sderaadt }
688df930be7Sderaadt 
689df930be7Sderaadt /*
690df930be7Sderaadt  * dodivert - divert the output to a temporary file
691df930be7Sderaadt  */
692bb34cd6cSespie static void
693df930be7Sderaadt dodiv(n)
69453f6f6bfSespie 	int n;
695df930be7Sderaadt {
696445b77f7Smillert 	int fd;
697445b77f7Smillert 
6987d3e0b6bSderaadt 	oindex = n;
699df930be7Sderaadt 	if (n < 0 || n >= MAXOUT)
700df930be7Sderaadt 		n = 0;		       /* bitbucket */
701df930be7Sderaadt 	if (outfile[n] == NULL) {
7023f42598dSespie 		char fname[] = _PATH_DIVNAME;
7033f42598dSespie 
7043f42598dSespie 		if ((fd = mkstemp(fname)) < 0 ||
7053f42598dSespie 			(outfile[n] = fdopen(fd, "w+")) == NULL)
7063f42598dSespie 				err(1, "%s: cannot divert", fname);
7073f42598dSespie 		if (unlink(fname) == -1)
7083f42598dSespie 			err(1, "%s: cannot unlink", fname);
709df930be7Sderaadt 	}
710df930be7Sderaadt 	active = outfile[n];
711df930be7Sderaadt }
712df930be7Sderaadt 
713df930be7Sderaadt /*
714df930be7Sderaadt  * doundivert - undivert a specified output, or all
715df930be7Sderaadt  *              other outputs, in numerical order.
716df930be7Sderaadt  */
717bb34cd6cSespie static void
718df930be7Sderaadt doundiv(argv, argc)
719bb34cd6cSespie 	const char *argv[];
72053f6f6bfSespie 	int argc;
721df930be7Sderaadt {
72253f6f6bfSespie 	int ind;
72353f6f6bfSespie 	int n;
724df930be7Sderaadt 
725df930be7Sderaadt 	if (argc > 2) {
726df930be7Sderaadt 		for (ind = 2; ind < argc; ind++) {
727df930be7Sderaadt 			n = atoi(argv[ind]);
728df930be7Sderaadt 			if (n > 0 && n < MAXOUT && outfile[n] != NULL)
729df930be7Sderaadt 				getdiv(n);
730df930be7Sderaadt 
731df930be7Sderaadt 		}
732df930be7Sderaadt 	}
733df930be7Sderaadt 	else
734df930be7Sderaadt 		for (n = 1; n < MAXOUT; n++)
735df930be7Sderaadt 			if (outfile[n] != NULL)
736df930be7Sderaadt 				getdiv(n);
737df930be7Sderaadt }
738df930be7Sderaadt 
739df930be7Sderaadt /*
740df930be7Sderaadt  * dosub - select substring
741df930be7Sderaadt  */
742bb34cd6cSespie static void
743df930be7Sderaadt dosub(argv, argc)
744bb34cd6cSespie 	const char *argv[];
74553f6f6bfSespie 	int argc;
746df930be7Sderaadt {
747bb34cd6cSespie 	const char *ap, *fc, *k;
74853f6f6bfSespie 	int nc;
749df930be7Sderaadt 
750df930be7Sderaadt 	if (argc < 5)
751df930be7Sderaadt 		nc = MAXTOK;
752df930be7Sderaadt 	else
753df930be7Sderaadt #ifdef EXPR
754df930be7Sderaadt 		nc = expr(argv[4]);
755df930be7Sderaadt #else
756df930be7Sderaadt 		nc = atoi(argv[4]);
757df930be7Sderaadt #endif
758df930be7Sderaadt 	ap = argv[2];		       /* target string */
759df930be7Sderaadt #ifdef EXPR
760df930be7Sderaadt 	fc = ap + expr(argv[3]);       /* first char */
761df930be7Sderaadt #else
762df930be7Sderaadt 	fc = ap + atoi(argv[3]);       /* first char */
763df930be7Sderaadt #endif
764df930be7Sderaadt 	if (fc >= ap && fc < ap + strlen(ap))
765df930be7Sderaadt 		for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
766df930be7Sderaadt 			putback(*k);
767df930be7Sderaadt }
768df930be7Sderaadt 
769df930be7Sderaadt /*
770df930be7Sderaadt  * map:
771df930be7Sderaadt  * map every character of s1 that is specified in from
772df930be7Sderaadt  * into s3 and replace in s. (source s1 remains untouched)
773df930be7Sderaadt  *
774df930be7Sderaadt  * This is a standard implementation of map(s,from,to) function of ICON
775df930be7Sderaadt  * language. Within mapvec, we replace every character of "from" with
776df930be7Sderaadt  * the corresponding character in "to". If "to" is shorter than "from",
777df930be7Sderaadt  * than the corresponding entries are null, which means that those
778df930be7Sderaadt  * characters dissapear altogether. Furthermore, imagine
779df930be7Sderaadt  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
780df930be7Sderaadt  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
781df930be7Sderaadt  * ultimately maps to `*'. In order to achieve this effect in an efficient
782df930be7Sderaadt  * manner (i.e. without multiple passes over the destination string), we
783df930be7Sderaadt  * loop over mapvec, starting with the initial source character. if the
784df930be7Sderaadt  * character value (dch) in this location is different than the source
785df930be7Sderaadt  * character (sch), sch becomes dch, once again to index into mapvec, until
786df930be7Sderaadt  * the character value stabilizes (i.e. sch = dch, in other words
787df930be7Sderaadt  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
788df930be7Sderaadt  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
789df930be7Sderaadt  * end, we restore mapvec* back to normal where mapvec[n] == n for
790df930be7Sderaadt  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
791df930be7Sderaadt  * about 5 times faster than any algorithm that makes multiple passes over
792df930be7Sderaadt  * destination string.
793df930be7Sderaadt  */
794bb34cd6cSespie static void
795df930be7Sderaadt map(dest, src, from, to)
79653f6f6bfSespie 	char *dest;
797bb34cd6cSespie 	const char *src;
798bb34cd6cSespie 	const char *from;
799bb34cd6cSespie 	const char *to;
800df930be7Sderaadt {
801bb34cd6cSespie 	const char *tmp;
802ee3599c7Sespie 	unsigned char sch, dch;
803ee3599c7Sespie 	static unsigned char mapvec[256] = {
804ee3599c7Sespie 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
805ee3599c7Sespie 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
806ee3599c7Sespie 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
807ee3599c7Sespie 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
808ee3599c7Sespie 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
809ee3599c7Sespie 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
810ee3599c7Sespie 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
811ee3599c7Sespie 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
812ee3599c7Sespie 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
813ee3599c7Sespie 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
814ee3599c7Sespie 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
815ee3599c7Sespie 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
816ee3599c7Sespie 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
817ee3599c7Sespie 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
818ee3599c7Sespie 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
819ee3599c7Sespie 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
820ee3599c7Sespie 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
821ee3599c7Sespie 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
822df930be7Sderaadt 	};
823df930be7Sderaadt 
824df930be7Sderaadt 	if (*src) {
825df930be7Sderaadt 		tmp = from;
826df930be7Sderaadt 	/*
827df930be7Sderaadt 	 * create a mapping between "from" and
828df930be7Sderaadt 	 * "to"
829df930be7Sderaadt 	 */
830df930be7Sderaadt 		while (*from)
831ee3599c7Sespie 			mapvec[(unsigned char)(*from++)] = (*to) ?
832ee3599c7Sespie 				(unsigned char)(*to++) : 0;
833df930be7Sderaadt 
834df930be7Sderaadt 		while (*src) {
835ee3599c7Sespie 			sch = (unsigned char)(*src++);
836df930be7Sderaadt 			dch = mapvec[sch];
837df930be7Sderaadt 			while (dch != sch) {
838df930be7Sderaadt 				sch = dch;
839df930be7Sderaadt 				dch = mapvec[sch];
840df930be7Sderaadt 			}
841ee3599c7Sespie 			if ((*dest = (char)dch))
842df930be7Sderaadt 				dest++;
843df930be7Sderaadt 		}
844df930be7Sderaadt 	/*
845df930be7Sderaadt 	 * restore all the changed characters
846df930be7Sderaadt 	 */
847df930be7Sderaadt 		while (*tmp) {
848ee3599c7Sespie 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
849df930be7Sderaadt 			tmp++;
850df930be7Sderaadt 		}
851df930be7Sderaadt 	}
852ee3599c7Sespie 	*dest = '\0';
853df930be7Sderaadt }
854