xref: /freebsd/usr.bin/m4/eval.c (revision 7bd6fde3)
1 /*	$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $	*/
2 /*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Ozan Yigit at York University.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
43 #else
44 #if 0
45 static char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $";
46 #endif
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/cdefs.h>
51 __FBSDID("$FreeBSD$");
52 
53 /*
54  * eval.c
55  * Facility: m4 macro processor
56  * by: oz
57  */
58 
59 #include <sys/types.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <stddef.h>
65 #include <string.h>
66 #include <fcntl.h>
67 #include <err.h>
68 #include "mdef.h"
69 #include "stdd.h"
70 #include "extern.h"
71 #include "pathnames.h"
72 
73 #define BUILTIN_MARKER	"__builtin_"
74 
75 static void	dodefn(const char *);
76 static void	dopushdef(const char *, const char *);
77 static void	dodump(const char *[], int);
78 static void	dotrace(const char *[], int, int);
79 static void	doifelse(const char *[], int);
80 static int	doincl(const char *);
81 static int	dopaste(const char *);
82 static void	gnu_dochq(const char *[], int);
83 static void	dochq(const char *[], int);
84 static void	gnu_dochc(const char *[], int);
85 static void	dochc(const char *[], int);
86 static void	dodiv(int);
87 static void	doundiv(const char *[], int);
88 static void	dosub(const char *[], int);
89 static void	map(char *, const char *, const char *, const char *);
90 static const char *handledash(char *, char *, const char *);
91 static void	expand_builtin(const char *[], int, int);
92 static void	expand_macro(const char *[], int);
93 static void	dump_one_def(ndptr);
94 
95 unsigned long	expansion_id;
96 
97 /*
98  * eval - eval all macros and builtins calls
99  *	  argc - number of elements in argv.
100  *	  argv - element vector :
101  *			argv[0] = definition of a user
102  *				  macro or nil if built-in.
103  *			argv[1] = name of the macro or
104  *				  built-in.
105  *			argv[2] = parameters to user-defined
106  *			   .	  macro or built-in.
107  *			   .
108  *
109  * A call in the form of macro-or-builtin() will result in:
110  *			argv[0] = nullstr
111  *			argv[1] = macro-or-builtin
112  *			argv[2] = nullstr
113  *
114  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
115  */
116 void
117 eval(const char *argv[], int argc, int td)
118 {
119 	ssize_t mark = -1;
120 
121 	expansion_id++;
122 	if (td & RECDEF)
123 		errx(1, "%s at line %lu: expanding recursive definition for %s",
124 			CURRENT_NAME, CURRENT_LINE, argv[1]);
125 	if (traced_macros && is_traced(argv[1]))
126 		mark = trace(argv, argc, infile+ilevel);
127 	if (td == MACRTYPE)
128 		expand_macro(argv, argc);
129 	else
130 		expand_builtin(argv, argc, td);
131     	if (mark != -1)
132 		finish_trace(mark);
133 }
134 
135 /*
136  * expand_builtin - evaluate built-in macros.
137  */
138 void
139 expand_builtin(const char *argv[], int argc, int td)
140 {
141 	int c, n;
142 	int ac;
143 	static int sysval = 0;
144 
145 #ifdef DEBUG
146 	printf("argc = %d\n", argc);
147 	for (n = 0; n < argc; n++)
148 		printf("argv[%d] = %s\n", n, argv[n]);
149 	fflush(stdout);
150 #endif
151 
152  /*
153   * if argc == 3 and argv[2] is null, then we
154   * have macro-or-builtin() type call. We adjust
155   * argc to avoid further checking..
156   */
157   	ac = argc;
158 
159 	if (argc == 3 && !*(argv[2]))
160 		argc--;
161 
162 	switch (td & TYPEMASK) {
163 
164 	case DEFITYPE:
165 		if (argc > 2)
166 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
167 		break;
168 
169 	case PUSDTYPE:
170 		if (argc > 2)
171 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
172 		break;
173 
174 	case DUMPTYPE:
175 		dodump(argv, argc);
176 		break;
177 
178 	case TRACEONTYPE:
179 		dotrace(argv, argc, 1);
180 		break;
181 
182 	case TRACEOFFTYPE:
183 		dotrace(argv, argc, 0);
184 		break;
185 
186 	case EXPRTYPE:
187 	/*
188 	 * doexpr - evaluate arithmetic
189 	 * expression
190 	 */
191 		if (argc > 2)
192 			pbnum(expr(argv[2]));
193 		break;
194 
195 	case IFELTYPE:
196 		if (argc > 4)
197 			doifelse(argv, argc);
198 		break;
199 
200 	case IFDFTYPE:
201 	/*
202 	 * doifdef - select one of two
203 	 * alternatives based on the existence of
204 	 * another definition
205 	 */
206 		if (argc > 3) {
207 			if (lookup(argv[2]) != nil)
208 				pbstr(argv[3]);
209 			else if (argc > 4)
210 				pbstr(argv[4]);
211 		}
212 		break;
213 
214 	case LENGTYPE:
215 	/*
216 	 * dolen - find the length of the
217 	 * argument
218 	 */
219 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
220 		break;
221 
222 	case INCRTYPE:
223 	/*
224 	 * doincr - increment the value of the
225 	 * argument
226 	 */
227 		if (argc > 2)
228 			pbnum(atoi(argv[2]) + 1);
229 		break;
230 
231 	case DECRTYPE:
232 	/*
233 	 * dodecr - decrement the value of the
234 	 * argument
235 	 */
236 		if (argc > 2)
237 			pbnum(atoi(argv[2]) - 1);
238 		break;
239 
240 	case SYSCTYPE:
241 	/*
242 	 * dosys - execute system command
243 	 */
244 		if (argc > 2) {
245 			fflush(NULL);
246 			sysval = system(argv[2]);
247 		}
248 		break;
249 
250 	case SYSVTYPE:
251 	/*
252 	 * dosysval - return value of the last
253 	 * system call.
254 	 *
255 	 */
256 		pbnum(sysval);
257 		break;
258 
259 	case ESYSCMDTYPE:
260 		if (argc > 2)
261 			doesyscmd(argv[2]);
262 	    	break;
263 	case INCLTYPE:
264 		if (argc > 2)
265 			if (!doincl(argv[2]))
266 				err(1, "%s at line %lu: include(%s)",
267 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
268 		break;
269 
270 	case SINCTYPE:
271 		if (argc > 2)
272 			(void) doincl(argv[2]);
273 		break;
274 #ifdef EXTENDED
275 	case PASTTYPE:
276 		if (argc > 2)
277 			if (!dopaste(argv[2]))
278 				err(1, "%s at line %lu: paste(%s)",
279 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
280 		break;
281 
282 	case SPASTYPE:
283 		if (argc > 2)
284 			(void) dopaste(argv[2]);
285 		break;
286 #endif
287 	case CHNQTYPE:
288 		if (mimic_gnu)
289 			gnu_dochq(argv, ac);
290 		else
291 			dochq(argv, argc);
292 		break;
293 
294 	case CHNCTYPE:
295 		if (mimic_gnu)
296 			gnu_dochc(argv, ac);
297 		else
298 			dochc(argv, argc);
299 		break;
300 
301 	case SUBSTYPE:
302 	/*
303 	 * dosub - select substring
304 	 *
305 	 */
306 		if (argc > 3)
307 			dosub(argv, argc);
308 		break;
309 
310 	case SHIFTYPE:
311 	/*
312 	 * doshift - push back all arguments
313 	 * except the first one (i.e. skip
314 	 * argv[2])
315 	 */
316 		if (argc > 3) {
317 			for (n = argc - 1; n > 3; n--) {
318 				pbstr(rquote);
319 				pbstr(argv[n]);
320 				pbstr(lquote);
321 				putback(COMMA);
322 			}
323 			pbstr(rquote);
324 			pbstr(argv[3]);
325 			pbstr(lquote);
326 		}
327 		break;
328 
329 	case DIVRTYPE:
330 		if (argc > 2 && (n = atoi(argv[2])) != 0)
331 			dodiv(n);
332 		else {
333 			active = stdout;
334 			oindex = 0;
335 		}
336 		break;
337 
338 	case UNDVTYPE:
339 		doundiv(argv, argc);
340 		break;
341 
342 	case DIVNTYPE:
343 	/*
344 	 * dodivnum - return the number of
345 	 * current output diversion
346 	 */
347 		pbnum(oindex);
348 		break;
349 
350 	case UNDFTYPE:
351 	/*
352 	 * doundefine - undefine a previously
353 	 * defined macro(s) or m4 keyword(s).
354 	 */
355 		if (argc > 2)
356 			for (n = 2; n < argc; n++)
357 				remhash(argv[n], ALL);
358 		break;
359 
360 	case POPDTYPE:
361 	/*
362 	 * dopopdef - remove the topmost
363 	 * definitions of macro(s) or m4
364 	 * keyword(s).
365 	 */
366 		if (argc > 2)
367 			for (n = 2; n < argc; n++)
368 				remhash(argv[n], TOP);
369 		break;
370 
371 	case MKTMTYPE:
372 	/*
373 	 * dotemp - create a temporary file
374 	 */
375 		if (argc > 2) {
376 			int fd;
377 			char *temp;
378 
379 			temp = xstrdup(argv[2]);
380 
381 			fd = mkstemp(temp);
382 			if (fd == -1)
383 				err(1,
384 	    "%s at line %lu: couldn't make temp file %s",
385 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
386 			close(fd);
387 			pbstr(temp);
388 			free(temp);
389 		}
390 		break;
391 
392 	case TRNLTYPE:
393 	/*
394 	 * dotranslit - replace all characters in
395 	 * the source string that appears in the
396 	 * "from" string with the corresponding
397 	 * characters in the "to" string.
398 	 */
399 		if (argc > 3) {
400 			char *temp;
401 
402 			temp = xalloc(strlen(argv[2])+1);
403 			if (argc > 4)
404 				map(temp, argv[2], argv[3], argv[4]);
405 			else
406 				map(temp, argv[2], argv[3], null);
407 			pbstr(temp);
408 			free(temp);
409 		} else if (argc > 2)
410 			pbstr(argv[2]);
411 		break;
412 
413 	case INDXTYPE:
414 	/*
415 	 * doindex - find the index of the second
416 	 * argument string in the first argument
417 	 * string. -1 if not present.
418 	 */
419 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
420 		break;
421 
422 	case ERRPTYPE:
423 	/*
424 	 * doerrp - print the arguments to stderr
425 	 * file
426 	 */
427 		if (argc > 2) {
428 			for (n = 2; n < argc; n++)
429 				fprintf(stderr, "%s ", argv[n]);
430 			fprintf(stderr, "\n");
431 		}
432 		break;
433 
434 	case DNLNTYPE:
435 	/*
436 	 * dodnl - eat-up-to and including
437 	 * newline
438 	 */
439 		while ((c = gpbc()) != '\n' && c != EOF)
440 			;
441 		break;
442 
443 	case M4WRTYPE:
444 	/*
445 	 * dom4wrap - set up for
446 	 * wrap-up/wind-down activity
447 	 */
448 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
449 		break;
450 
451 	case EXITTYPE:
452 	/*
453 	 * doexit - immediate exit from m4.
454 	 */
455 		killdiv();
456 		exit((argc > 2) ? atoi(argv[2]) : 0);
457 		break;
458 
459 	case DEFNTYPE:
460 		if (argc > 2)
461 			for (n = 2; n < argc; n++)
462 				dodefn(argv[n]);
463 		break;
464 
465 	case INDIRTYPE:	/* Indirect call */
466 		if (argc > 2)
467 			doindir(argv, argc);
468 		break;
469 
470 	case BUILTINTYPE: /* Builtins only */
471 		if (argc > 2)
472 			dobuiltin(argv, argc);
473 		break;
474 
475 	case PATSTYPE:
476 		if (argc > 2)
477 			dopatsubst(argv, argc);
478 		break;
479 	case REGEXPTYPE:
480 		if (argc > 2)
481 			doregexp(argv, argc);
482 		break;
483 	case LINETYPE:
484 		doprintlineno(infile+ilevel);
485 		break;
486 	case FILENAMETYPE:
487 		doprintfilename(infile+ilevel);
488 		break;
489 	case SELFTYPE:
490 		pbstr(rquote);
491 		pbstr(argv[1]);
492 		pbstr(lquote);
493 		break;
494 	default:
495 		errx(1, "%s at line %lu: eval: major botch.",
496 			CURRENT_NAME, CURRENT_LINE);
497 		break;
498 	}
499 }
500 
501 /*
502  * expand_macro - user-defined macro expansion
503  */
504 void
505 expand_macro(const char *argv[], int argc)
506 {
507 	const char *t;
508 	const char *p;
509 	int n;
510 	int argno;
511 
512 	t = argv[0];		       /* defn string as a whole */
513 	p = t;
514 	while (*p)
515 		p++;
516 	p--;			       /* last character of defn */
517 	while (p > t) {
518 		if (*(p - 1) != ARGFLAG)
519 			PUTBACK(*p);
520 		else {
521 			switch (*p) {
522 
523 			case '#':
524 				pbnum(argc - 2);
525 				break;
526 			case '0':
527 			case '1':
528 			case '2':
529 			case '3':
530 			case '4':
531 			case '5':
532 			case '6':
533 			case '7':
534 			case '8':
535 			case '9':
536 				if ((argno = *p - '0') < argc - 1)
537 					pbstr(argv[argno + 1]);
538 				break;
539 			case '*':
540 				if (argc > 2) {
541 					for (n = argc - 1; n > 2; n--) {
542 						pbstr(argv[n]);
543 						putback(COMMA);
544 					}
545 					pbstr(argv[2]);
546 			    	}
547 				break;
548                         case '@':
549 				if (argc > 2) {
550 					for (n = argc - 1; n > 2; n--) {
551 						pbstr(rquote);
552 						pbstr(argv[n]);
553 						pbstr(lquote);
554 						putback(COMMA);
555 					}
556 					pbstr(rquote);
557 					pbstr(argv[2]);
558 					pbstr(lquote);
559 				}
560                                 break;
561 			default:
562 				PUTBACK(*p);
563 				PUTBACK('$');
564 				break;
565 			}
566 			p--;
567 		}
568 		p--;
569 	}
570 	if (p == t)		       /* do last character */
571 		PUTBACK(*p);
572 }
573 
574 /*
575  * dodefine - install definition in the table
576  */
577 void
578 dodefine(const char *name, const char *defn)
579 {
580 	ndptr p;
581 	int n;
582 
583 	if (!*name)
584 		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
585 		    CURRENT_LINE);
586 	if ((p = lookup(name)) == nil)
587 		p = addent(name);
588 	else if (p->defn != null)
589 		free((char *) p->defn);
590 	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
591 		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
592 		if (n != -1) {
593 			p->type = n & TYPEMASK;
594 			if ((n & NOARGS) == 0)
595 				p->type |= NEEDARGS;
596 			p->defn = null;
597 			return;
598 		}
599 	}
600 	if (!*defn)
601 		p->defn = null;
602 	else
603 		p->defn = xstrdup(defn);
604 	p->type = MACRTYPE;
605 	if (STREQ(name, defn))
606 		p->type |= RECDEF;
607 }
608 
609 /*
610  * dodefn - push back a quoted definition of
611  *      the given name.
612  */
613 static void
614 dodefn(const char *name)
615 {
616 	ndptr p;
617 	const char *real;
618 
619 	if ((p = lookup(name)) != nil) {
620 		if (p->defn != null) {
621 			pbstr(rquote);
622 			pbstr(p->defn);
623 			pbstr(lquote);
624 		} else if ((real = builtin_realname(p->type)) != NULL) {
625 			pbstr(real);
626 			pbstr(BUILTIN_MARKER);
627 		}
628 	}
629 }
630 
631 /*
632  * dopushdef - install a definition in the hash table
633  *      without removing a previous definition. Since
634  *      each new entry is entered in *front* of the
635  *      hash bucket, it hides a previous definition from
636  *      lookup.
637  */
638 static void
639 dopushdef(const char *name, const char *defn)
640 {
641 	ndptr p;
642 
643 	if (!*name)
644 		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
645 		    CURRENT_LINE);
646 	p = addent(name);
647 	if (!*defn)
648 		p->defn = null;
649 	else
650 		p->defn = xstrdup(defn);
651 	p->type = MACRTYPE;
652 	if (STREQ(name, defn))
653 		p->type |= RECDEF;
654 }
655 
656 /*
657  * dump_one_def - dump the specified definition.
658  */
659 static void
660 dump_one_def(ndptr p)
661 {
662 	const char *real;
663 
664 	if (mimic_gnu) {
665 		if ((p->type & TYPEMASK) == MACRTYPE)
666 			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
667 		else {
668 			real = builtin_realname(p->type);
669 			if (real == NULL)
670 				real = null;
671 			fprintf(traceout, "%s:\t<%s>\n", p->name, real);
672 	    	}
673 	} else
674 		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
675 }
676 
677 /*
678  * dodumpdef - dump the specified definitions in the hash
679  *      table to stderr. If nothing is specified, the entire
680  *      hash table is dumped.
681  */
682 static void
683 dodump(const char *argv[], int argc)
684 {
685 	int n;
686 	ndptr p;
687 
688 	if (argc > 2) {
689 		for (n = 2; n < argc; n++)
690 			if ((p = lookup(argv[n])) != nil)
691 				dump_one_def(p);
692 	} else {
693 		for (n = 0; n < HASHSIZE; n++)
694 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
695 				dump_one_def(p);
696 	}
697 }
698 
699 /*
700  * dotrace - mark some macros as traced/untraced depending upon on.
701  */
702 static void
703 dotrace(const char *argv[], int argc, int on)
704 {
705 	int n;
706 
707 	if (argc > 2) {
708 		for (n = 2; n < argc; n++)
709 			mark_traced(argv[n], on);
710 	} else
711 		mark_traced(NULL, on);
712 }
713 
714 /*
715  * doifelse - select one of two alternatives - loop.
716  */
717 static void
718 doifelse(const char *argv[], int argc)
719 {
720 	cycle {
721 		if (STREQ(argv[2], argv[3]))
722 			pbstr(argv[4]);
723 		else if (argc == 6)
724 			pbstr(argv[5]);
725 		else if (argc > 6) {
726 			argv += 3;
727 			argc -= 3;
728 			continue;
729 		}
730 		break;
731 	}
732 }
733 
734 /*
735  * doinclude - include a given file.
736  */
737 static int
738 doincl(const char *ifile)
739 {
740 	if (ilevel + 1 == MAXINP)
741 		errx(1, "%s at line %lu: too many include files.",
742 		    CURRENT_NAME, CURRENT_LINE);
743 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
744 		ilevel++;
745 		if ((inname[ilevel] = strdup(ifile)) == NULL)
746 			err(1, NULL);
747 		inlineno[ilevel] = 1;
748 		bbase[ilevel] = bufbase = bp;
749 		emitline();
750 		return (1);
751 	} else
752 		return (0);
753 }
754 
755 #ifdef EXTENDED
756 /*
757  * dopaste - include a given file without any
758  *           macro processing.
759  */
760 static int
761 dopaste(const char *pfile)
762 {
763 	FILE *pf;
764 	int c;
765 
766 	if ((pf = fopen(pfile, "r")) != NULL) {
767 		fprintf(active, "#line 1 \"%s\"\n", pfile);
768 		while ((c = getc(pf)) != EOF)
769 			putc(c, active);
770 		(void) fclose(pf);
771 		emitline();
772 		return (1);
773 	} else
774 		return (0);
775 }
776 #endif
777 
778 static void
779 gnu_dochq(const char *argv[], int ac)
780 {
781 	/* In gnu-m4 mode, the only way to restore quotes is to have no
782 	 * arguments at all. */
783 	if (ac == 2) {
784 		lquote[0] = LQUOTE, lquote[1] = EOS;
785 		rquote[0] = RQUOTE, rquote[1] = EOS;
786 	} else {
787 		strlcpy(lquote, argv[2], sizeof(lquote));
788 		if(ac > 3)
789 			strlcpy(rquote, argv[3], sizeof(rquote));
790 		else
791 			rquote[0] = EOS;
792 	}
793 }
794 
795 /*
796  * dochq - change quote characters
797  */
798 static void
799 dochq(const char *argv[], int argc)
800 {
801 	if (argc > 2) {
802 		if (*argv[2])
803 			strlcpy(lquote, argv[2], sizeof(lquote));
804 		else {
805 			lquote[0] = LQUOTE;
806 			lquote[1] = EOS;
807 		}
808 		if (argc > 3) {
809 			if (*argv[3])
810 				strlcpy(rquote, argv[3], sizeof(rquote));
811 		} else
812 			strcpy(rquote, lquote);
813 	} else {
814 		lquote[0] = LQUOTE, lquote[1] = EOS;
815 		rquote[0] = RQUOTE, rquote[1] = EOS;
816 	}
817 }
818 
819 static void
820 gnu_dochc(const char *argv[], int ac)
821 {
822 	/* In gnu-m4 mode, no arguments mean no comment
823 	 * arguments at all. */
824 	if (ac == 2) {
825 		scommt[0] = EOS;
826 		ecommt[0] = EOS;
827 	} else {
828 		if (*argv[2])
829 			strlcpy(scommt, argv[2], sizeof(scommt));
830 		else
831 			scommt[0] = SCOMMT, scommt[1] = EOS;
832 		if(ac > 3 && *argv[3])
833 			strlcpy(ecommt, argv[3], sizeof(ecommt));
834 		else
835 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
836 	}
837 }
838 /*
839  * dochc - change comment characters
840  */
841 static void
842 dochc(const char *argv[], int argc)
843 {
844 	if (argc > 2) {
845 		if (*argv[2])
846 			strlcpy(scommt, argv[2], sizeof(scommt));
847 		if (argc > 3) {
848 			if (*argv[3])
849 				strlcpy(ecommt, argv[3], sizeof(ecommt));
850 		}
851 		else
852 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
853 	}
854 	else {
855 		scommt[0] = SCOMMT, scommt[1] = EOS;
856 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
857 	}
858 }
859 
860 /*
861  * dodivert - divert the output to a temporary file
862  */
863 static void
864 dodiv(int n)
865 {
866 	int fd;
867 
868 	oindex = n;
869 	if (n >= maxout) {
870 		if (mimic_gnu)
871 			resizedivs(n + 10);
872 		else
873 			n = 0;		/* bitbucket */
874     	}
875 
876 	if (n < 0)
877 		n = 0;		       /* bitbucket */
878 	if (outfile[n] == NULL) {
879 		char fname[] = _PATH_DIVNAME;
880 
881 		if ((fd = mkstemp(fname)) < 0 ||
882 			(outfile[n] = fdopen(fd, "w+")) == NULL)
883 				err(1, "%s: cannot divert", fname);
884 		if (unlink(fname) == -1)
885 			err(1, "%s: cannot unlink", fname);
886 	}
887 	active = outfile[n];
888 }
889 
890 /*
891  * doundivert - undivert a specified output, or all
892  *              other outputs, in numerical order.
893  */
894 static void
895 doundiv(const char *argv[], int argc)
896 {
897 	int ind;
898 	int n;
899 
900 	if (argc > 2) {
901 		for (ind = 2; ind < argc; ind++) {
902 			n = atoi(argv[ind]);
903 			if (n > 0 && n < maxout && outfile[n] != NULL)
904 				getdiv(n);
905 
906 		}
907 	}
908 	else
909 		for (n = 1; n < maxout; n++)
910 			if (outfile[n] != NULL)
911 				getdiv(n);
912 }
913 
914 /*
915  * dosub - select substring
916  */
917 static void
918 dosub(const char *argv[], int argc)
919 {
920 	const char *ap, *fc, *k;
921 	int nc;
922 
923 	ap = argv[2];		       /* target string */
924 #ifdef EXPR
925 	fc = ap + expr(argv[3]);       /* first char */
926 #else
927 	fc = ap + atoi(argv[3]);       /* first char */
928 #endif
929 	nc = strlen(fc);
930 	if (argc >= 5)
931 #ifdef EXPR
932 		nc = min(nc, expr(argv[4]));
933 #else
934 		nc = min(nc, atoi(argv[4]));
935 #endif
936 	if (fc >= ap && fc < ap + strlen(ap))
937 		for (k = fc + nc - 1; k >= fc; k--)
938 			putback(*k);
939 }
940 
941 /*
942  * map:
943  * map every character of s1 that is specified in from
944  * into s3 and replace in s. (source s1 remains untouched)
945  *
946  * This is a standard implementation of map(s,from,to) function of ICON
947  * language. Within mapvec, we replace every character of "from" with
948  * the corresponding character in "to". If "to" is shorter than "from",
949  * than the corresponding entries are null, which means that those
950  * characters dissapear altogether. Furthermore, imagine
951  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
952  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
953  * ultimately maps to `*'. In order to achieve this effect in an efficient
954  * manner (i.e. without multiple passes over the destination string), we
955  * loop over mapvec, starting with the initial source character. if the
956  * character value (dch) in this location is different than the source
957  * character (sch), sch becomes dch, once again to index into mapvec, until
958  * the character value stabilizes (i.e. sch = dch, in other words
959  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
960  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
961  * end, we restore mapvec* back to normal where mapvec[n] == n for
962  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
963  * about 5 times faster than any algorithm that makes multiple passes over
964  * destination string.
965  */
966 static void
967 map(char *dest, const char *src, const char *from, const char *to)
968 {
969 	const char *tmp;
970 	unsigned char sch, dch;
971 	static char frombis[257];
972 	static char tobis[257];
973 	static unsigned char mapvec[256] = {
974 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
975 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
976 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
977 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
978 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
979 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
980 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
981 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
982 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
983 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
984 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
985 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
986 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
987 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
988 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
989 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
990 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
991 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
992 	};
993 
994 	if (*src) {
995 		if (mimic_gnu) {
996 			/*
997 			 * expand character ranges on the fly
998 			 */
999 			from = handledash(frombis, frombis + 256, from);
1000 			to = handledash(tobis, tobis + 256, to);
1001 		}
1002 		tmp = from;
1003 	/*
1004 	 * create a mapping between "from" and
1005 	 * "to"
1006 	 */
1007 		while (*from)
1008 			mapvec[(unsigned char)(*from++)] = (*to) ?
1009 				(unsigned char)(*to++) : 0;
1010 
1011 		while (*src) {
1012 			sch = (unsigned char)(*src++);
1013 			dch = mapvec[sch];
1014 			while (dch != sch) {
1015 				sch = dch;
1016 				dch = mapvec[sch];
1017 			}
1018 			if ((*dest = (char)dch))
1019 				dest++;
1020 		}
1021 	/*
1022 	 * restore all the changed characters
1023 	 */
1024 		while (*tmp) {
1025 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1026 			tmp++;
1027 		}
1028 	}
1029 	*dest = '\0';
1030 }
1031 
1032 
1033 /*
1034  * handledash:
1035  *  use buffer to copy the src string, expanding character ranges
1036  * on the way.
1037  */
1038 static const char *
1039 handledash(char *buffer, char *end, const char *src)
1040 {
1041 	char *p;
1042 
1043 	p = buffer;
1044 	while(*src) {
1045 		if (src[1] == '-' && src[2]) {
1046 			unsigned char i;
1047 			for (i = (unsigned char)src[0];
1048 			    i <= (unsigned char)src[2]; i++) {
1049 				*p++ = i;
1050 				if (p == end) {
1051 					*p = '\0';
1052 					return buffer;
1053 				}
1054 			}
1055 			src += 3;
1056 		} else
1057 			*p++ = *src++;
1058 		if (p == end)
1059 			break;
1060 	}
1061 	*p = '\0';
1062 	return buffer;
1063 }
1064