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