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