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