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