xref: /freebsd/usr.bin/m4/eval.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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 			sysval = system(argv[2]);
246 		break;
247 
248 	case SYSVTYPE:
249 	/*
250 	 * dosysval - return value of the last
251 	 * system call.
252 	 *
253 	 */
254 		pbnum(sysval);
255 		break;
256 
257 	case ESYSCMDTYPE:
258 		if (argc > 2)
259 			doesyscmd(argv[2]);
260 	    	break;
261 	case INCLTYPE:
262 		if (argc > 2)
263 			if (!doincl(argv[2]))
264 				err(1, "%s at line %lu: include(%s)",
265 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
266 		break;
267 
268 	case SINCTYPE:
269 		if (argc > 2)
270 			(void) doincl(argv[2]);
271 		break;
272 #ifdef EXTENDED
273 	case PASTTYPE:
274 		if (argc > 2)
275 			if (!dopaste(argv[2]))
276 				err(1, "%s at line %lu: paste(%s)",
277 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
278 		break;
279 
280 	case SPASTYPE:
281 		if (argc > 2)
282 			(void) dopaste(argv[2]);
283 		break;
284 #endif
285 	case CHNQTYPE:
286 		if (mimic_gnu)
287 			gnu_dochq(argv, ac);
288 		else
289 			dochq(argv, argc);
290 		break;
291 
292 	case CHNCTYPE:
293 		if (mimic_gnu)
294 			gnu_dochc(argv, ac);
295 		else
296 			dochc(argv, argc);
297 		break;
298 
299 	case SUBSTYPE:
300 	/*
301 	 * dosub - select substring
302 	 *
303 	 */
304 		if (argc > 3)
305 			dosub(argv, argc);
306 		break;
307 
308 	case SHIFTYPE:
309 	/*
310 	 * doshift - push back all arguments
311 	 * except the first one (i.e. skip
312 	 * argv[2])
313 	 */
314 		if (argc > 3) {
315 			for (n = argc - 1; n > 3; n--) {
316 				pbstr(rquote);
317 				pbstr(argv[n]);
318 				pbstr(lquote);
319 				putback(COMMA);
320 			}
321 			pbstr(rquote);
322 			pbstr(argv[3]);
323 			pbstr(lquote);
324 		}
325 		break;
326 
327 	case DIVRTYPE:
328 		if (argc > 2 && (n = atoi(argv[2])) != 0)
329 			dodiv(n);
330 		else {
331 			active = stdout;
332 			oindex = 0;
333 		}
334 		break;
335 
336 	case UNDVTYPE:
337 		doundiv(argv, argc);
338 		break;
339 
340 	case DIVNTYPE:
341 	/*
342 	 * dodivnum - return the number of
343 	 * current output diversion
344 	 */
345 		pbnum(oindex);
346 		break;
347 
348 	case UNDFTYPE:
349 	/*
350 	 * doundefine - undefine a previously
351 	 * defined macro(s) or m4 keyword(s).
352 	 */
353 		if (argc > 2)
354 			for (n = 2; n < argc; n++)
355 				remhash(argv[n], ALL);
356 		break;
357 
358 	case POPDTYPE:
359 	/*
360 	 * dopopdef - remove the topmost
361 	 * definitions of macro(s) or m4
362 	 * keyword(s).
363 	 */
364 		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  */
502 void
503 expand_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  */
575 void
576 dodefine(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  */
611 static void
612 dodefn(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  */
636 static void
637 dopushdef(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  */
657 static void
658 dump_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  */
680 static void
681 dodump(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  */
700 static void
701 dotrace(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  */
715 static void
716 doifelse(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  */
735 static int
736 doincl(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  */
758 static int
759 dopaste(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 
776 static void
777 gnu_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  */
796 static void
797 dochq(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 
817 static void
818 gnu_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  */
839 static void
840 dochc(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  */
861 static void
862 dodiv(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  */
892 static void
893 doundiv(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  */
915 static void
916 dosub(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  */
964 static void
965 map(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  */
1036 static const char *
1037 handledash(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