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