xref: /openbsd-src/usr.bin/m4/eval.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 /*
37  * eval.c
38  * Facility: m4 macro processor
39  * by: oz
40  */
41 
42 #include <sys/types.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <unistd.h>
47 #include <stdio.h>
48 #include <stdint.h>
49 #include <stdlib.h>
50 #include <stddef.h>
51 #include <string.h>
52 #include <fcntl.h>
53 #include "mdef.h"
54 #include "stdd.h"
55 #include "extern.h"
56 #include "pathnames.h"
57 
58 static void	dodefn(const char *);
59 static void	dopushdef(const char *, const char *);
60 static void	dodump(const char *[], int);
61 static void	dotrace(const char *[], int, int);
62 static void	doifelse(const char *[], int);
63 static int	doincl(const char *);
64 static int	dopaste(const char *);
65 static void	dochq(const char *[], int);
66 static void	dochc(const char *[], int);
67 static void	dom4wrap(const char *);
68 static void	dodiv(int);
69 static void	doundiv(const char *[], int);
70 static void	dosub(const char *[], int);
71 static void	map(char *, const char *, const char *, const char *);
72 static const char *handledash(char *, char *, const char *);
73 static void	expand_builtin(const char *[], int, int);
74 static void	expand_macro(const char *[], int);
75 static void	dump_one_def(const char *, struct macro_definition *);
76 
77 unsigned long	expansion_id;
78 
79 /*
80  * eval - eval all macros and builtins calls
81  *	  argc - number of elements in argv.
82  *	  argv - element vector :
83  *			argv[0] = definition of a user
84  *				  macro or NULL if built-in.
85  *			argv[1] = name of the macro or
86  *				  built-in.
87  *			argv[2] = parameters to user-defined
88  *			   .	  macro or built-in.
89  *			   .
90  *
91  * A call in the form of macro-or-builtin() will result in:
92  *			argv[0] = nullstr
93  *			argv[1] = macro-or-builtin
94  *			argv[2] = nullstr
95  *
96  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
97  */
98 void
99 eval(const char *argv[], int argc, int td, int is_traced)
100 {
101 	size_t mark = SIZE_MAX;
102 
103 	expansion_id++;
104 	if (td & RECDEF)
105 		m4errx(1, "expanding recursive definition for %s.", argv[1]);
106 	if (is_traced)
107 		mark = trace(argv, argc, infile+ilevel);
108 	if (td == MACRTYPE)
109 		expand_macro(argv, argc);
110 	else
111 		expand_builtin(argv, argc, td);
112 	if (mark != SIZE_MAX)
113 		finish_trace(mark);
114 }
115 
116 /*
117  * expand_builtin - evaluate built-in macros.
118  */
119 void
120 expand_builtin(const char *argv[], int argc, int td)
121 {
122 	int c, n;
123 	int ac;
124 	static int sysval = 0;
125 
126 #ifdef DEBUG
127 	printf("argc = %d\n", argc);
128 	for (n = 0; n < argc; n++)
129 		printf("argv[%d] = %s\n", n, argv[n]);
130 	fflush(stdout);
131 #endif
132 
133  /*
134   * if argc == 3 and argv[2] is null, then we
135   * have macro-or-builtin() type call. We adjust
136   * argc to avoid further checking..
137   */
138  /* we keep the initial value for those built-ins that differentiate
139   * between builtin() and builtin.
140   */
141 	ac = argc;
142 
143 	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
144 		argc--;
145 
146 	switch (td & TYPEMASK) {
147 
148 	case DEFITYPE:
149 		if (argc > 2)
150 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
151 		break;
152 
153 	case PUSDTYPE:
154 		if (argc > 2)
155 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
156 		break;
157 
158 	case DUMPTYPE:
159 		dodump(argv, argc);
160 		break;
161 
162 	case TRACEONTYPE:
163 		dotrace(argv, argc, 1);
164 		break;
165 
166 	case TRACEOFFTYPE:
167 		dotrace(argv, argc, 0);
168 		break;
169 
170 	case EXPRTYPE:
171 	/*
172 	 * doexpr - evaluate arithmetic
173 	 * expression
174 	 */
175 	{
176 		int base = 10;
177 		int maxdigits = 0;
178 		const char *errstr;
179 
180 		if (argc > 3) {
181 			base = strtonum(argv[3], 2, 36, &errstr);
182 			if (errstr) {
183 				m4errx(1, "expr: base %s invalid.", argv[3]);
184 			}
185 		}
186 		if (argc > 4) {
187 			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
188 			if (errstr) {
189 				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
190 			}
191 		}
192 		if (argc > 2)
193 			pbnumbase(expr(argv[2]), base, maxdigits);
194 		break;
195 	}
196 
197 	case IFELTYPE:
198 		if (argc > 4)
199 			doifelse(argv, argc);
200 		break;
201 
202 	case IFDFTYPE:
203 	/*
204 	 * doifdef - select one of two
205 	 * alternatives based on the existence of
206 	 * another definition
207 	 */
208 		if (argc > 3) {
209 			if (lookup_macro_definition(argv[2]) != NULL)
210 				pbstr(argv[3]);
211 			else if (argc > 4)
212 				pbstr(argv[4]);
213 		}
214 		break;
215 
216 	case LENGTYPE:
217 	/*
218 	 * dolen - find the length of the
219 	 * argument
220 	 */
221 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
222 		break;
223 
224 	case INCRTYPE:
225 	/*
226 	 * doincr - increment the value of the
227 	 * argument
228 	 */
229 		if (argc > 2)
230 			pbnum(atoi(argv[2]) + 1);
231 		break;
232 
233 	case DECRTYPE:
234 	/*
235 	 * dodecr - decrement the value of the
236 	 * argument
237 	 */
238 		if (argc > 2)
239 			pbnum(atoi(argv[2]) - 1);
240 		break;
241 
242 	case SYSCTYPE:
243 	/*
244 	 * dosys - execute system command
245 	 */
246 		if (argc > 2) {
247 			fflush(stdout);
248 			sysval = system(argv[2]);
249 		}
250 		break;
251 
252 	case SYSVTYPE:
253 	/*
254 	 * dosysval - return value of the last
255 	 * system call.
256 	 *
257 	 */
258 		pbnum(sysval);
259 		break;
260 
261 	case ESYSCMDTYPE:
262 		if (argc > 2)
263 			doesyscmd(argv[2]);
264 		break;
265 	case INCLTYPE:
266 		if (argc > 2) {
267 			if (!doincl(argv[2])) {
268 				if (mimic_gnu) {
269 					warn("%s at line %lu: include(%s)",
270 					    CURRENT_NAME, CURRENT_LINE, argv[2]);
271 					exit_code = 1;
272 				} else
273 					err(1, "%s at line %lu: include(%s)",
274 					    CURRENT_NAME, CURRENT_LINE, argv[2]);
275 			}
276 		}
277 		break;
278 
279 	case SINCTYPE:
280 		if (argc > 2)
281 			(void) doincl(argv[2]);
282 		break;
283 #ifdef EXTENDED
284 	case PASTTYPE:
285 		if (argc > 2)
286 			if (!dopaste(argv[2]))
287 				err(1, "%s at line %lu: paste(%s)",
288 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
289 		break;
290 
291 	case SPASTYPE:
292 		if (argc > 2)
293 			(void) dopaste(argv[2]);
294 		break;
295 	case FORMATTYPE:
296 		doformat(argv, argc);
297 		break;
298 #endif
299 	case CHNQTYPE:
300 		dochq(argv, ac);
301 		break;
302 
303 	case CHNCTYPE:
304 		dochc(argv, argc);
305 		break;
306 
307 	case SUBSTYPE:
308 	/*
309 	 * dosub - select substring
310 	 *
311 	 */
312 		if (argc > 3)
313 			dosub(argv, argc);
314 		break;
315 
316 	case SHIFTYPE:
317 	/*
318 	 * doshift - push back all arguments
319 	 * except the first one (i.e. skip
320 	 * argv[2])
321 	 */
322 		if (argc > 3) {
323 			for (n = argc - 1; n > 3; n--) {
324 				pbstr(rquote);
325 				pbstr(argv[n]);
326 				pbstr(lquote);
327 				pushback(COMMA);
328 			}
329 			pbstr(rquote);
330 			pbstr(argv[3]);
331 			pbstr(lquote);
332 		}
333 		break;
334 
335 	case DIVRTYPE:
336 		if (argc > 2 && (n = atoi(argv[2])) != 0)
337 			dodiv(n);
338 		else {
339 			active = stdout;
340 			oindex = 0;
341 		}
342 		break;
343 
344 	case UNDVTYPE:
345 		doundiv(argv, argc);
346 		break;
347 
348 	case DIVNTYPE:
349 	/*
350 	 * dodivnum - return the number of
351 	 * current output diversion
352 	 */
353 		pbnum(oindex);
354 		break;
355 
356 	case UNDFTYPE:
357 	/*
358 	 * doundefine - undefine a previously
359 	 * defined macro(s) or m4 keyword(s).
360 	 */
361 		if (argc > 2)
362 			for (n = 2; n < argc; n++)
363 				macro_undefine(argv[n]);
364 		break;
365 
366 	case POPDTYPE:
367 	/*
368 	 * dopopdef - remove the topmost
369 	 * definitions of macro(s) or m4
370 	 * keyword(s).
371 	 */
372 		if (argc > 2)
373 			for (n = 2; n < argc; n++)
374 				macro_popdef(argv[n]);
375 		break;
376 
377 	case MKTMTYPE:
378 	/*
379 	 * dotemp - create a temporary file
380 	 */
381 		if (argc > 2) {
382 			int fd;
383 			char *temp;
384 
385 			temp = xstrdup(argv[2]);
386 
387 			fd = mkstemp(temp);
388 			if (fd == -1)
389 				err(1,
390 	    "%s at line %lu: couldn't make temp file %s",
391 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
392 			close(fd);
393 			pbstr(temp);
394 			free(temp);
395 		}
396 		break;
397 
398 	case TRNLTYPE:
399 	/*
400 	 * dotranslit - replace all characters in
401 	 * the source string that appears in the
402 	 * "from" string with the corresponding
403 	 * characters in the "to" string.
404 	 */
405 		if (argc > 3) {
406 			char *temp;
407 
408 			temp = xalloc(strlen(argv[2])+1, NULL);
409 			if (argc > 4)
410 				map(temp, argv[2], argv[3], argv[4]);
411 			else
412 				map(temp, argv[2], argv[3], null);
413 			pbstr(temp);
414 			free(temp);
415 		} else if (argc > 2)
416 			pbstr(argv[2]);
417 		break;
418 
419 	case INDXTYPE:
420 	/*
421 	 * doindex - find the index of the second
422 	 * argument string in the first argument
423 	 * string. -1 if not present.
424 	 */
425 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
426 		break;
427 
428 	case ERRPTYPE:
429 	/*
430 	 * doerrp - print the arguments to stderr
431 	 * file
432 	 */
433 		if (argc > 2) {
434 			for (n = 2; n < argc; n++)
435 				fprintf(stderr, "%s ", argv[n]);
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 = xreallocarray(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 		    unlink(fname) == -1 ||
823 		    (outfile[n] = fdopen(fd, "w+")) == NULL)
824 			err(1, "%s: cannot divert", fname);
825 	}
826 	active = outfile[n];
827 }
828 
829 /*
830  * doundivert - undivert a specified output, or all
831  *              other outputs, in numerical order.
832  */
833 static void
834 doundiv(const char *argv[], int argc)
835 {
836 	int ind;
837 	int n;
838 
839 	if (argc > 2) {
840 		for (ind = 2; ind < argc; ind++) {
841 			const char *errstr;
842 			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
843 			if (errstr) {
844 				if (errno == EINVAL && mimic_gnu)
845 					getdivfile(argv[ind]);
846 			} else {
847 				if (n < maxout && outfile[n] != NULL)
848 					getdiv(n);
849 			}
850 		}
851 	}
852 	else
853 		for (n = 1; n < maxout; n++)
854 			if (outfile[n] != NULL)
855 				getdiv(n);
856 }
857 
858 /*
859  * dosub - select substring
860  */
861 static void
862 dosub(const char *argv[], int argc)
863 {
864 	const char *ap, *fc, *k;
865 	int nc;
866 
867 	ap = argv[2];		       /* target string */
868 #ifdef EXPR
869 	fc = ap + expr(argv[3]);       /* first char */
870 #else
871 	fc = ap + atoi(argv[3]);       /* first char */
872 #endif
873 	nc = strlen(fc);
874 	if (argc >= 5)
875 #ifdef EXPR
876 		nc = min(nc, expr(argv[4]));
877 #else
878 		nc = min(nc, atoi(argv[4]));
879 #endif
880 	if (fc >= ap && fc < ap + strlen(ap))
881 		for (k = fc + nc - 1; k >= fc; k--)
882 			pushback(*k);
883 }
884 
885 /*
886  * map:
887  * map every character of s1 that is specified in from
888  * into s3 and replace in s. (source s1 remains untouched)
889  *
890  * This is derived from the a standard implementation of map(s,from,to)
891  * function of ICON language. Within mapvec, we replace every character
892  * of "from" with the corresponding character in "to".
893  * If "to" is shorter than "from", than the corresponding entries are null,
894  * which means that those characters dissapear altogether.
895  */
896 static void
897 map(char *dest, const char *src, const char *from, const char *to)
898 {
899 	const char *tmp;
900 	unsigned char sch, dch;
901 	static char frombis[257];
902 	static char tobis[257];
903 	int i;
904 	char seen[256];
905 	static unsigned char mapvec[256] = {
906 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
907 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
908 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
909 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
910 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
911 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
912 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
913 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
914 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
915 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
916 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
917 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
918 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
919 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
920 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
921 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
922 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
923 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
924 	};
925 
926 	if (*src) {
927 		if (mimic_gnu) {
928 			/*
929 			 * expand character ranges on the fly
930 			 */
931 			from = handledash(frombis, frombis + 256, from);
932 			to = handledash(tobis, tobis + 256, to);
933 		}
934 		tmp = from;
935 	/*
936 	 * create a mapping between "from" and
937 	 * "to"
938 	 */
939 		for (i = 0; i < 256; i++)
940 			seen[i] = 0;
941 		while (*from) {
942 			if (!seen[(unsigned char)(*from)]) {
943 				mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
944 				seen[(unsigned char)(*from)] = 1;
945 			}
946 			from++;
947 			if (*to)
948 				to++;
949 		}
950 
951 		while (*src) {
952 			sch = (unsigned char)(*src++);
953 			dch = mapvec[sch];
954 			if ((*dest = (char)dch))
955 				dest++;
956 		}
957 	/*
958 	 * restore all the changed characters
959 	 */
960 		while (*tmp) {
961 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
962 			tmp++;
963 		}
964 	}
965 	*dest = '\0';
966 }
967 
968 
969 /*
970  * handledash:
971  *  use buffer to copy the src string, expanding character ranges
972  * on the way.
973  */
974 static const char *
975 handledash(char *buffer, char *end, const char *src)
976 {
977 	char *p;
978 
979 	p = buffer;
980 	while(*src) {
981 		if (src[1] == '-' && src[2]) {
982 			unsigned char i;
983 			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
984 				for (i = (unsigned char)src[0];
985 				    i <= (unsigned char)src[2]; i++) {
986 					*p++ = i;
987 					if (p == end) {
988 						*p = '\0';
989 						return buffer;
990 					}
991 				}
992 			} else {
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 			}
1002 			src += 3;
1003 		} else
1004 			*p++ = *src++;
1005 		if (p == end)
1006 			break;
1007 	}
1008 	*p = '\0';
1009 	return buffer;
1010 }
1011