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