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