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