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