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