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