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