Home | History | Annotate | Line # | Download | only in m4
eval.c revision 1.8
      1 /*	$NetBSD: eval.c,v 1.8 1997/01/09 20:20:35 tls 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 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
     42 #else
     43 static char rcsid[] = "$NetBSD: eval.c,v 1.8 1997/01/09 20:20:35 tls Exp $";
     44 #endif
     45 #endif /* not lint */
     46 
     47 /*
     48  * eval.c
     49  * Facility: m4 macro processor
     50  * by: oz
     51  */
     52 
     53 #include <sys/types.h>
     54 #include <errno.h>
     55 #include <unistd.h>
     56 #include <stdio.h>
     57 #include <stdlib.h>
     58 #include <string.h>
     59 #include "mdef.h"
     60 #include "stdd.h"
     61 #include "extern.h"
     62 #include "pathnames.h"
     63 
     64 /*
     65  * eval - evaluate built-in macros.
     66  *	  argc - number of elements in argv.
     67  *	  argv - element vector :
     68  *			argv[0] = definition of a user
     69  *				  macro or nil if built-in.
     70  *			argv[1] = name of the macro or
     71  *				  built-in.
     72  *			argv[2] = parameters to user-defined
     73  *			   .	  macro or built-in.
     74  *			   .
     75  *
     76  * Note that the minimum value for argc is 3. A call in the form
     77  * of macro-or-builtin() will result in:
     78  *			argv[0] = nullstr
     79  *			argv[1] = macro-or-builtin
     80  *			argv[2] = nullstr
     81  */
     82 
     83 void
     84 eval(argv, argc, td)
     85 register char *argv[];
     86 register int argc;
     87 register int td;
     88 {
     89 	register int c, n;
     90 	static int sysval = 0;
     91 
     92 #ifdef DEBUG
     93 	printf("argc = %d\n", argc);
     94 	for (n = 0; n < argc; n++)
     95 		printf("argv[%d] = %s\n", n, argv[n]);
     96 #endif
     97  /*
     98   * if argc == 3 and argv[2] is null, then we
     99   * have macro-or-builtin() type call. We adjust
    100   * argc to avoid further checking..
    101   */
    102 	if (argc == 3 && !*(argv[2]))
    103 		argc--;
    104 
    105 	switch (td & ~STATIC) {
    106 
    107 	case DEFITYPE:
    108 		if (argc > 2)
    109 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
    110 		break;
    111 
    112 	case PUSDTYPE:
    113 		if (argc > 2)
    114 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
    115 		break;
    116 
    117 	case DUMPTYPE:
    118 		dodump(argv, argc);
    119 		break;
    120 
    121 	case EXPRTYPE:
    122 	/*
    123 	 * doexpr - evaluate arithmetic
    124 	 * expression
    125 	 */
    126 		if (argc > 2)
    127 			pbnum(expr(argv[2]));
    128 		break;
    129 
    130 	case IFELTYPE:
    131 		if (argc > 4)
    132 			doifelse(argv, argc);
    133 		break;
    134 
    135 	case IFDFTYPE:
    136 	/*
    137 	 * doifdef - select one of two
    138 	 * alternatives based on the existence of
    139 	 * another definition
    140 	 */
    141 		if (argc > 3) {
    142 			if (lookup(argv[2]) != nil)
    143 				pbstr(argv[3]);
    144 			else if (argc > 4)
    145 				pbstr(argv[4]);
    146 		}
    147 		break;
    148 
    149 	case LENGTYPE:
    150 	/*
    151 	 * dolen - find the length of the
    152 	 * argument
    153 	 */
    154 		if (argc > 2)
    155 			pbnum((argc > 2) ? strlen(argv[2]) : 0);
    156 		break;
    157 
    158 	case INCRTYPE:
    159 	/*
    160 	 * doincr - increment the value of the
    161 	 * argument
    162 	 */
    163 		if (argc > 2)
    164 			pbnum(atoi(argv[2]) + 1);
    165 		break;
    166 
    167 	case DECRTYPE:
    168 	/*
    169 	 * dodecr - decrement the value of the
    170 	 * argument
    171 	 */
    172 		if (argc > 2)
    173 			pbnum(atoi(argv[2]) - 1);
    174 		break;
    175 
    176 	case SYSCTYPE:
    177 	/*
    178 	 * dosys - execute system command
    179 	 */
    180 		if (argc > 2)
    181 			sysval = system(argv[2]);
    182 		break;
    183 
    184 	case SYSVTYPE:
    185 	/*
    186 	 * dosysval - return value of the last
    187 	 * system call.
    188 	 *
    189 	 */
    190 		pbnum(sysval);
    191 		break;
    192 
    193 	case INCLTYPE:
    194 		if (argc > 2)
    195 			if (!doincl(argv[2]))
    196 				oops("%s: %s", argv[2], strerror(errno));
    197 		break;
    198 
    199 	case SINCTYPE:
    200 		if (argc > 2)
    201 			(void) doincl(argv[2]);
    202 		break;
    203 #ifdef EXTENDED
    204 	case PASTTYPE:
    205 		if (argc > 2)
    206 			if (!dopaste(argv[2]))
    207 				oops("%s: %s", argv[2], strerror(errno));
    208 		break;
    209 
    210 	case SPASTYPE:
    211 		if (argc > 2)
    212 			(void) dopaste(argv[2]);
    213 		break;
    214 #endif
    215 	case CHNQTYPE:
    216 		dochq(argv, argc);
    217 		break;
    218 
    219 	case CHNCTYPE:
    220 		dochc(argv, argc);
    221 		break;
    222 
    223 	case SUBSTYPE:
    224 	/*
    225 	 * dosub - select substring
    226 	 *
    227 	 */
    228 		if (argc > 3)
    229 			dosub(argv, argc);
    230 		break;
    231 
    232 	case SHIFTYPE:
    233 	/*
    234 	 * doshift - push back all arguments
    235 	 * except the first one (i.e. skip
    236 	 * argv[2])
    237 	 */
    238 		if (argc > 3) {
    239 			int k;
    240 			for (n = argc - 1; n > 3; n--) {
    241 				k = strlen(rquote);
    242 				while (k--)
    243 					putback(rquote[k]);
    244 				pbstr(argv[n]);
    245 				k = strlen(lquote);
    246 				while (k--)
    247 					putback(lquote[k]);
    248 				putback(COMMA);
    249 			}
    250 			k = strlen(rquote);
    251 			while (k--)
    252 				putback(rquote[k]);
    253 			pbstr(argv[3]);
    254 			k = strlen(lquote);
    255 			while (k--)
    256 				putback(lquote[k]);
    257 		}
    258 		break;
    259 
    260 	case DIVRTYPE:
    261 		if (argc > 2 && (n = atoi(argv[2])) != 0)
    262 			dodiv(n);
    263 		else {
    264 			active = stdout;
    265 			oindex = 0;
    266 		}
    267 		break;
    268 
    269 	case UNDVTYPE:
    270 		doundiv(argv, argc);
    271 		break;
    272 
    273 	case DIVNTYPE:
    274 	/*
    275 	 * dodivnum - return the number of
    276 	 * current output diversion
    277 	 */
    278 		pbnum(oindex);
    279 		break;
    280 
    281 	case UNDFTYPE:
    282 	/*
    283 	 * doundefine - undefine a previously
    284 	 * defined macro(s) or m4 keyword(s).
    285 	 */
    286 		if (argc > 2)
    287 			for (n = 2; n < argc; n++)
    288 				remhash(argv[n], ALL);
    289 		break;
    290 
    291 	case POPDTYPE:
    292 	/*
    293 	 * dopopdef - remove the topmost
    294 	 * definitions of macro(s) or m4
    295 	 * keyword(s).
    296 	 */
    297 		if (argc > 2)
    298 			for (n = 2; n < argc; n++)
    299 				remhash(argv[n], TOP);
    300 		break;
    301 
    302 	case MKTMTYPE:
    303 	/*
    304 	 * dotemp - create a temporary file
    305 	 */
    306 		if (argc > 2)
    307 			pbstr(mktemp(argv[2]));
    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 		oops("%s: major botch.", "eval");
    383 		break;
    384 	}
    385 }
    386 
    387 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 register char *argv[];
    395 register int argc;
    396 {
    397 	register char *t;
    398 	register char *p;
    399 	register int n;
    400 	register 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 register char *name;
    466 register char *defn;
    467 {
    468 	register ndptr p;
    469 
    470 	if (!*name)
    471 		oops("null definition.");
    472 	if (STREQ(name, defn))
    473 		oops("%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 	register ndptr p;
    494 
    495 	if ((p = lookup(name)) != nil && p->defn != null) {
    496 		int n = strlen(rquote);
    497 		while (n--)
    498 			putback(rquote[n]);
    499 		pbstr(p->defn);
    500 		n = strlen(lquote);
    501 		while (n--)
    502 			putback(lquote[n]);
    503 	}
    504 }
    505 
    506 /*
    507  * dopushdef - install a definition in the hash table
    508  *      without removing a previous definition. Since
    509  *      each new entry is entered in *front* of the
    510  *      hash bucket, it hides a previous definition from
    511  *      lookup.
    512  */
    513 void
    514 dopushdef(name, defn)
    515 register char *name;
    516 register char *defn;
    517 {
    518 	register ndptr p;
    519 
    520 	if (!*name)
    521 		oops("null definition");
    522 	if (STREQ(name, defn))
    523 		oops("%s: recursive definition.", name);
    524 	p = addent(name);
    525 	if (!*defn)
    526 		p->defn = null;
    527 	else
    528 		p->defn = xstrdup(defn);
    529 	p->type = MACRTYPE;
    530 }
    531 
    532 /*
    533  * dodumpdef - dump the specified definitions in the hash
    534  *      table to stderr. If nothing is specified, the entire
    535  *      hash table is dumped.
    536  */
    537 void
    538 dodump(argv, argc)
    539 register char *argv[];
    540 register int argc;
    541 {
    542 	register int n;
    543 	ndptr p;
    544 
    545 	if (argc > 2) {
    546 		for (n = 2; n < argc; n++)
    547 			if ((p = lookup(argv[n])) != nil)
    548 				fprintf(stderr, dumpfmt, p->name,
    549 					p->defn);
    550 	}
    551 	else {
    552 		for (n = 0; n < HASHSIZE; n++)
    553 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
    554 				fprintf(stderr, dumpfmt, p->name,
    555 					p->defn);
    556 	}
    557 }
    558 
    559 /*
    560  * doifelse - select one of two alternatives - loop.
    561  */
    562 void
    563 doifelse(argv, argc)
    564 register char *argv[];
    565 register int argc;
    566 {
    567 	cycle {
    568 		if (STREQ(argv[2], argv[3]))
    569 			pbstr(argv[4]);
    570 		else if (argc == 6)
    571 			pbstr(argv[5]);
    572 		else if (argc > 6) {
    573 			argv += 3;
    574 			argc -= 3;
    575 			continue;
    576 		}
    577 		break;
    578 	}
    579 }
    580 
    581 /*
    582  * doinclude - include a given file.
    583  */
    584 int
    585 doincl(ifile)
    586 char *ifile;
    587 {
    588 	if (ilevel + 1 == MAXINP)
    589 		oops("too many include files.");
    590 	if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
    591 		ilevel++;
    592 		bbase[ilevel] = bufbase = bp;
    593 		return (1);
    594 	}
    595 	else
    596 		return (0);
    597 }
    598 
    599 #ifdef EXTENDED
    600 /*
    601  * dopaste - include a given file without any
    602  *           macro processing.
    603  */
    604 int
    605 dopaste(pfile)
    606 char *pfile;
    607 {
    608 	FILE *pf;
    609 	register int c;
    610 
    611 	if ((pf = fopen(pfile, "r")) != NULL) {
    612 		while ((c = getc(pf)) != EOF)
    613 			putc(c, active);
    614 		(void) fclose(pf);
    615 		return (1);
    616 	}
    617 	else
    618 		return (0);
    619 }
    620 #endif
    621 
    622 /*
    623  * dochq - change quote characters
    624  */
    625 void
    626 dochq(argv, argc)
    627 register char *argv[];
    628 register int argc;
    629 {
    630 	if (argc > 2) {
    631 		if (*argv[2])
    632 			strncpy(lquote, argv[2], MAXCCHARS);
    633 		if (argc > 3) {
    634 			if (*argv[3])
    635 				strncpy(rquote, argv[3], MAXCCHARS);
    636 		}
    637 		else
    638 			strcpy(rquote, lquote);
    639 	}
    640 	else {
    641 		lquote[0] = LQUOTE, lquote[1] = '\0';
    642 		rquote[0] = RQUOTE, rquote[1] = '\0';
    643 	}
    644 }
    645 
    646 /*
    647  * dochc - change comment characters
    648  */
    649 void
    650 dochc(argv, argc)
    651 register char *argv[];
    652 register int argc;
    653 {
    654 	if (argc > 2) {
    655 		if (*argv[2])
    656 			strncpy(scommt, argv[2], MAXCCHARS);
    657 		if (argc > 3) {
    658 			if (*argv[3])
    659 				strncpy(ecommt, argv[3], MAXCCHARS);
    660 		}
    661 		else
    662 			ecommt[0] = ECOMMT, ecommt[1] = '\0';
    663 	}
    664 	else {
    665 		scommt[0] = SCOMMT, scommt[1] = '\0';
    666 		ecommt[0] = ECOMMT, ecommt[1] = '\0';
    667 	}
    668 }
    669 
    670 /*
    671  * dodivert - divert the output to a temporary file
    672  */
    673 void
    674 dodiv(n)
    675 register int n;
    676 {
    677 	if (n < 0 || n >= MAXOUT)
    678 		n = 0;		       /* bitbucket */
    679 	if (outfile[n] == NULL) {
    680 		m4temp[UNIQUE] = n + '0';
    681 		if ((outfile[n] = fopen(m4temp, "w")) == NULL)
    682 			oops("%s: cannot divert.", m4temp);
    683 	}
    684 	oindex = n;
    685 	active = outfile[n];
    686 }
    687 
    688 /*
    689  * doundivert - undivert a specified output, or all
    690  *              other outputs, in numerical order.
    691  */
    692 void
    693 doundiv(argv, argc)
    694 register char *argv[];
    695 register int argc;
    696 {
    697 	register int ind;
    698 	register int n;
    699 
    700 	if (argc > 2) {
    701 		for (ind = 2; ind < argc; ind++) {
    702 			n = atoi(argv[ind]);
    703 			if (n > 0 && n < MAXOUT && outfile[n] != NULL)
    704 				getdiv(n);
    705 
    706 		}
    707 	}
    708 	else
    709 		for (n = 1; n < MAXOUT; n++)
    710 			if (outfile[n] != NULL)
    711 				getdiv(n);
    712 }
    713 
    714 /*
    715  * dosub - select substring
    716  */
    717 void
    718 dosub(argv, argc)
    719 register char *argv[];
    720 register int argc;
    721 {
    722 	register char *ap, *fc, *k;
    723 	register int nc;
    724 
    725 	if (argc < 5)
    726 		nc = MAXTOK;
    727 	else
    728 #ifdef EXPR
    729 		nc = expr(argv[4]);
    730 #else
    731 		nc = atoi(argv[4]);
    732 #endif
    733 	ap = argv[2];		       /* target string */
    734 #ifdef EXPR
    735 	fc = ap + expr(argv[3]);       /* first char */
    736 #else
    737 	fc = ap + atoi(argv[3]);       /* first char */
    738 #endif
    739 	if (fc >= ap && fc < ap + strlen(ap))
    740 		for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
    741 			putback(*k);
    742 }
    743 
    744 /*
    745  * map:
    746  * map every character of s1 that is specified in from
    747  * into s3 and replace in s. (source s1 remains untouched)
    748  *
    749  * This is a standard implementation of map(s,from,to) function of ICON
    750  * language. Within mapvec, we replace every character of "from" with
    751  * the corresponding character in "to". If "to" is shorter than "from",
    752  * than the corresponding entries are null, which means that those
    753  * characters dissapear altogether. Furthermore, imagine
    754  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
    755  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
    756  * ultimately maps to `*'. In order to achieve this effect in an efficient
    757  * manner (i.e. without multiple passes over the destination string), we
    758  * loop over mapvec, starting with the initial source character. if the
    759  * character value (dch) in this location is different than the source
    760  * character (sch), sch becomes dch, once again to index into mapvec, until
    761  * the character value stabilizes (i.e. sch = dch, in other words
    762  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
    763  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
    764  * end, we restore mapvec* back to normal where mapvec[n] == n for
    765  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
    766  * about 5 times faster than any algorithm that makes multiple passes over
    767  * destination string.
    768  */
    769 void
    770 map(dest, src, from, to)
    771 register char *dest;
    772 register char *src;
    773 register char *from;
    774 register char *to;
    775 {
    776 	register char *tmp;
    777 	register char sch, dch;
    778 	static char mapvec[128] = {
    779 		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
    780 		12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
    781 		24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
    782 		36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
    783 		48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
    784 		60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
    785 		72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
    786 		84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
    787 		96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
    788 		108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
    789 		120, 121, 122, 123, 124, 125, 126, 127
    790 	};
    791 
    792 	if (*src) {
    793 		tmp = from;
    794 	/*
    795 	 * create a mapping between "from" and
    796 	 * "to"
    797 	 */
    798 		while (*from)
    799 			mapvec[*from++] = (*to) ? *to++ : (char) 0;
    800 
    801 		while (*src) {
    802 			sch = *src++;
    803 			dch = mapvec[sch];
    804 			while (dch != sch) {
    805 				sch = dch;
    806 				dch = mapvec[sch];
    807 			}
    808 			if (*dest = dch)
    809 				dest++;
    810 		}
    811 	/*
    812 	 * restore all the changed characters
    813 	 */
    814 		while (*tmp) {
    815 			mapvec[*tmp] = *tmp;
    816 			tmp++;
    817 		}
    818 	}
    819 	*dest = (char) 0;
    820 }
    821