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