Home | History | Annotate | Line # | Download | only in m4
eval.c revision 1.4
      1 /*      $NetBSD: eval.c,v 1.4 1995/09/28 05:37:28 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.4 1995/09/28 05:37:28 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 			for (n = argc - 1; n > 3; n--) {
    240 				putback(rquote);
    241 				pbstr(argv[n]);
    242 				putback(lquote);
    243 				putback(',');
    244 			}
    245 			putback(rquote);
    246 			pbstr(argv[3]);
    247 			putback(lquote);
    248 		}
    249 		break;
    250 
    251 	case DIVRTYPE:
    252 		if (argc > 2 && (n = atoi(argv[2])) != 0)
    253 			dodiv(n);
    254 		else {
    255 			active = stdout;
    256 			oindex = 0;
    257 		}
    258 		break;
    259 
    260 	case UNDVTYPE:
    261 		doundiv(argv, argc);
    262 		break;
    263 
    264 	case DIVNTYPE:
    265 	/*
    266 	 * dodivnum - return the number of
    267 	 * current output diversion
    268 	 */
    269 		pbnum(oindex);
    270 		break;
    271 
    272 	case UNDFTYPE:
    273 	/*
    274 	 * doundefine - undefine a previously
    275 	 * defined macro(s) or m4 keyword(s).
    276 	 */
    277 		if (argc > 2)
    278 			for (n = 2; n < argc; n++)
    279 				remhash(argv[n], ALL);
    280 		break;
    281 
    282 	case POPDTYPE:
    283 	/*
    284 	 * dopopdef - remove the topmost
    285 	 * definitions of macro(s) or m4
    286 	 * keyword(s).
    287 	 */
    288 		if (argc > 2)
    289 			for (n = 2; n < argc; n++)
    290 				remhash(argv[n], TOP);
    291 		break;
    292 
    293 	case MKTMTYPE:
    294 	/*
    295 	 * dotemp - create a temporary file
    296 	 */
    297 		if (argc > 2)
    298 			pbstr(mktemp(argv[2]));
    299 		break;
    300 
    301 	case TRNLTYPE:
    302 	/*
    303 	 * dotranslit - replace all characters in
    304 	 * the source string that appears in the
    305 	 * "from" string with the corresponding
    306 	 * characters in the "to" string.
    307 	 */
    308 		if (argc > 3) {
    309 			char temp[MAXTOK];
    310 			if (argc > 4)
    311 				map(temp, argv[2], argv[3], argv[4]);
    312 			else
    313 				map(temp, argv[2], argv[3], null);
    314 			pbstr(temp);
    315 		}
    316 		else if (argc > 2)
    317 			pbstr(argv[2]);
    318 		break;
    319 
    320 	case INDXTYPE:
    321 	/*
    322 	 * doindex - find the index of the second
    323 	 * argument string in the first argument
    324 	 * string. -1 if not present.
    325 	 */
    326 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
    327 		break;
    328 
    329 	case ERRPTYPE:
    330 	/*
    331 	 * doerrp - print the arguments to stderr
    332 	 * file
    333 	 */
    334 		if (argc > 2) {
    335 			for (n = 2; n < argc; n++)
    336 				fprintf(stderr, "%s ", argv[n]);
    337 			fprintf(stderr, "\n");
    338 		}
    339 		break;
    340 
    341 	case DNLNTYPE:
    342 	/*
    343 	 * dodnl - eat-up-to and including
    344 	 * newline
    345 	 */
    346 		while ((c = gpbc()) != '\n' && c != EOF)
    347 			;
    348 		break;
    349 
    350 	case M4WRTYPE:
    351 	/*
    352 	 * dom4wrap - set up for
    353 	 * wrap-up/wind-down activity
    354 	 */
    355 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
    356 		break;
    357 
    358 	case EXITTYPE:
    359 	/*
    360 	 * doexit - immediate exit from m4.
    361 	 */
    362 		killdiv();
    363 		exit((argc > 2) ? atoi(argv[2]) : 0);
    364 		break;
    365 
    366 	case DEFNTYPE:
    367 		if (argc > 2)
    368 			for (n = 2; n < argc; n++)
    369 				dodefn(argv[n]);
    370 		break;
    371 
    372 	default:
    373 		oops("%s: major botch.", "eval");
    374 		break;
    375 	}
    376 }
    377 
    378 char *dumpfmt = "`%s'\t`%s'\n";	       /* format string for dumpdef   */
    379 
    380 /*
    381  * expand - user-defined macro expansion
    382  */
    383 void
    384 expand(argv, argc)
    385 register char *argv[];
    386 register int argc;
    387 {
    388 	register char *t;
    389 	register char *p;
    390 	register int n;
    391 	register int argno;
    392 
    393 	t = argv[0];		       /* defn string as a whole */
    394 	p = t;
    395 	while (*p)
    396 		p++;
    397 	p--;			       /* last character of defn */
    398 	while (p > t) {
    399 		if (*(p - 1) != ARGFLAG)
    400 			putback(*p);
    401 		else {
    402 			switch (*p) {
    403 
    404 			case '#':
    405 				pbnum(argc - 2);
    406 				break;
    407 			case '0':
    408 			case '1':
    409 			case '2':
    410 			case '3':
    411 			case '4':
    412 			case '5':
    413 			case '6':
    414 			case '7':
    415 			case '8':
    416 			case '9':
    417 				if ((argno = *p - '0') < argc - 1)
    418 					pbstr(argv[argno + 1]);
    419 				break;
    420 			case '*':
    421 				for (n = argc - 1; n > 2; n--) {
    422 					pbstr(argv[n]);
    423 					putback(',');
    424 				}
    425 				pbstr(argv[2]);
    426 				break;
    427 			default:
    428 				putback(*p);
    429 				putback('$');
    430 				break;
    431 			}
    432 			p--;
    433 		}
    434 		p--;
    435 	}
    436 	if (p == t)		       /* do last character */
    437 		putback(*p);
    438 }
    439 
    440 /*
    441  * dodefine - install definition in the table
    442  */
    443 void
    444 dodefine(name, defn)
    445 register char *name;
    446 register char *defn;
    447 {
    448 	register ndptr p;
    449 
    450 	if (!*name)
    451 		oops("null definition.");
    452 	if (STREQ(name, defn))
    453 		oops("%s: recursive definition.", name);
    454 	if ((p = lookup(name)) == nil)
    455 		p = addent(name);
    456 	else if (p->defn != null)
    457 		free((char *) p->defn);
    458 	if (!*defn)
    459 		p->defn = null;
    460 	else
    461 		p->defn = xstrdup(defn);
    462 	p->type = MACRTYPE;
    463 }
    464 
    465 /*
    466  * dodefn - push back a quoted definition of
    467  *      the given name.
    468  */
    469 void
    470 dodefn(name)
    471 char *name;
    472 {
    473 	register ndptr p;
    474 
    475 	if ((p = lookup(name)) != nil && p->defn != null) {
    476 		putback(rquote);
    477 		pbstr(p->defn);
    478 		putback(lquote);
    479 	}
    480 }
    481 
    482 /*
    483  * dopushdef - install a definition in the hash table
    484  *      without removing a previous definition. Since
    485  *      each new entry is entered in *front* of the
    486  *      hash bucket, it hides a previous definition from
    487  *      lookup.
    488  */
    489 void
    490 dopushdef(name, defn)
    491 register char *name;
    492 register char *defn;
    493 {
    494 	register ndptr p;
    495 
    496 	if (!*name)
    497 		oops("null definition");
    498 	if (STREQ(name, defn))
    499 		oops("%s: recursive definition.", name);
    500 	p = addent(name);
    501 	if (!*defn)
    502 		p->defn = null;
    503 	else
    504 		p->defn = xstrdup(defn);
    505 	p->type = MACRTYPE;
    506 }
    507 
    508 /*
    509  * dodumpdef - dump the specified definitions in the hash
    510  *      table to stderr. If nothing is specified, the entire
    511  *      hash table is dumped.
    512  */
    513 void
    514 dodump(argv, argc)
    515 register char *argv[];
    516 register int argc;
    517 {
    518 	register int n;
    519 	ndptr p;
    520 
    521 	if (argc > 2) {
    522 		for (n = 2; n < argc; n++)
    523 			if ((p = lookup(argv[n])) != nil)
    524 				fprintf(stderr, dumpfmt, p->name,
    525 					p->defn);
    526 	}
    527 	else {
    528 		for (n = 0; n < HASHSIZE; n++)
    529 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
    530 				fprintf(stderr, dumpfmt, p->name,
    531 					p->defn);
    532 	}
    533 }
    534 
    535 /*
    536  * doifelse - select one of two alternatives - loop.
    537  */
    538 void
    539 doifelse(argv, argc)
    540 register char *argv[];
    541 register int argc;
    542 {
    543 	cycle {
    544 		if (STREQ(argv[2], argv[3]))
    545 			pbstr(argv[4]);
    546 		else if (argc == 6)
    547 			pbstr(argv[5]);
    548 		else if (argc > 6) {
    549 			argv += 3;
    550 			argc -= 3;
    551 			continue;
    552 		}
    553 		break;
    554 	}
    555 }
    556 
    557 /*
    558  * doinclude - include a given file.
    559  */
    560 int
    561 doincl(ifile)
    562 char *ifile;
    563 {
    564 	if (ilevel + 1 == MAXINP)
    565 		oops("too many include files.");
    566 	if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
    567 		ilevel++;
    568 		bbase[ilevel] = bufbase = bp;
    569 		return (1);
    570 	}
    571 	else
    572 		return (0);
    573 }
    574 
    575 #ifdef EXTENDED
    576 /*
    577  * dopaste - include a given file without any
    578  *           macro processing.
    579  */
    580 int
    581 dopaste(pfile)
    582 char *pfile;
    583 {
    584 	FILE *pf;
    585 	register int c;
    586 
    587 	if ((pf = fopen(pfile, "r")) != NULL) {
    588 		while ((c = getc(pf)) != EOF)
    589 			putc(c, active);
    590 		(void) fclose(pf);
    591 		return (1);
    592 	}
    593 	else
    594 		return (0);
    595 }
    596 #endif
    597 
    598 /*
    599  * dochq - change quote characters
    600  */
    601 void
    602 dochq(argv, argc)
    603 register char *argv[];
    604 register int argc;
    605 {
    606 	if (argc > 2) {
    607 		if (*argv[2])
    608 			lquote = *argv[2];
    609 		if (argc > 3) {
    610 			if (*argv[3])
    611 				rquote = *argv[3];
    612 		}
    613 		else
    614 			rquote = lquote;
    615 	}
    616 	else {
    617 		lquote = LQUOTE;
    618 		rquote = RQUOTE;
    619 	}
    620 }
    621 
    622 /*
    623  * dochc - change comment characters
    624  */
    625 void
    626 dochc(argv, argc)
    627 register char *argv[];
    628 register int argc;
    629 {
    630 	if (argc > 2) {
    631 		if (*argv[2])
    632 			scommt = *argv[2];
    633 		if (argc > 3) {
    634 			if (*argv[3])
    635 				ecommt = *argv[3];
    636 		}
    637 		else
    638 			ecommt = ECOMMT;
    639 	}
    640 	else {
    641 		scommt = SCOMMT;
    642 		ecommt = ECOMMT;
    643 	}
    644 }
    645 
    646 /*
    647  * dodivert - divert the output to a temporary file
    648  */
    649 void
    650 dodiv(n)
    651 register int n;
    652 {
    653 	if (n < 0 || n >= MAXOUT)
    654 		n = 0;		       /* bitbucket */
    655 	if (outfile[n] == NULL) {
    656 		m4temp[UNIQUE] = n + '0';
    657 		if ((outfile[n] = fopen(m4temp, "w")) == NULL)
    658 			oops("%s: cannot divert.", m4temp);
    659 	}
    660 	oindex = n;
    661 	active = outfile[n];
    662 }
    663 
    664 /*
    665  * doundivert - undivert a specified output, or all
    666  *              other outputs, in numerical order.
    667  */
    668 void
    669 doundiv(argv, argc)
    670 register char *argv[];
    671 register int argc;
    672 {
    673 	register int ind;
    674 	register int n;
    675 
    676 	if (argc > 2) {
    677 		for (ind = 2; ind < argc; ind++) {
    678 			n = atoi(argv[ind]);
    679 			if (n > 0 && n < MAXOUT && outfile[n] != NULL)
    680 				getdiv(n);
    681 
    682 		}
    683 	}
    684 	else
    685 		for (n = 1; n < MAXOUT; n++)
    686 			if (outfile[n] != NULL)
    687 				getdiv(n);
    688 }
    689 
    690 /*
    691  * dosub - select substring
    692  */
    693 void
    694 dosub(argv, argc)
    695 register char *argv[];
    696 register int argc;
    697 {
    698 	register char *ap, *fc, *k;
    699 	register int nc;
    700 
    701 	if (argc < 5)
    702 		nc = MAXTOK;
    703 	else
    704 #ifdef EXPR
    705 		nc = expr(argv[4]);
    706 #else
    707 		nc = atoi(argv[4]);
    708 #endif
    709 	ap = argv[2];		       /* target string */
    710 #ifdef EXPR
    711 	fc = ap + expr(argv[3]);       /* first char */
    712 #else
    713 	fc = ap + atoi(argv[3]);       /* first char */
    714 #endif
    715 	if (fc >= ap && fc < ap + strlen(ap))
    716 		for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
    717 			putback(*k);
    718 }
    719 
    720 /*
    721  * map:
    722  * map every character of s1 that is specified in from
    723  * into s3 and replace in s. (source s1 remains untouched)
    724  *
    725  * This is a standard implementation of map(s,from,to) function of ICON
    726  * language. Within mapvec, we replace every character of "from" with
    727  * the corresponding character in "to". If "to" is shorter than "from",
    728  * than the corresponding entries are null, which means that those
    729  * characters dissapear altogether. Furthermore, imagine
    730  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
    731  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
    732  * ultimately maps to `*'. In order to achieve this effect in an efficient
    733  * manner (i.e. without multiple passes over the destination string), we
    734  * loop over mapvec, starting with the initial source character. if the
    735  * character value (dch) in this location is different than the source
    736  * character (sch), sch becomes dch, once again to index into mapvec, until
    737  * the character value stabilizes (i.e. sch = dch, in other words
    738  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
    739  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
    740  * end, we restore mapvec* back to normal where mapvec[n] == n for
    741  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
    742  * about 5 times faster than any algorithm that makes multiple passes over
    743  * destination string.
    744  */
    745 void
    746 map(dest, src, from, to)
    747 register char *dest;
    748 register char *src;
    749 register char *from;
    750 register char *to;
    751 {
    752 	register char *tmp;
    753 	register char sch, dch;
    754 	static char mapvec[128] = {
    755 		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
    756 		12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
    757 		24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
    758 		36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
    759 		48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
    760 		60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
    761 		72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
    762 		84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
    763 		96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
    764 		108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
    765 		120, 121, 122, 123, 124, 125, 126, 127
    766 	};
    767 
    768 	if (*src) {
    769 		tmp = from;
    770 	/*
    771 	 * create a mapping between "from" and
    772 	 * "to"
    773 	 */
    774 		while (*from)
    775 			mapvec[*from++] = (*to) ? *to++ : (char) 0;
    776 
    777 		while (*src) {
    778 			sch = *src++;
    779 			dch = mapvec[sch];
    780 			while (dch != sch) {
    781 				sch = dch;
    782 				dch = mapvec[sch];
    783 			}
    784 			if (*dest = dch)
    785 				dest++;
    786 		}
    787 	/*
    788 	 * restore all the changed characters
    789 	 */
    790 		while (*tmp) {
    791 			mapvec[*tmp] = *tmp;
    792 			tmp++;
    793 		}
    794 	}
    795 	*dest = (char) 0;
    796 }
    797