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