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