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