xref: /dragonfly/usr.bin/m4/eval.c (revision 4afad3d8)
1 /*	$OpenBSD: eval.c,v 1.69 2011/03/24 11:23:08 espie 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  * $FreeBSD: src/usr.bin/m4/eval.c,v 1.28 2012/11/17 01:54:24 svnexp Exp $
36  */
37 
38 
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 <stdint.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <stddef.h>
54 #include <string.h>
55 #include <fcntl.h>
56 #include "mdef.h"
57 #include "stdd.h"
58 #include "extern.h"
59 #include "pathnames.h"
60 
61 static void	dodefn(const char *);
62 static void	dopushdef(const char *, const char *);
63 static void	dodump(const char *[], int);
64 static void	dotrace(const char *[], int, int);
65 static void	doifelse(const char *[], int);
66 static int	doincl(const char *);
67 static int	dopaste(const char *);
68 static void	dochq(const char *[], int);
69 static void	dochc(const char *[], int);
70 static void	dom4wrap(const char *);
71 static void	dodiv(int);
72 static void	doundiv(const char *[], int);
73 static void	dosub(const char *[], int);
74 static void	map(char *, const char *, const char *, const char *);
75 static const char *handledash(char *, char *, const char *);
76 static void	expand_builtin(const char *[], int, int);
77 static void	expand_macro(const char *[], int);
78 static void	dump_one_def(const char *, struct macro_definition *);
79 
80 unsigned long	expansion_id;
81 
82 /*
83  * eval - eval all macros and builtins calls
84  *	  argc - number of elements in argv.
85  *	  argv - element vector :
86  *			argv[0] = definition of a user
87  *				  macro or NULL if built-in.
88  *			argv[1] = name of the macro or
89  *				  built-in.
90  *			argv[2] = parameters to user-defined
91  *			   .	  macro or built-in.
92  *			   .
93  *
94  * A call in the form of macro-or-builtin() will result in:
95  *			argv[0] = nullstr
96  *			argv[1] = macro-or-builtin
97  *			argv[2] = nullstr
98  *
99  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
100  */
101 void
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 == MACRTYPE)
112 		expand_macro(argv, argc);
113 	else
114 		expand_builtin(argv, argc, td);
115 	if (mark != SIZE_MAX)
116 		finish_trace(mark);
117 }
118 
119 /*
120  * expand_builtin - evaluate built-in macros.
121  */
122 void
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 	int ac;
127 	static int sysval = 0;
128 
129 #ifdef DEBUG
130 	printf("argc = %d\n", argc);
131 	for (n = 0; n < argc; n++)
132 		printf("argv[%d] = %s\n", n, argv[n]);
133 	fflush(stdout);
134 #endif
135 
136  /*
137   * if argc == 3 and argv[2] is null, then we
138   * have macro-or-builtin() type call. We adjust
139   * argc to avoid further checking.
140   */
141  /* we keep the initial value for those built-ins that differentiate
142   * between builtin() and builtin.
143   */
144 	ac = argc;
145 
146 	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
147 		argc--;
148 
149 	switch (td & TYPEMASK) {
150 
151 	case DEFITYPE:
152 		if (argc > 2)
153 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
154 		break;
155 
156 	case PUSDTYPE:
157 		if (argc > 2)
158 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
159 		break;
160 
161 	case DUMPTYPE:
162 		dodump(argv, argc);
163 		break;
164 
165 	case TRACEONTYPE:
166 		dotrace(argv, argc, 1);
167 		break;
168 
169 	case TRACEOFFTYPE:
170 		dotrace(argv, argc, 0);
171 		break;
172 
173 	case EXPRTYPE:
174 	/*
175 	 * doexpr - evaluate arithmetic
176 	 * expression
177 	 */
178 	{
179 		int base = 10;
180 		int maxdigits = 0;
181 		const char *errstr;
182 
183 		if (argc > 3) {
184 			base = strtonum(argv[3], 2, 36, &errstr);
185 			if (errstr) {
186 				m4errx(1, "expr: base %s invalid.", argv[3]);
187 			}
188 		}
189 		if (argc > 4) {
190 			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
191 			if (errstr) {
192 				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
193 			}
194 		}
195 		if (argc > 2)
196 			pbnumbase(expr(argv[2]), base, maxdigits);
197 		break;
198 	}
199 
200 	case IFELTYPE:
201 		if (argc > 4)
202 			doifelse(argv, argc);
203 		break;
204 
205 	case IFDFTYPE:
206 	/*
207 	 * doifdef - select one of two
208 	 * alternatives based on the existence of
209 	 * 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 LENGTYPE:
220 	/*
221 	 * dolen - find the length of the
222 	 * argument
223 	 */
224 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
225 		break;
226 
227 	case INCRTYPE:
228 	/*
229 	 * doincr - increment the value of the
230 	 * argument
231 	 */
232 		if (argc > 2)
233 			pbnum(atoi(argv[2]) + 1);
234 		break;
235 
236 	case DECRTYPE:
237 	/*
238 	 * dodecr - decrement the value of the
239 	 * argument
240 	 */
241 		if (argc > 2)
242 			pbnum(atoi(argv[2]) - 1);
243 		break;
244 
245 	case SYSCTYPE:
246 	/*
247 	 * dosys - execute system command
248 	 */
249 		if (argc > 2) {
250 			fflush(stdout);
251 			sysval = system(argv[2]);
252 		}
253 		break;
254 
255 	case SYSVTYPE:
256 	/*
257 	 * dosysval - return value of the last
258 	 * system call.
259 	 *
260 	 */
261 		pbnum(sysval);
262 		break;
263 
264 	case ESYSCMDTYPE:
265 		if (argc > 2)
266 			doesyscmd(argv[2]);
267 		break;
268 	case INCLTYPE:
269 		if (argc > 2)
270 			if (!doincl(argv[2])) {
271 				if (mimic_gnu) {
272 					warn("%s at line %lu: include(%s)",
273 					    CURRENT_NAME, CURRENT_LINE, argv[2]);
274 					exit_code = 1;
275 				} else
276 					err(1, "%s at line %lu: include(%s)",
277 					    CURRENT_NAME, CURRENT_LINE, argv[2]);
278 			}
279 		break;
280 
281 	case SINCTYPE:
282 		if (argc > 2)
283 			doincl(argv[2]);
284 		break;
285 #ifdef EXTENDED
286 	case PASTTYPE:
287 		if (argc > 2)
288 			if (!dopaste(argv[2]))
289 				err(1, "%s at line %lu: paste(%s)",
290 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
291 		break;
292 
293 	case SPASTYPE:
294 		if (argc > 2)
295 			dopaste(argv[2]);
296 		break;
297 	case FORMATTYPE:
298 		doformat(argv, argc);
299 		break;
300 #endif
301 	case CHNQTYPE:
302 		dochq(argv, ac);
303 		break;
304 
305 	case CHNCTYPE:
306 		dochc(argv, argc);
307 		break;
308 
309 	case SUBSTYPE:
310 	/*
311 	 * dosub - select substring
312 	 *
313 	 */
314 		if (argc > 3)
315 			dosub(argv, argc);
316 		break;
317 
318 	case SHIFTYPE:
319 	/*
320 	 * doshift - push back all arguments
321 	 * except the first one (i.e. skip
322 	 * argv[2])
323 	 */
324 		if (argc > 3) {
325 			for (n = argc - 1; n > 3; n--) {
326 				pbstr(rquote);
327 				pbstr(argv[n]);
328 				pbstr(lquote);
329 				pushback(COMMA);
330 			}
331 			pbstr(rquote);
332 			pbstr(argv[3]);
333 			pbstr(lquote);
334 		}
335 		break;
336 
337 	case DIVRTYPE:
338 		if (argc > 2 && (n = atoi(argv[2])) != 0)
339 			dodiv(n);
340 		else {
341 			active = stdout;
342 			oindex = 0;
343 		}
344 		break;
345 
346 	case UNDVTYPE:
347 		doundiv(argv, argc);
348 		break;
349 
350 	case DIVNTYPE:
351 	/*
352 	 * dodivnum - return the number of
353 	 * current output diversion
354 	 */
355 		pbnum(oindex);
356 		break;
357 
358 	case UNDFTYPE:
359 	/*
360 	 * doundefine - undefine a previously
361 	 * defined macro(s) or m4 keyword(s).
362 	 */
363 		if (argc > 2)
364 			for (n = 2; n < argc; n++)
365 				macro_undefine(argv[n]);
366 		break;
367 
368 	case POPDTYPE:
369 	/*
370 	 * dopopdef - remove the topmost
371 	 * definitions of macro(s) or m4
372 	 * keyword(s).
373 	 */
374 		if (argc > 2)
375 			for (n = 2; n < argc; n++)
376 				macro_popdef(argv[n]);
377 		break;
378 
379 	case MKTMTYPE:
380 	/*
381 	 * dotemp - create a temporary file
382 	 */
383 		if (argc > 2) {
384 			int fd;
385 			char *temp;
386 
387 			temp = xstrdup(argv[2]);
388 
389 			fd = mkstemp(temp);
390 			if (fd == -1)
391 				err(1,
392 	    "%s at line %lu: couldn't make temp file %s",
393 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
394 			close(fd);
395 			pbstr(temp);
396 			free(temp);
397 		}
398 		break;
399 
400 	case TRNLTYPE:
401 	/*
402 	 * dotranslit - replace all characters in
403 	 * the source string that appears in the
404 	 * "from" string with the corresponding
405 	 * characters in the "to" string.
406 	 */
407 		if (argc > 3) {
408 			char *temp;
409 
410 			temp = xalloc(strlen(argv[2]) + 1, NULL);
411 			if (argc > 4)
412 				map(temp, argv[2], argv[3], argv[4]);
413 			else
414 				map(temp, argv[2], argv[3], null);
415 			pbstr(temp);
416 			free(temp);
417 		} else if (argc > 2)
418 			pbstr(argv[2]);
419 		break;
420 
421 	case INDXTYPE:
422 	/*
423 	 * doindex - find the index of the second
424 	 * argument string in the first argument
425 	 * string. -1 if not present.
426 	 */
427 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
428 		break;
429 
430 	case ERRPTYPE:
431 	/*
432 	 * doerrp - print the arguments to stderr
433 	 * file
434 	 */
435 		if (argc > 2) {
436 			for (n = 2; n < argc; n++)
437 				fprintf(stderr, "%s ", argv[n]);
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 	} else
646 		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
647 }
648 
649 /*
650  * dodumpdef - dump the specified definitions in the hash
651  *      table to stderr. If nothing is specified, the entire
652  *      hash table is dumped.
653  */
654 static void
dodump(const char * argv[],int argc)655 dodump(const char *argv[], int argc)
656 {
657 	int n;
658 	struct macro_definition *p;
659 
660 	if (argc > 2) {
661 		for (n = 2; n < argc; n++)
662 			if ((p = lookup_macro_definition(argv[n])) != NULL)
663 				dump_one_def(argv[n], p);
664 	} else
665 		macro_for_all(dump_one_def);
666 }
667 
668 /*
669  * dotrace - mark some macros as traced/untraced depending upon on.
670  */
671 static void
dotrace(const char * argv[],int argc,int on)672 dotrace(const char *argv[], int argc, int on)
673 {
674 	int n;
675 
676 	if (argc > 2) {
677 		for (n = 2; n < argc; n++)
678 			mark_traced(argv[n], on);
679 	} else
680 		mark_traced(NULL, on);
681 }
682 
683 /*
684  * doifelse - select one of two alternatives - loop.
685  */
686 static void
doifelse(const char * argv[],int argc)687 doifelse(const char *argv[], int argc)
688 {
689 	for(;;) {
690 		if (STREQ(argv[2], argv[3]))
691 			pbstr(argv[4]);
692 		else if (argc == 6)
693 			pbstr(argv[5]);
694 		else if (argc > 6) {
695 			argv += 3;
696 			argc -= 3;
697 			continue;
698 		}
699 		break;
700 	}
701 }
702 
703 /*
704  * doinclude - include a given file.
705  */
706 static int
doincl(const char * ifile)707 doincl(const char *ifile)
708 {
709 	if (ilevel + 1 == MAXINP)
710 		m4errx(1, "too many include files.");
711 	if (fopen_trypath(infile + ilevel + 1, ifile) != NULL) {
712 		ilevel++;
713 		bbase[ilevel] = bufbase = bp;
714 		return (1);
715 	} else
716 		return (0);
717 }
718 
719 #ifdef EXTENDED
720 /*
721  * dopaste - include a given file without any
722  *           macro processing.
723  */
724 static int
dopaste(const char * pfile)725 dopaste(const char *pfile)
726 {
727 	FILE *pf;
728 	int c;
729 
730 	if ((pf = fopen(pfile, "r")) != NULL) {
731 		if (synch_lines)
732 		    fprintf(active, "#line 1 \"%s\"\n", pfile);
733 		while ((c = getc(pf)) != EOF)
734 			putc(c, active);
735 		fclose(pf);
736 		emit_synchline();
737 		return (1);
738 	} else
739 		return (0);
740 }
741 #endif
742 
743 /*
744  * dochq - change quote characters
745  */
746 static void
dochq(const char * argv[],int ac)747 dochq(const char *argv[], int ac)
748 {
749 	if (ac == 2) {
750 		lquote[0] = LQUOTE; lquote[1] = EOS;
751 		rquote[0] = RQUOTE; rquote[1] = EOS;
752 	} else {
753 		strlcpy(lquote, argv[2], sizeof(lquote));
754 		if (ac > 3) {
755 			strlcpy(rquote, argv[3], sizeof(rquote));
756 		} else {
757 			rquote[0] = ECOMMT; rquote[1] = EOS;
758 		}
759 	}
760 }
761 
762 /*
763  * dochc - change comment characters
764  */
765 static void
dochc(const char * argv[],int argc)766 dochc(const char *argv[], int argc)
767 {
768 /* XXX Note that there is no difference between no argument and a single
769  * empty argument.
770  */
771 	if (argc == 2) {
772 		scommt[0] = EOS;
773 		ecommt[0] = EOS;
774 	} else {
775 		strlcpy(scommt, argv[2], sizeof(scommt));
776 		if (argc == 3) {
777 			ecommt[0] = ECOMMT; ecommt[1] = EOS;
778 		} else {
779 			strlcpy(ecommt, argv[3], sizeof(ecommt));
780 		}
781 	}
782 }
783 
784 /*
785  * dom4wrap - expand text at EOF
786  */
787 static void
dom4wrap(const char * text)788 dom4wrap(const char *text)
789 {
790 	if (wrapindex >= maxwraps) {
791 		if (maxwraps == 0)
792 			maxwraps = 16;
793 		else
794 			maxwraps *= 2;
795 		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
796 		   "too many m4wraps");
797 	}
798 	m4wraps[wrapindex++] = xstrdup(text);
799 }
800 
801 /*
802  * dodivert - divert the output to a temporary file
803  */
804 static void
dodiv(int n)805 dodiv(int n)
806 {
807 	int fd;
808 
809 	oindex = n;
810 	if (n >= maxout) {
811 		if (mimic_gnu)
812 			resizedivs(n + 10);
813 		else
814 			n = 0;		/* bitbucket */
815 	}
816 
817 	if (n < 0)
818 		n = 0;		       /* bitbucket */
819 	if (outfile[n] == NULL) {
820 		char fname[] = _PATH_DIVNAME;
821 
822 		if ((fd = mkstemp(fname)) < 0 ||
823 			(outfile[n] = fdopen(fd, "w+")) == NULL)
824 				err(1, "%s: cannot divert", fname);
825 		if (unlink(fname) == -1)
826 			err(1, "%s: cannot unlink", fname);
827 	}
828 	active = outfile[n];
829 }
830 
831 /*
832  * doundivert - undivert a specified output, or all
833  *              other outputs, in numerical order.
834  */
835 static void
doundiv(const char * argv[],int argc)836 doundiv(const char *argv[], int argc)
837 {
838 	int ind;
839 	int n;
840 
841 	if (argc > 2) {
842 		for (ind = 2; ind < argc; ind++) {
843 			const char *errstr;
844 			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
845 			if (errstr) {
846 				if (errno == EINVAL && mimic_gnu)
847 					getdivfile(argv[ind]);
848 			} else {
849 				if (n < maxout && outfile[n] != NULL)
850 					getdiv(n);
851 			}
852 		}
853 	}
854 	else
855 		for (n = 1; n < maxout; n++)
856 			if (outfile[n] != NULL)
857 				getdiv(n);
858 }
859 
860 /*
861  * dosub - select substring
862  */
863 static void
dosub(const char * argv[],int argc)864 dosub(const char *argv[], int argc)
865 {
866 	const char *ap, *fc, *k;
867 	int nc;
868 
869 	ap = argv[2];		       /* target string */
870 #ifdef EXPR
871 	fc = ap + expr(argv[3]);       /* first char */
872 #else
873 	fc = ap + atoi(argv[3]);       /* first char */
874 #endif
875 	nc = strlen(fc);
876 	if (argc >= 5)
877 #ifdef EXPR
878 		nc = min(nc, expr(argv[4]));
879 #else
880 		nc = min(nc, atoi(argv[4]));
881 #endif
882 	if (fc >= ap && fc < ap + strlen(ap))
883 		for (k = fc + nc - 1; k >= fc; k--)
884 			pushback(*k);
885 }
886 
887 /*
888  * map:
889  * map every character of s1 that is specified in from
890  * into s3 and replace in s. (source s1 remains untouched)
891  *
892  * This is derived from the a standard implementation of map(s,from,to)
893  * function of ICON language. Within mapvec, we replace every character
894  * of "from" with the corresponding character in "to".
895  * If "to" is shorter than "from", than the corresponding entries are null,
896  * which means that those characters dissapear altogether.
897  */
898 static void
map(char * dest,const char * src,const char * from,const char * to)899 map(char *dest, const char *src, const char *from, const char *to)
900 {
901 	const char *tmp;
902 	unsigned char sch, dch;
903 	static char frombis[257];
904 	static char tobis[257];
905 	int i;
906 	char seen[256];
907 	static unsigned char mapvec[256] = {
908 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
909 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
910 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
911 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
912 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
913 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
914 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
915 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
916 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
917 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
918 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
919 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
920 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
921 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
922 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
923 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
924 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
925 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
926 	};
927 
928 	if (*src) {
929 		if (mimic_gnu) {
930 			/*
931 			 * expand character ranges on the fly
932 			 */
933 			from = handledash(frombis, frombis + 256, from);
934 			to = handledash(tobis, tobis + 256, to);
935 		}
936 		tmp = from;
937 	/*
938 	 * create a mapping between "from" and
939 	 * "to"
940 	 */
941 		for (i = 0; i < 256; i++)
942 			seen[i] = 0;
943 		while (*from) {
944 			if (!seen[(unsigned char)(*from)]) {
945 				mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
946 				seen[(unsigned char)(*from)] = 1;
947 			}
948 			from++;
949 			if (*to)
950 				to++;
951 		}
952 
953 		while (*src) {
954 			sch = (unsigned char)(*src++);
955 			dch = mapvec[sch];
956 			if ((*dest = (char)dch))
957 				dest++;
958 		}
959 	/*
960 	 * restore all the changed characters
961 	 */
962 		while (*tmp) {
963 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
964 			tmp++;
965 		}
966 	}
967 	*dest = '\0';
968 }
969 
970 
971 /*
972  * handledash:
973  *  use buffer to copy the src string, expanding character ranges
974  * on the way.
975  */
976 static const char *
handledash(char * buffer,char * end,const char * src)977 handledash(char *buffer, char *end, const char *src)
978 {
979 	char *p;
980 
981 	p = buffer;
982 	while(*src) {
983 		if (src[1] == '-' && src[2]) {
984 			unsigned char i;
985 			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
986 				for (i = (unsigned char)src[0];
987 				    i <= (unsigned char)src[2]; i++) {
988 					*p++ = i;
989 					if (p == end) {
990 						*p = '\0';
991 						return buffer;
992 					}
993 				}
994 			} else {
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 			}
1004 			src += 3;
1005 		} else
1006 			*p++ = *src++;
1007 		if (p == end)
1008 			break;
1009 	}
1010 	*p = '\0';
1011 	return buffer;
1012 }
1013