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