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