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