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