xref: /openbsd/usr.bin/m4/eval.c (revision cca36db2)
1 /*	$OpenBSD: eval.c,v 1.70 2012/04/12 17:00:11 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 /*
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 <stdlib.h>
49 #include <stddef.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include "mdef.h"
53 #include "stdd.h"
54 #include "extern.h"
55 #include "pathnames.h"
56 
57 static void	dodefn(const char *);
58 static void	dopushdef(const char *, const char *);
59 static void	dodump(const char *[], int);
60 static void	dotrace(const char *[], int, int);
61 static void	doifelse(const char *[], int);
62 static int	doincl(const char *);
63 static int	dopaste(const char *);
64 static void	dochq(const char *[], int);
65 static void	dochc(const char *[], int);
66 static void	dom4wrap(const char *);
67 static void	dodiv(int);
68 static void	doundiv(const char *[], int);
69 static void	dosub(const char *[], int);
70 static void	map(char *, const char *, const char *, const char *);
71 static const char *handledash(char *, char *, const char *);
72 static void	expand_builtin(const char *[], int, int);
73 static void	expand_macro(const char *[], int);
74 static void	dump_one_def(const char *, struct macro_definition *);
75 
76 unsigned long	expansion_id;
77 
78 /*
79  * eval - eval all macros and builtins calls
80  *	  argc - number of elements in argv.
81  *	  argv - element vector :
82  *			argv[0] = definition of a user
83  *				  macro or NULL if built-in.
84  *			argv[1] = name of the macro or
85  *				  built-in.
86  *			argv[2] = parameters to user-defined
87  *			   .	  macro or built-in.
88  *			   .
89  *
90  * A call in the form of macro-or-builtin() will result in:
91  *			argv[0] = nullstr
92  *			argv[1] = macro-or-builtin
93  *			argv[2] = nullstr
94  *
95  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
96  */
97 void
98 eval(const char *argv[], int argc, int td, int is_traced)
99 {
100 	size_t mark = SIZE_MAX;
101 
102 	expansion_id++;
103 	if (td & RECDEF)
104 		m4errx(1, "expanding recursive definition for %s.", argv[1]);
105 	if (is_traced)
106 		mark = trace(argv, argc, infile+ilevel);
107 	if (td == MACRTYPE)
108 		expand_macro(argv, argc);
109 	else
110 		expand_builtin(argv, argc, td);
111 	if (mark != SIZE_MAX)
112 		finish_trace(mark);
113 }
114 
115 /*
116  * expand_builtin - evaluate built-in macros.
117  */
118 void
119 expand_builtin(const char *argv[], int argc, int td)
120 {
121 	int c, n;
122 	int ac;
123 	static int sysval = 0;
124 
125 #ifdef DEBUG
126 	printf("argc = %d\n", argc);
127 	for (n = 0; n < argc; n++)
128 		printf("argv[%d] = %s\n", n, argv[n]);
129 	fflush(stdout);
130 #endif
131 
132  /*
133   * if argc == 3 and argv[2] is null, then we
134   * have macro-or-builtin() type call. We adjust
135   * argc to avoid further checking..
136   */
137  /* we keep the initial value for those built-ins that differentiate
138   * between builtin() and builtin.
139   */
140 	ac = argc;
141 
142 	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
143 		argc--;
144 
145 	switch (td & TYPEMASK) {
146 
147 	case DEFITYPE:
148 		if (argc > 2)
149 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
150 		break;
151 
152 	case PUSDTYPE:
153 		if (argc > 2)
154 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
155 		break;
156 
157 	case DUMPTYPE:
158 		dodump(argv, argc);
159 		break;
160 
161 	case TRACEONTYPE:
162 		dotrace(argv, argc, 1);
163 		break;
164 
165 	case TRACEOFFTYPE:
166 		dotrace(argv, argc, 0);
167 		break;
168 
169 	case EXPRTYPE:
170 	/*
171 	 * doexpr - evaluate arithmetic
172 	 * expression
173 	 */
174 	{
175 		int base = 10;
176 		int maxdigits = 0;
177 		const char *errstr;
178 
179 		if (argc > 3) {
180 			base = strtonum(argv[3], 2, 36, &errstr);
181 			if (errstr) {
182 				m4errx(1, "expr: base %s invalid.", argv[3]);
183 			}
184 		}
185 		if (argc > 4) {
186 			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
187 			if (errstr) {
188 				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
189 			}
190 		}
191 		if (argc > 2)
192 			pbnumbase(expr(argv[2]), base, maxdigits);
193 		break;
194 	}
195 
196 	case IFELTYPE:
197 		if (argc > 4)
198 			doifelse(argv, argc);
199 		break;
200 
201 	case IFDFTYPE:
202 	/*
203 	 * doifdef - select one of two
204 	 * alternatives based on the existence of
205 	 * another definition
206 	 */
207 		if (argc > 3) {
208 			if (lookup_macro_definition(argv[2]) != NULL)
209 				pbstr(argv[3]);
210 			else if (argc > 4)
211 				pbstr(argv[4]);
212 		}
213 		break;
214 
215 	case LENGTYPE:
216 	/*
217 	 * dolen - find the length of the
218 	 * argument
219 	 */
220 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
221 		break;
222 
223 	case INCRTYPE:
224 	/*
225 	 * doincr - increment the value of the
226 	 * argument
227 	 */
228 		if (argc > 2)
229 			pbnum(atoi(argv[2]) + 1);
230 		break;
231 
232 	case DECRTYPE:
233 	/*
234 	 * dodecr - decrement the value of the
235 	 * argument
236 	 */
237 		if (argc > 2)
238 			pbnum(atoi(argv[2]) - 1);
239 		break;
240 
241 	case SYSCTYPE:
242 	/*
243 	 * dosys - execute system command
244 	 */
245 		if (argc > 2) {
246 			fflush(stdout);
247 			sysval = system(argv[2]);
248 		}
249 		break;
250 
251 	case SYSVTYPE:
252 	/*
253 	 * dosysval - return value of the last
254 	 * system call.
255 	 *
256 	 */
257 		pbnum(sysval);
258 		break;
259 
260 	case ESYSCMDTYPE:
261 		if (argc > 2)
262 			doesyscmd(argv[2]);
263 		break;
264 	case INCLTYPE:
265 		if (argc > 2)
266 			if (!doincl(argv[2]))
267 				if (mimic_gnu) {
268 					warn("%s at line %lu: include(%s)",
269 					    CURRENT_NAME, CURRENT_LINE, argv[2]);
270 					exit_code = 1;
271 				} else
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