eval.c revision 100014
1162413Ssam/*	$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $	*/
2178354Ssam/*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
3162413Ssam
4162413Ssam/*
5162413Ssam * Copyright (c) 1989, 1993
6162413Ssam *	The Regents of the University of California.  All rights reserved.
7162413Ssam *
8162413Ssam * This code is derived from software contributed to Berkeley by
9162413Ssam * Ozan Yigit at York University.
10162413Ssam *
11162413Ssam * Redistribution and use in source and binary forms, with or without
12162413Ssam * modification, are permitted provided that the following conditions
13162413Ssam * are met:
14162413Ssam * 1. Redistributions of source code must retain the above copyright
15162413Ssam *    notice, this list of conditions and the following disclaimer.
16162413Ssam * 2. Redistributions in binary form must reproduce the above copyright
17162413Ssam *    notice, this list of conditions and the following disclaimer in the
18162413Ssam *    documentation and/or other materials provided with the distribution.
19162413Ssam * 3. All advertising materials mentioning features or use of this software
20162413Ssam *    must display the following acknowledgement:
21162413Ssam *	This product includes software developed by the University of
22162413Ssam *	California, Berkeley and its contributors.
23162413Ssam * 4. Neither the name of the University nor the names of its contributors
24162413Ssam *    may be used to endorse or promote products derived from this software
25162413Ssam *    without specific prior written permission.
26162413Ssam *
27162413Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28162413Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29162413Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30162413Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31162413Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32162413Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33162413Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34162413Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35162413Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36162413Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37162413Ssam * SUCH DAMAGE.
38162413Ssam */
39162413Ssam
40162413Ssam#ifndef lint
41162413Ssam#if 0
42162413Ssamstatic char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
43162413Ssam#else
44162413Ssam#if 0
45162413Ssamstatic char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $";
46185522Ssam#endif
47162413Ssam#endif
48162413Ssam#endif /* not lint */
49162413Ssam
50162413Ssam#include <sys/cdefs.h>
51162413Ssam__FBSDID("$FreeBSD: head/usr.bin/m4/eval.c 100014 2002-07-15 02:15:12Z jmallett $");
52162413Ssam
53162413Ssam/*
54162413Ssam * eval.c
55162413Ssam * Facility: m4 macro processor
56162413Ssam * by: oz
57162413Ssam */
58162413Ssam
59185522Ssam#include <sys/types.h>
60162413Ssam#include <errno.h>
61162413Ssam#include <unistd.h>
62162413Ssam#include <stdio.h>
63162413Ssam#include <stdlib.h>
64162413Ssam#include <stddef.h>
65162413Ssam#include <string.h>
66162413Ssam#include <fcntl.h>
67162413Ssam#include <err.h>
68162413Ssam#include "mdef.h"
69162413Ssam#include "stdd.h"
70162413Ssam#include "extern.h"
71162413Ssam#include "pathnames.h"
72162413Ssam
73162413Ssam#define BUILTIN_MARKER	"__builtin_"
74219315Sadrian
75162413Ssamstatic void	dodefn(const char *);
76162413Ssamstatic void	dopushdef(const char *, const char *);
77162413Ssamstatic void	dodump(const char *[], int);
78162413Ssamstatic void	dotrace(const char *[], int, int);
79162413Ssamstatic void	doifelse(const char *[], int);
80162413Ssamstatic int	doincl(const char *);
81162413Ssamstatic int	dopaste(const char *);
82219315Sadrianstatic void	gnu_dochq(const char *[], int);
83162413Ssamstatic void	dochq(const char *[], int);
84162413Ssamstatic void	gnu_dochc(const char *[], int);
85162413Ssamstatic void	dochc(const char *[], int);
86162413Ssamstatic void	dodiv(int);
87162413Ssamstatic void	doundiv(const char *[], int);
88219942Sadrianstatic void	dosub(const char *[], int);
89219942Sadrianstatic void	map(char *, const char *, const char *, const char *);
90219948Sadrianstatic const char *handledash(char *, char *, const char *);
91219942Sadrianstatic void	expand_builtin(const char *[], int, int);
92219942Sadrianstatic void	expand_macro(const char *[], int);
93162413Ssamstatic void	dump_one_def(ndptr);
94162413Ssam
95162413Ssamunsigned long	expansion_id;
96162413Ssam
97162413Ssam/*
98162413Ssam * eval - eval all macros and builtins calls
99162413Ssam *	  argc - number of elements in argv.
100162413Ssam *	  argv - element vector :
101162413Ssam *			argv[0] = definition of a user
102162413Ssam *				  macro or nil if built-in.
103162413Ssam *			argv[1] = name of the macro or
104162413Ssam *				  built-in.
105162413Ssam *			argv[2] = parameters to user-defined
106162413Ssam *			   .	  macro or built-in.
107162413Ssam *			   .
108162413Ssam *
109162413Ssam * A call in the form of macro-or-builtin() will result in:
110162413Ssam *			argv[0] = nullstr
111162413Ssam *			argv[1] = macro-or-builtin
112162413Ssam *			argv[2] = nullstr
113162413Ssam *
114162413Ssam * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
115162413Ssam */
116162413Ssamvoid
117162413Ssameval(const char *argv[], int argc, int td)
118196935Ssam{
119162413Ssam	ssize_t mark = -1;
120162413Ssam
121162413Ssam	expansion_id++;
122162413Ssam	if (td & RECDEF)
123162413Ssam		errx(1, "%s at line %lu: expanding recursive definition for %s",
124162413Ssam			CURRENT_NAME, CURRENT_LINE, argv[1]);
125162413Ssam	if (traced_macros && is_traced(argv[1]))
126162413Ssam		mark = trace(argv, argc, infile+ilevel);
127162413Ssam	if (td == MACRTYPE)
128162413Ssam		expand_macro(argv, argc);
129162413Ssam	else
130162413Ssam		expand_builtin(argv, argc, td);
131162413Ssam    	if (mark != -1)
132162413Ssam		finish_trace(mark);
133162413Ssam}
134162413Ssam
135162413Ssam/*
136162413Ssam * expand_builtin - evaluate built-in macros.
137162413Ssam */
138162413Ssamvoid
139162413Ssamexpand_builtin(const char *argv[], int argc, int td)
140162413Ssam{
141162413Ssam	int c, n;
142162413Ssam	int ac;
143162413Ssam	static int sysval = 0;
144219315Sadrian
145184369Ssam#ifdef DEBUG
146184369Ssam	printf("argc = %d\n", argc);
147184369Ssam	for (n = 0; n < argc; n++)
148184369Ssam		printf("argv[%d] = %s\n", n, argv[n]);
149184369Ssam	fflush(stdout);
150184369Ssam#endif
151184369Ssam
152184369Ssam /*
153162413Ssam  * if argc == 3 and argv[2] is null, then we
154162413Ssam  * have macro-or-builtin() type call. We adjust
155162413Ssam  * argc to avoid further checking..
156162413Ssam  */
157162413Ssam  	ac = argc;
158162413Ssam
159162413Ssam	if (argc == 3 && !*(argv[2]))
160162413Ssam		argc--;
161162413Ssam
162162413Ssam	switch (td & TYPEMASK) {
163162413Ssam
164162413Ssam	case DEFITYPE:
165162413Ssam		if (argc > 2)
166162413Ssam			dodefine(argv[2], (argc > 3) ? argv[3] : null);
167162413Ssam		break;
168162413Ssam
169162413Ssam	case PUSDTYPE:
170162413Ssam		if (argc > 2)
171185522Ssam			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
172162413Ssam		break;
173162413Ssam
174162413Ssam	case DUMPTYPE:
175162413Ssam		dodump(argv, argc);
176220367Sadrian		break;
177220367Sadrian
178220367Sadrian	case TRACEONTYPE:
179220367Sadrian		dotrace(argv, argc, 1);
180220367Sadrian		break;
181162413Ssam
182162413Ssam	case TRACEOFFTYPE:
183162413Ssam		dotrace(argv, argc, 0);
184162413Ssam		break;
185162413Ssam
186162413Ssam	case EXPRTYPE:
187162413Ssam	/*
188162413Ssam	 * doexpr - evaluate arithmetic
189168589Srwatson	 * expression
190168589Srwatson	 */
191168589Srwatson		if (argc > 2)
192168589Srwatson			pbnum(expr(argv[2]));
193168589Srwatson		break;
194168589Srwatson
195168589Srwatson	case IFELTYPE:
196162413Ssam		if (argc > 4)
197162413Ssam			doifelse(argv, argc);
198162413Ssam		break;
199162413Ssam
200162413Ssam	case IFDFTYPE:
201162413Ssam	/*
202162413Ssam	 * doifdef - select one of two
203162413Ssam	 * alternatives based on the existence of
204162413Ssam	 * another definition
205162413Ssam	 */
206162413Ssam		if (argc > 3) {
207162413Ssam			if (lookup(argv[2]) != nil)
208162413Ssam				pbstr(argv[3]);
209162413Ssam			else if (argc > 4)
210162413Ssam				pbstr(argv[4]);
211162413Ssam		}
212162413Ssam		break;
213162413Ssam
214162413Ssam	case LENGTYPE:
215162413Ssam	/*
216162413Ssam	 * dolen - find the length of the
217162413Ssam	 * argument
218162413Ssam	 */
219162413Ssam		pbnum((argc > 2) ? strlen(argv[2]) : 0);
220162413Ssam		break;
221162413Ssam
222162413Ssam	case INCRTYPE:
223162413Ssam	/*
224162413Ssam	 * doincr - increment the value of the
225162413Ssam	 * argument
226162413Ssam	 */
227162413Ssam		if (argc > 2)
228162413Ssam			pbnum(atoi(argv[2]) + 1);
229162413Ssam		break;
230162413Ssam
231162413Ssam	case DECRTYPE:
232162413Ssam	/*
233162413Ssam	 * dodecr - decrement the value of the
234162413Ssam	 * argument
235162413Ssam	 */
236162413Ssam		if (argc > 2)
237162413Ssam			pbnum(atoi(argv[2]) - 1);
238162413Ssam		break;
239162413Ssam
240162413Ssam	case SYSCTYPE:
241162413Ssam	/*
242162413Ssam	 * dosys - execute system command
243162413Ssam	 */
244162413Ssam		if (argc > 2)
245162413Ssam			sysval = system(argv[2]);
246162413Ssam		break;
247162413Ssam
248162413Ssam	case SYSVTYPE:
249162413Ssam	/*
250162413Ssam	 * dosysval - return value of the last
251162413Ssam	 * system call.
252162413Ssam	 *
253185522Ssam	 */
254162413Ssam		pbnum(sysval);
255162413Ssam		break;
256162413Ssam
257162413Ssam	case ESYSCMDTYPE:
258162413Ssam		if (argc > 2)
259162413Ssam			doesyscmd(argv[2]);
260162413Ssam	    	break;
261162413Ssam	case INCLTYPE:
262162413Ssam		if (argc > 2)
263162413Ssam			if (!doincl(argv[2]))
264162413Ssam				err(1, "%s at line %lu: include(%s)",
265162413Ssam				    CURRENT_NAME, CURRENT_LINE, argv[2]);
266195418Ssam		break;
267162413Ssam
268162413Ssam	case SINCTYPE:
269162413Ssam		if (argc > 2)
270162413Ssam			(void) doincl(argv[2]);
271162413Ssam		break;
272162413Ssam#ifdef EXTENDED
273162413Ssam	case PASTTYPE:
274162413Ssam		if (argc > 2)
275162413Ssam			if (!dopaste(argv[2]))
276162413Ssam				err(1, "%s at line %lu: paste(%s)",
277185522Ssam				    CURRENT_NAME, CURRENT_LINE, argv[2]);
278162413Ssam		break;
279162413Ssam
280162413Ssam	case SPASTYPE:
281195418Ssam		if (argc > 2)
282162413Ssam			(void) dopaste(argv[2]);
283162413Ssam		break;
284162413Ssam#endif
285162413Ssam	case CHNQTYPE:
286162413Ssam		if (mimic_gnu)
287162413Ssam			gnu_dochq(argv, ac);
288162413Ssam		else
289162413Ssam			dochq(argv, argc);
290162413Ssam		break;
291162413Ssam
292162413Ssam	case CHNCTYPE:
293162413Ssam		if (mimic_gnu)
294162413Ssam			gnu_dochc(argv, ac);
295162413Ssam		else
296162413Ssam			dochc(argv, argc);
297162413Ssam		break;
298162413Ssam
299162413Ssam	case SUBSTYPE:
300162413Ssam	/*
301162413Ssam	 * dosub - select substring
302162413Ssam	 *
303162413Ssam	 */
304162413Ssam		if (argc > 3)
305162413Ssam			dosub(argv, argc);
306162413Ssam		break;
307162413Ssam
308162413Ssam	case SHIFTYPE:
309162413Ssam	/*
310162413Ssam	 * doshift - push back all arguments
311162413Ssam	 * except the first one (i.e. skip
312162413Ssam	 * argv[2])
313162413Ssam	 */
314162413Ssam		if (argc > 3) {
315162413Ssam			for (n = argc - 1; n > 3; n--) {
316162413Ssam				pbstr(rquote);
317162413Ssam				pbstr(argv[n]);
318162413Ssam				pbstr(lquote);
319162413Ssam				putback(COMMA);
320162413Ssam			}
321162413Ssam			pbstr(rquote);
322162413Ssam			pbstr(argv[3]);
323162413Ssam			pbstr(lquote);
324162413Ssam		}
325162413Ssam		break;
326162413Ssam
327162413Ssam	case DIVRTYPE:
328162413Ssam		if (argc > 2 && (n = atoi(argv[2])) != 0)
329185522Ssam			dodiv(n);
330162413Ssam		else {
331162413Ssam			active = stdout;
332195418Ssam			oindex = 0;
333162413Ssam		}
334162413Ssam		break;
335162413Ssam
336162413Ssam	case UNDVTYPE:
337162413Ssam		doundiv(argv, argc);
338162413Ssam		break;
339162413Ssam
340162413Ssam	case DIVNTYPE:
341162413Ssam	/*
342162413Ssam	 * dodivnum - return the number of
343185522Ssam	 * current output diversion
344162413Ssam	 */
345162413Ssam		pbnum(oindex);
346162413Ssam		break;
347195418Ssam
348162413Ssam	case UNDFTYPE:
349162413Ssam	/*
350162413Ssam	 * doundefine - undefine a previously
351162413Ssam	 * defined macro(s) or m4 keyword(s).
352162413Ssam	 */
353162413Ssam		if (argc > 2)
354162413Ssam			for (n = 2; n < argc; n++)
355162413Ssam				remhash(argv[n], ALL);
356162413Ssam		break;
357162413Ssam
358162413Ssam	case POPDTYPE:
359162413Ssam	/*
360162413Ssam	 * dopopdef - remove the topmost
361162413Ssam	 * definitions of macro(s) or m4
362162413Ssam	 * keyword(s).
363162413Ssam	 */
364162413Ssam		if (argc > 2)
365			for (n = 2; n < argc; n++)
366				remhash(argv[n], TOP);
367		break;
368
369	case MKTMTYPE:
370	/*
371	 * dotemp - create a temporary file
372	 */
373		if (argc > 2) {
374			int fd;
375			char *temp;
376
377			temp = xstrdup(argv[2]);
378
379			fd = mkstemp(temp);
380			if (fd == -1)
381				err(1,
382	    "%s at line %lu: couldn't make temp file %s",
383	    CURRENT_NAME, CURRENT_LINE, argv[2]);
384			close(fd);
385			pbstr(temp);
386			free(temp);
387		}
388		break;
389
390	case TRNLTYPE:
391	/*
392	 * dotranslit - replace all characters in
393	 * the source string that appears in the
394	 * "from" string with the corresponding
395	 * characters in the "to" string.
396	 */
397		if (argc > 3) {
398			char *temp;
399
400			temp = xalloc(strlen(argv[2])+1);
401			if (argc > 4)
402				map(temp, argv[2], argv[3], argv[4]);
403			else
404				map(temp, argv[2], argv[3], null);
405			pbstr(temp);
406			free(temp);
407		} else if (argc > 2)
408			pbstr(argv[2]);
409		break;
410
411	case INDXTYPE:
412	/*
413	 * doindex - find the index of the second
414	 * argument string in the first argument
415	 * string. -1 if not present.
416	 */
417		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
418		break;
419
420	case ERRPTYPE:
421	/*
422	 * doerrp - print the arguments to stderr
423	 * file
424	 */
425		if (argc > 2) {
426			for (n = 2; n < argc; n++)
427				fprintf(stderr, "%s ", argv[n]);
428			fprintf(stderr, "\n");
429		}
430		break;
431
432	case DNLNTYPE:
433	/*
434	 * dodnl - eat-up-to and including
435	 * newline
436	 */
437		while ((c = gpbc()) != '\n' && c != EOF)
438			;
439		break;
440
441	case M4WRTYPE:
442	/*
443	 * dom4wrap - set up for
444	 * wrap-up/wind-down activity
445	 */
446		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
447		break;
448
449	case EXITTYPE:
450	/*
451	 * doexit - immediate exit from m4.
452	 */
453		killdiv();
454		exit((argc > 2) ? atoi(argv[2]) : 0);
455		break;
456
457	case DEFNTYPE:
458		if (argc > 2)
459			for (n = 2; n < argc; n++)
460				dodefn(argv[n]);
461		break;
462
463	case INDIRTYPE:	/* Indirect call */
464		if (argc > 2)
465			doindir(argv, argc);
466		break;
467
468	case BUILTINTYPE: /* Builtins only */
469		if (argc > 2)
470			dobuiltin(argv, argc);
471		break;
472
473	case PATSTYPE:
474		if (argc > 2)
475			dopatsubst(argv, argc);
476		break;
477	case REGEXPTYPE:
478		if (argc > 2)
479			doregexp(argv, argc);
480		break;
481	case LINETYPE:
482		doprintlineno(infile+ilevel);
483		break;
484	case FILENAMETYPE:
485		doprintfilename(infile+ilevel);
486		break;
487	case SELFTYPE:
488		pbstr(rquote);
489		pbstr(argv[1]);
490		pbstr(lquote);
491		break;
492	default:
493		errx(1, "%s at line %lu: eval: major botch.",
494			CURRENT_NAME, CURRENT_LINE);
495		break;
496	}
497}
498
499/*
500 * expand_macro - user-defined macro expansion
501 */
502void
503expand_macro(const char *argv[], int argc)
504{
505	const char *t;
506	const char *p;
507	int n;
508	int argno;
509
510	t = argv[0];		       /* defn string as a whole */
511	p = t;
512	while (*p)
513		p++;
514	p--;			       /* last character of defn */
515	while (p > t) {
516		if (*(p - 1) != ARGFLAG)
517			PUTBACK(*p);
518		else {
519			switch (*p) {
520
521			case '#':
522				pbnum(argc - 2);
523				break;
524			case '0':
525			case '1':
526			case '2':
527			case '3':
528			case '4':
529			case '5':
530			case '6':
531			case '7':
532			case '8':
533			case '9':
534				if ((argno = *p - '0') < argc - 1)
535					pbstr(argv[argno + 1]);
536				break;
537			case '*':
538				if (argc > 2) {
539					for (n = argc - 1; n > 2; n--) {
540						pbstr(argv[n]);
541						putback(COMMA);
542					}
543					pbstr(argv[2]);
544			    	}
545				break;
546                        case '@':
547				if (argc > 2) {
548					for (n = argc - 1; n > 2; n--) {
549						pbstr(rquote);
550						pbstr(argv[n]);
551						pbstr(lquote);
552						putback(COMMA);
553					}
554					pbstr(rquote);
555					pbstr(argv[2]);
556					pbstr(lquote);
557				}
558                                break;
559			default:
560				PUTBACK(*p);
561				PUTBACK('$');
562				break;
563			}
564			p--;
565		}
566		p--;
567	}
568	if (p == t)		       /* do last character */
569		PUTBACK(*p);
570}
571
572/*
573 * dodefine - install definition in the table
574 */
575void
576dodefine(const char *name, const char *defn)
577{
578	ndptr p;
579	int n;
580
581	if (!*name)
582		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
583		    CURRENT_LINE);
584	if ((p = lookup(name)) == nil)
585		p = addent(name);
586	else if (p->defn != null)
587		free((char *) p->defn);
588	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
589		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
590		if (n != -1) {
591			p->type = n & TYPEMASK;
592			if ((n & NOARGS) == 0)
593				p->type |= NEEDARGS;
594			p->defn = xstrdup(null);
595			return;
596		}
597	}
598	if (!*defn)
599		p->defn = xstrdup(null);
600	else
601		p->defn = xstrdup(defn);
602	p->type = MACRTYPE;
603	if (STREQ(name, defn))
604		p->type |= RECDEF;
605}
606
607/*
608 * dodefn - push back a quoted definition of
609 *      the given name.
610 */
611static void
612dodefn(const char *name)
613{
614	ndptr p;
615	const char *real;
616
617	if ((p = lookup(name)) != nil) {
618		if (p->defn != null) {
619			pbstr(rquote);
620			pbstr(p->defn);
621			pbstr(lquote);
622		} else if ((real = builtin_realname(p->type)) != NULL) {
623			pbstr(real);
624			pbstr(BUILTIN_MARKER);
625		}
626	}
627}
628
629/*
630 * dopushdef - install a definition in the hash table
631 *      without removing a previous definition. Since
632 *      each new entry is entered in *front* of the
633 *      hash bucket, it hides a previous definition from
634 *      lookup.
635 */
636static void
637dopushdef(const char *name, const char *defn)
638{
639	ndptr p;
640
641	if (!*name)
642		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
643		    CURRENT_LINE);
644	p = addent(name);
645	if (!*defn)
646		p->defn = xstrdup(null);
647	else
648		p->defn = xstrdup(defn);
649	p->type = MACRTYPE;
650	if (STREQ(name, defn))
651		p->type |= RECDEF;
652}
653
654/*
655 * dump_one_def - dump the specified definition.
656 */
657static void
658dump_one_def(ndptr p)
659{
660	const char *real;
661
662	if (mimic_gnu) {
663		if ((p->type & TYPEMASK) == MACRTYPE)
664			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
665		else {
666			real = builtin_realname(p->type);
667			if (real == NULL)
668				real = null;
669			fprintf(traceout, "%s:\t<%s>\n", p->name, real);
670	    	}
671	} else
672		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
673}
674
675/*
676 * dodumpdef - dump the specified definitions in the hash
677 *      table to stderr. If nothing is specified, the entire
678 *      hash table is dumped.
679 */
680static void
681dodump(const char *argv[], int argc)
682{
683	int n;
684	ndptr p;
685
686	if (argc > 2) {
687		for (n = 2; n < argc; n++)
688			if ((p = lookup(argv[n])) != nil)
689				dump_one_def(p);
690	} else {
691		for (n = 0; n < HASHSIZE; n++)
692			for (p = hashtab[n]; p != nil; p = p->nxtptr)
693				dump_one_def(p);
694	}
695}
696
697/*
698 * dotrace - mark some macros as traced/untraced depending upon on.
699 */
700static void
701dotrace(const char *argv[], int argc, int on)
702{
703	int n;
704
705	if (argc > 2) {
706		for (n = 2; n < argc; n++)
707			mark_traced(argv[n], on);
708	} else
709		mark_traced(NULL, on);
710}
711
712/*
713 * doifelse - select one of two alternatives - loop.
714 */
715static void
716doifelse(const char *argv[], int argc)
717{
718	cycle {
719		if (STREQ(argv[2], argv[3]))
720			pbstr(argv[4]);
721		else if (argc == 6)
722			pbstr(argv[5]);
723		else if (argc > 6) {
724			argv += 3;
725			argc -= 3;
726			continue;
727		}
728		break;
729	}
730}
731
732/*
733 * doinclude - include a given file.
734 */
735static int
736doincl(const char *ifile)
737{
738	if (ilevel + 1 == MAXINP)
739		errx(1, "%s at line %lu: too many include files.",
740		    CURRENT_NAME, CURRENT_LINE);
741	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
742		ilevel++;
743		if ((inname[ilevel] = strdup(ifile)) == NULL)
744			err(1, NULL);
745		inlineno[ilevel] = 1;
746		bbase[ilevel] = bufbase = bp;
747		emitline();
748		return (1);
749	} else
750		return (0);
751}
752
753#ifdef EXTENDED
754/*
755 * dopaste - include a given file without any
756 *           macro processing.
757 */
758static int
759dopaste(const char *pfile)
760{
761	FILE *pf;
762	int c;
763
764	if ((pf = fopen(pfile, "r")) != NULL) {
765		fprintf(active, "#line 1 \"%s\"\n", pfile);
766		while ((c = getc(pf)) != EOF)
767			putc(c, active);
768		(void) fclose(pf);
769		emitline();
770		return (1);
771	} else
772		return (0);
773}
774#endif
775
776static void
777gnu_dochq(const char *argv[], int ac)
778{
779	/* In gnu-m4 mode, the only way to restore quotes is to have no
780	 * arguments at all. */
781	if (ac == 2) {
782		lquote[0] = LQUOTE, lquote[1] = EOS;
783		rquote[0] = RQUOTE, rquote[1] = EOS;
784	} else {
785		strlcpy(lquote, argv[2], sizeof(lquote));
786		if(ac > 3)
787			strlcpy(rquote, argv[3], sizeof(rquote));
788		else
789			rquote[0] = EOS;
790	}
791}
792
793/*
794 * dochq - change quote characters
795 */
796static void
797dochq(const char *argv[], int argc)
798{
799	if (argc > 2) {
800		if (*argv[2])
801			strlcpy(lquote, argv[2], sizeof(lquote));
802		else {
803			lquote[0] = LQUOTE;
804			lquote[1] = EOS;
805		}
806		if (argc > 3) {
807			if (*argv[3])
808				strlcpy(rquote, argv[3], sizeof(rquote));
809		} else
810			strcpy(rquote, lquote);
811	} else {
812		lquote[0] = LQUOTE, lquote[1] = EOS;
813		rquote[0] = RQUOTE, rquote[1] = EOS;
814	}
815}
816
817static void
818gnu_dochc(const char *argv[], int ac)
819{
820	/* In gnu-m4 mode, no arguments mean no comment
821	 * arguments at all. */
822	if (ac == 2) {
823		scommt[0] = EOS;
824		ecommt[0] = EOS;
825	} else {
826		if (*argv[2])
827			strlcpy(scommt, argv[2], sizeof(scommt));
828		else
829			scommt[0] = SCOMMT, scommt[1] = EOS;
830		if(ac > 3 && *argv[3])
831			strlcpy(ecommt, argv[3], sizeof(ecommt));
832		else
833			ecommt[0] = ECOMMT, ecommt[1] = EOS;
834	}
835}
836/*
837 * dochc - change comment characters
838 */
839static void
840dochc(const char *argv[], int argc)
841{
842	if (argc > 2) {
843		if (*argv[2])
844			strlcpy(scommt, argv[2], sizeof(scommt));
845		if (argc > 3) {
846			if (*argv[3])
847				strlcpy(ecommt, argv[3], sizeof(ecommt));
848		}
849		else
850			ecommt[0] = ECOMMT, ecommt[1] = EOS;
851	}
852	else {
853		scommt[0] = SCOMMT, scommt[1] = EOS;
854		ecommt[0] = ECOMMT, ecommt[1] = EOS;
855	}
856}
857
858/*
859 * dodivert - divert the output to a temporary file
860 */
861static void
862dodiv(int n)
863{
864	int fd;
865
866	oindex = n;
867	if (n >= maxout) {
868		if (mimic_gnu)
869			resizedivs(n + 10);
870		else
871			n = 0;		/* bitbucket */
872    	}
873
874	if (n < 0)
875		n = 0;		       /* bitbucket */
876	if (outfile[n] == NULL) {
877		char fname[] = _PATH_DIVNAME;
878
879		if ((fd = mkstemp(fname)) < 0 ||
880			(outfile[n] = fdopen(fd, "w+")) == NULL)
881				err(1, "%s: cannot divert", fname);
882		if (unlink(fname) == -1)
883			err(1, "%s: cannot unlink", fname);
884	}
885	active = outfile[n];
886}
887
888/*
889 * doundivert - undivert a specified output, or all
890 *              other outputs, in numerical order.
891 */
892static void
893doundiv(const char *argv[], int argc)
894{
895	int ind;
896	int n;
897
898	if (argc > 2) {
899		for (ind = 2; ind < argc; ind++) {
900			n = atoi(argv[ind]);
901			if (n > 0 && n < maxout && outfile[n] != NULL)
902				getdiv(n);
903
904		}
905	}
906	else
907		for (n = 1; n < maxout; n++)
908			if (outfile[n] != NULL)
909				getdiv(n);
910}
911
912/*
913 * dosub - select substring
914 */
915static void
916dosub(const char *argv[], int argc)
917{
918	const char *ap, *fc, *k;
919	int nc;
920
921	ap = argv[2];		       /* target string */
922#ifdef EXPR
923	fc = ap + expr(argv[3]);       /* first char */
924#else
925	fc = ap + atoi(argv[3]);       /* first char */
926#endif
927	nc = strlen(fc);
928	if (argc >= 5)
929#ifdef EXPR
930		nc = min(nc, expr(argv[4]));
931#else
932		nc = min(nc, atoi(argv[4]));
933#endif
934	if (fc >= ap && fc < ap + strlen(ap))
935		for (k = fc + nc - 1; k >= fc; k--)
936			putback(*k);
937}
938
939/*
940 * map:
941 * map every character of s1 that is specified in from
942 * into s3 and replace in s. (source s1 remains untouched)
943 *
944 * This is a standard implementation of map(s,from,to) function of ICON
945 * language. Within mapvec, we replace every character of "from" with
946 * the corresponding character in "to". If "to" is shorter than "from",
947 * than the corresponding entries are null, which means that those
948 * characters dissapear altogether. Furthermore, imagine
949 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
950 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
951 * ultimately maps to `*'. In order to achieve this effect in an efficient
952 * manner (i.e. without multiple passes over the destination string), we
953 * loop over mapvec, starting with the initial source character. if the
954 * character value (dch) in this location is different than the source
955 * character (sch), sch becomes dch, once again to index into mapvec, until
956 * the character value stabilizes (i.e. sch = dch, in other words
957 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
958 * character, it will stabilize, since mapvec[0] == 0 at all times. At the
959 * end, we restore mapvec* back to normal where mapvec[n] == n for
960 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
961 * about 5 times faster than any algorithm that makes multiple passes over
962 * destination string.
963 */
964static void
965map(char *dest, const char *src, const char *from, const char *to)
966{
967	const char *tmp;
968	unsigned char sch, dch;
969	static char frombis[257];
970	static char tobis[257];
971	static unsigned char mapvec[256] = {
972	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
973	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
974	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
975	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
976	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
977	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
978	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
979	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
980	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
981	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
982	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
983	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
984	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
985	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
986	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
987	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
988	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
989	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
990	};
991
992	if (*src) {
993		if (mimic_gnu) {
994			/*
995			 * expand character ranges on the fly
996			 */
997			from = handledash(frombis, frombis + 256, from);
998			to = handledash(tobis, tobis + 256, to);
999		}
1000		tmp = from;
1001	/*
1002	 * create a mapping between "from" and
1003	 * "to"
1004	 */
1005		while (*from)
1006			mapvec[(unsigned char)(*from++)] = (*to) ?
1007				(unsigned char)(*to++) : 0;
1008
1009		while (*src) {
1010			sch = (unsigned char)(*src++);
1011			dch = mapvec[sch];
1012			while (dch != sch) {
1013				sch = dch;
1014				dch = mapvec[sch];
1015			}
1016			if ((*dest = (char)dch))
1017				dest++;
1018		}
1019	/*
1020	 * restore all the changed characters
1021	 */
1022		while (*tmp) {
1023			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1024			tmp++;
1025		}
1026	}
1027	*dest = '\0';
1028}
1029
1030
1031/*
1032 * handledash:
1033 *  use buffer to copy the src string, expanding character ranges
1034 * on the way.
1035 */
1036static const char *
1037handledash(char *buffer, char *end, const char *src)
1038{
1039	char *p;
1040
1041	p = buffer;
1042	while(*src) {
1043		if (src[1] == '-' && src[2]) {
1044			unsigned char i;
1045			for (i = (unsigned char)src[0];
1046			    i <= (unsigned char)src[2]; i++) {
1047				*p++ = i;
1048				if (p == end) {
1049					*p = '\0';
1050					return buffer;
1051				}
1052			}
1053			src += 3;
1054		} else
1055			*p++ = *src++;
1056		if (p == end)
1057			break;
1058	}
1059	*p = '\0';
1060	return buffer;
1061}
1062