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