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