xref: /freebsd/usr.bin/m4/eval.c (revision 41059135ce931c0f1014a999ffabc6bc470ce856)
1 /*	$OpenBSD: eval.c,v 1.74 2015/02/05 12:59:57 millert 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 <stdio.h>
52 #include <stdint.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 		}
281 		break;
282 
283 	case SINCTYPE:
284 		if (argc > 2)
285 			(void) doincl(argv[2]);
286 		break;
287 #ifdef EXTENDED
288 	case PASTTYPE:
289 		if (argc > 2)
290 			if (!dopaste(argv[2]))
291 				err(1, "%s at line %lu: paste(%s)",
292 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
293 		break;
294 
295 	case SPASTYPE:
296 		if (argc > 2)
297 			(void) dopaste(argv[2]);
298 		break;
299 	case FORMATTYPE:
300 		doformat(argv, argc);
301 		break;
302 #endif
303 	case CHNQTYPE:
304 		dochq(argv, ac);
305 		break;
306 
307 	case CHNCTYPE:
308 		dochc(argv, argc);
309 		break;
310 
311 	case SUBSTYPE:
312 	/*
313 	 * dosub - select substring
314 	 *
315 	 */
316 		if (argc > 3)
317 			dosub(argv, argc);
318 		break;
319 
320 	case SHIFTYPE:
321 	/*
322 	 * doshift - push back all arguments
323 	 * except the first one (i.e. skip
324 	 * argv[2])
325 	 */
326 		if (argc > 3) {
327 			for (n = argc - 1; n > 3; n--) {
328 				pbstr(rquote);
329 				pbstr(argv[n]);
330 				pbstr(lquote);
331 				pushback(COMMA);
332 			}
333 			pbstr(rquote);
334 			pbstr(argv[3]);
335 			pbstr(lquote);
336 		}
337 		break;
338 
339 	case DIVRTYPE:
340 		if (argc > 2 && (n = atoi(argv[2])) != 0)
341 			dodiv(n);
342 		else {
343 			active = stdout;
344 			oindex = 0;
345 		}
346 		break;
347 
348 	case UNDVTYPE:
349 		doundiv(argv, argc);
350 		break;
351 
352 	case DIVNTYPE:
353 	/*
354 	 * dodivnum - return the number of
355 	 * current output diversion
356 	 */
357 		pbnum(oindex);
358 		break;
359 
360 	case UNDFTYPE:
361 	/*
362 	 * doundefine - undefine a previously
363 	 * defined macro(s) or m4 keyword(s).
364 	 */
365 		if (argc > 2)
366 			for (n = 2; n < argc; n++)
367 				macro_undefine(argv[n]);
368 		break;
369 
370 	case POPDTYPE:
371 	/*
372 	 * dopopdef - remove the topmost
373 	 * definitions of macro(s) or m4
374 	 * keyword(s).
375 	 */
376 		if (argc > 2)
377 			for (n = 2; n < argc; n++)
378 				macro_popdef(argv[n]);
379 		break;
380 
381 	case MKTMTYPE:
382 	/*
383 	 * dotemp - create a temporary file
384 	 */
385 		if (argc > 2) {
386 			int fd;
387 			char *temp;
388 
389 			temp = xstrdup(argv[2]);
390 
391 			fd = mkstemp(temp);
392 			if (fd == -1)
393 				err(1,
394 	    "%s at line %lu: couldn't make temp file %s",
395 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
396 			close(fd);
397 			pbstr(temp);
398 			free(temp);
399 		}
400 		break;
401 
402 	case TRNLTYPE:
403 	/*
404 	 * dotranslit - replace all characters in
405 	 * the source string that appears in the
406 	 * "from" string with the corresponding
407 	 * characters in the "to" string.
408 	 */
409 		if (argc > 3) {
410 			char *temp;
411 
412 			temp = xalloc(strlen(argv[2])+1, NULL);
413 			if (argc > 4)
414 				map(temp, argv[2], argv[3], argv[4]);
415 			else
416 				map(temp, argv[2], argv[3], null);
417 			pbstr(temp);
418 			free(temp);
419 		} else if (argc > 2)
420 			pbstr(argv[2]);
421 		break;
422 
423 	case INDXTYPE:
424 	/*
425 	 * doindex - find the index of the second
426 	 * argument string in the first argument
427 	 * string. -1 if not present.
428 	 */
429 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
430 		break;
431 
432 	case ERRPTYPE:
433 	/*
434 	 * doerrp - print the arguments to stderr
435 	 * file
436 	 */
437 		if (argc > 2) {
438 			for (n = 2; n < argc; n++)
439 				fprintf(stderr, "%s ", argv[n]);
440 			fprintf(stderr, "\n");
441 		}
442 		break;
443 
444 	case DNLNTYPE:
445 	/*
446 	 * dodnl - eat-up-to and including
447 	 * newline
448 	 */
449 		while ((c = gpbc()) != '\n' && c != EOF)
450 			;
451 		break;
452 
453 	case M4WRTYPE:
454 	/*
455 	 * dom4wrap - set up for
456 	 * wrap-up/wind-down activity
457 	 */
458 		if (argc > 2)
459 			dom4wrap(argv[2]);
460 		break;
461 
462 	case EXITTYPE:
463 	/*
464 	 * doexit - immediate exit from m4.
465 	 */
466 		killdiv();
467 		exit((argc > 2) ? atoi(argv[2]) : 0);
468 		break;
469 
470 	case DEFNTYPE:
471 		if (argc > 2)
472 			for (n = 2; n < argc; n++)
473 				dodefn(argv[n]);
474 		break;
475 
476 	case INDIRTYPE:	/* Indirect call */
477 		if (argc > 2)
478 			doindir(argv, argc);
479 		break;
480 
481 	case BUILTINTYPE: /* Builtins only */
482 		if (argc > 2)
483 			dobuiltin(argv, argc);
484 		break;
485 
486 	case PATSTYPE:
487 		if (argc > 2)
488 			dopatsubst(argv, argc);
489 		break;
490 	case REGEXPTYPE:
491 		if (argc > 2)
492 			doregexp(argv, argc);
493 		break;
494 	case LINETYPE:
495 		doprintlineno(infile+ilevel);
496 		break;
497 	case FILENAMETYPE:
498 		doprintfilename(infile+ilevel);
499 		break;
500 	case SELFTYPE:
501 		pbstr(rquote);
502 		pbstr(argv[1]);
503 		pbstr(lquote);
504 		break;
505 	default:
506 		m4errx(1, "eval: major botch.");
507 		break;
508 	}
509 }
510 
511 /*
512  * expand_macro - user-defined macro expansion
513  */
514 void
515 expand_macro(const char *argv[], int argc)
516 {
517 	const char *t;
518 	const char *p;
519 	int n;
520 	int argno;
521 
522 	t = argv[0];		       /* defn string as a whole */
523 	p = t;
524 	while (*p)
525 		p++;
526 	p--;			       /* last character of defn */
527 	while (p > t) {
528 		if (*(p - 1) != ARGFLAG)
529 			PUSHBACK(*p);
530 		else {
531 			switch (*p) {
532 
533 			case '#':
534 				pbnum(argc - 2);
535 				break;
536 			case '0':
537 			case '1':
538 			case '2':
539 			case '3':
540 			case '4':
541 			case '5':
542 			case '6':
543 			case '7':
544 			case '8':
545 			case '9':
546 				if ((argno = *p - '0') < argc - 1)
547 					pbstr(argv[argno + 1]);
548 				break;
549 			case '*':
550 				if (argc > 2) {
551 					for (n = argc - 1; n > 2; n--) {
552 						pbstr(argv[n]);
553 						pushback(COMMA);
554 					}
555 					pbstr(argv[2]);
556 				}
557 				break;
558                         case '@':
559 				if (argc > 2) {
560 					for (n = argc - 1; n > 2; n--) {
561 						pbstr(rquote);
562 						pbstr(argv[n]);
563 						pbstr(lquote);
564 						pushback(COMMA);
565 					}
566 					pbstr(rquote);
567 					pbstr(argv[2]);
568 					pbstr(lquote);
569 				}
570                                 break;
571 			default:
572 				PUSHBACK(*p);
573 				PUSHBACK('$');
574 				break;
575 			}
576 			p--;
577 		}
578 		p--;
579 	}
580 	if (p == t)		       /* do last character */
581 		PUSHBACK(*p);
582 }
583 
584 
585 /*
586  * dodefine - install definition in the table
587  */
588 void
589 dodefine(const char *name, const char *defn)
590 {
591 	if (!*name && !mimic_gnu)
592 		m4errx(1, "null definition.");
593 	else
594 		macro_define(name, defn);
595 }
596 
597 /*
598  * dodefn - push back a quoted definition of
599  *      the given name.
600  */
601 static void
602 dodefn(const char *name)
603 {
604 	struct macro_definition *p;
605 
606 	if ((p = lookup_macro_definition(name)) != NULL) {
607 		if ((p->type & TYPEMASK) == MACRTYPE) {
608 			pbstr(rquote);
609 			pbstr(p->defn);
610 			pbstr(lquote);
611 		} else {
612 			pbstr(p->defn);
613 			pbstr(BUILTIN_MARKER);
614 		}
615 	}
616 }
617 
618 /*
619  * dopushdef - install a definition in the hash table
620  *      without removing a previous definition. Since
621  *      each new entry is entered in *front* of the
622  *      hash bucket, it hides a previous definition from
623  *      lookup.
624  */
625 static void
626 dopushdef(const char *name, const char *defn)
627 {
628 	if (!*name && !mimic_gnu)
629 		m4errx(1, "null definition.");
630 	else
631 		macro_pushdef(name, defn);
632 }
633 
634 /*
635  * dump_one_def - dump the specified definition.
636  */
637 static void
638 dump_one_def(const char *name, struct macro_definition *p)
639 {
640 	if (!traceout)
641 		traceout = stderr;
642 	if (mimic_gnu) {
643 		if ((p->type & TYPEMASK) == MACRTYPE)
644 			fprintf(traceout, "%s:\t%s\n", name, p->defn);
645 		else {
646 			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
647 		}
648 	} else
649 		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
650 }
651 
652 /*
653  * dodumpdef - dump the specified definitions in the hash
654  *      table to stderr. If nothing is specified, the entire
655  *      hash table is dumped.
656  */
657 static void
658 dodump(const char *argv[], int argc)
659 {
660 	int n;
661 	struct macro_definition *p;
662 
663 	if (argc > 2) {
664 		for (n = 2; n < argc; n++)
665 			if ((p = lookup_macro_definition(argv[n])) != NULL)
666 				dump_one_def(argv[n], p);
667 	} else
668 		macro_for_all(dump_one_def);
669 }
670 
671 /*
672  * dotrace - mark some macros as traced/untraced depending upon on.
673  */
674 static void
675 dotrace(const char *argv[], int argc, int on)
676 {
677 	int n;
678 
679 	if (argc > 2) {
680 		for (n = 2; n < argc; n++)
681 			mark_traced(argv[n], on);
682 	} else
683 		mark_traced(NULL, on);
684 }
685 
686 /*
687  * doifelse - select one of two alternatives - loop.
688  */
689 static void
690 doifelse(const char *argv[], int argc)
691 {
692 	cycle {
693 		if (STREQ(argv[2], argv[3]))
694 			pbstr(argv[4]);
695 		else if (argc == 6)
696 			pbstr(argv[5]);
697 		else if (argc > 6) {
698 			argv += 3;
699 			argc -= 3;
700 			continue;
701 		}
702 		break;
703 	}
704 }
705 
706 /*
707  * doinclude - include a given file.
708  */
709 static int
710 doincl(const char *ifile)
711 {
712 	if (ilevel + 1 == MAXINP)
713 		m4errx(1, "too many include files.");
714 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
715 		ilevel++;
716 		bbase[ilevel] = bufbase = bp;
717 		return (1);
718 	} else
719 		return (0);
720 }
721 
722 #ifdef EXTENDED
723 /*
724  * dopaste - include a given file without any
725  *           macro processing.
726  */
727 static int
728 dopaste(const char *pfile)
729 {
730 	FILE *pf;
731 	int c;
732 
733 	if ((pf = fopen(pfile, "r")) != NULL) {
734 		if (synch_lines)
735 		    fprintf(active, "#line 1 \"%s\"\n", pfile);
736 		while ((c = getc(pf)) != EOF)
737 			putc(c, active);
738 		(void) fclose(pf);
739 		emit_synchline();
740 		return (1);
741 	} else
742 		return (0);
743 }
744 #endif
745 
746 /*
747  * dochq - change quote characters
748  */
749 static void
750 dochq(const char *argv[], int ac)
751 {
752 	if (ac == 2) {
753 		lquote[0] = LQUOTE; lquote[1] = EOS;
754 		rquote[0] = RQUOTE; rquote[1] = EOS;
755 	} else {
756 		strlcpy(lquote, argv[2], sizeof(lquote));
757 		if (ac > 3) {
758 			strlcpy(rquote, argv[3], sizeof(rquote));
759 		} else {
760 			rquote[0] = ECOMMT; rquote[1] = EOS;
761 		}
762 	}
763 }
764 
765 /*
766  * dochc - change comment characters
767  */
768 static void
769 dochc(const char *argv[], int argc)
770 {
771 /* XXX Note that there is no difference between no argument and a single
772  * empty argument.
773  */
774 	if (argc == 2) {
775 		scommt[0] = EOS;
776 		ecommt[0] = EOS;
777 	} else {
778 		strlcpy(scommt, argv[2], sizeof(scommt));
779 		if (argc == 3) {
780 			ecommt[0] = ECOMMT; ecommt[1] = EOS;
781 		} else {
782 			strlcpy(ecommt, argv[3], sizeof(ecommt));
783 		}
784 	}
785 }
786 
787 /*
788  * dom4wrap - expand text at EOF
789  */
790 static void
791 dom4wrap(const char *text)
792 {
793 	if (wrapindex >= maxwraps) {
794 		if (maxwraps == 0)
795 			maxwraps = 16;
796 		else
797 			maxwraps *= 2;
798 		m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps),
799 		   "too many m4wraps");
800 	}
801 	m4wraps[wrapindex++] = xstrdup(text);
802 }
803 
804 /*
805  * dodivert - divert the output to a temporary file
806  */
807 static void
808 dodiv(int n)
809 {
810 	int fd;
811 
812 	oindex = n;
813 	if (n >= maxout) {
814 		if (mimic_gnu)
815 			resizedivs(n + 10);
816 		else
817 			n = 0;		/* bitbucket */
818 	}
819 
820 	if (n < 0)
821 		n = 0;		       /* bitbucket */
822 	if (outfile[n] == NULL) {
823 		char fname[] = _PATH_DIVNAME;
824 
825 		if ((fd = mkstemp(fname)) < 0 ||
826 		    unlink(fname) == -1 ||
827 		    (outfile[n] = fdopen(fd, "w+")) == NULL)
828 			err(1, "%s: cannot divert", 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 disappear 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