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