Home | History | Annotate | Line # | Download | only in m4
eval.c revision 1.17
      1 /*	$NetBSD: eval.c,v 1.17 2003/08/07 11:14:30 agc Exp $	*/
      2 /*	$OpenBSD: eval.c,v 1.41 2001/10/10 23:25:31 espie Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 1989, 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * Ozan Yigit at York University.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #if defined(__RCSID) && !defined(lint)
     38 #if 0
     39 static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
     40 #else
     41 __RCSID("$NetBSD: eval.c,v 1.17 2003/08/07 11:14:30 agc Exp $");
     42 #endif
     43 #endif /* not lint */
     44 
     45 /*
     46  * eval.c
     47  * Facility: m4 macro processor
     48  * by: oz
     49  */
     50 
     51 #include <sys/types.h>
     52 #include <errno.h>
     53 #include <fcntl.h>
     54 #include <stdio.h>
     55 #include <stdlib.h>
     56 #include <stddef.h>
     57 #include <string.h>
     58 #include "mdef.h"
     59 #include "stdd.h"
     60 #include "extern.h"
     61 #include "pathnames.h"
     62 
     63 #define BUILTIN_MARKER	"__builtin_"
     64 
     65 static void	dodefn __P((const char *));
     66 static void	dopushdef __P((const char *, const char *));
     67 static void	dodump __P((const char *[], int));
     68 static void	dotrace __P((const char *[], int, int));
     69 static void	doifelse __P((const char *[], int));
     70 static int	doincl __P((const char *));
     71 static int	dopaste __P((const char *));
     72 static void	gnu_dochq __P((const char *[], int));
     73 static void	dochq __P((const char *[], int));
     74 static void	gnu_dochc __P((const char *[], int));
     75 static void	dochc __P((const char *[], int));
     76 static void	dodiv __P((int));
     77 static void	doundiv __P((const char *[], int));
     78 static void	dosub __P((const char *[], int));
     79 static void	map __P((char *, const char *, const char *, const char *));
     80 static const char *handledash __P((char *, char *, const char *));
     81 static void	expand_builtin __P((const char *[], int, int));
     82 static void	expand_macro __P((const char *[], int));
     83 static void	dump_one_def __P((ndptr));
     84 
     85 unsigned long	expansion_id;
     86 
     87 /*
     88  * eval - eval all macros and builtins calls
     89  *	  argc - number of elements in argv.
     90  *	  argv - element vector :
     91  *			argv[0] = definition of a user
     92  *				  macro or nil if built-in.
     93  *			argv[1] = name of the macro or
     94  *				  built-in.
     95  *			argv[2] = parameters to user-defined
     96  *			   .	  macro or built-in.
     97  *			   .
     98  *
     99  * A call in the form of macro-or-builtin() will result in:
    100  *			argv[0] = nullstr
    101  *			argv[1] = macro-or-builtin
    102  *			argv[2] = nullstr
    103  *
    104  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
    105  */
    106 void
    107 eval(argv, argc, td)
    108 	const char *argv[];
    109 	int argc;
    110 	int td;
    111 {
    112 	ssize_t mark = -1;
    113 
    114 	expansion_id++;
    115 	if (td & RECDEF)
    116 		errx(1, "%s at line %lu: expanding recursive definition for %s",
    117 			CURRENT_NAME, CURRENT_LINE, argv[1]);
    118 	if (traced_macros && is_traced(argv[1]))
    119 		mark = trace(argv, argc, infile+ilevel);
    120 	if (td == MACRTYPE)
    121 		expand_macro(argv, argc);
    122 	else
    123 		expand_builtin(argv, argc, td);
    124     	if (mark != -1)
    125 		finish_trace(mark);
    126 }
    127 
    128 /*
    129  * expand_builtin - evaluate built-in macros.
    130  */
    131 void
    132 expand_builtin(argv, argc, td)
    133 	const char *argv[];
    134 	int argc;
    135 	int td;
    136 {
    137 	int c, n;
    138 	int ac;
    139 	static int sysval = 0;
    140 
    141 #ifdef DEBUG
    142 	printf("argc = %d\n", argc);
    143 	for (n = 0; n < argc; n++)
    144 		printf("argv[%d] = %s\n", n, argv[n]);
    145 #endif
    146 
    147  /*
    148   * if argc == 3 and argv[2] is null, then we
    149   * have macro-or-builtin() type call. We adjust
    150   * argc to avoid further checking..
    151   */
    152   	ac = argc;
    153 
    154 	if (argc == 3 && !*(argv[2]))
    155 		argc--;
    156 
    157 	switch (td & TYPEMASK) {
    158 
    159 	case DEFITYPE:
    160 		if (argc > 2)
    161 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
    162 		break;
    163 
    164 	case PUSDTYPE:
    165 		if (argc > 2)
    166 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
    167 		break;
    168 
    169 	case DUMPTYPE:
    170 		dodump(argv, argc);
    171 		break;
    172 
    173 	case TRACEONTYPE:
    174 		dotrace(argv, argc, 1);
    175 		break;
    176 
    177 	case TRACEOFFTYPE:
    178 		dotrace(argv, argc, 0);
    179 		break;
    180 
    181 	case EXPRTYPE:
    182 	/*
    183 	 * doexpr - evaluate arithmetic
    184 	 * expression
    185 	 */
    186 		if (argc > 2)
    187 			pbnum(expr(argv[2]));
    188 		break;
    189 
    190 	case IFELTYPE:
    191 		if (argc > 4)
    192 			doifelse(argv, argc);
    193 		break;
    194 
    195 	case IFDFTYPE:
    196 	/*
    197 	 * doifdef - select one of two
    198 	 * alternatives based on the existence of
    199 	 * another definition
    200 	 */
    201 		if (argc > 3) {
    202 			if (lookup(argv[2]) != nil)
    203 				pbstr(argv[3]);
    204 			else if (argc > 4)
    205 				pbstr(argv[4]);
    206 		}
    207 		break;
    208 
    209 	case LENGTYPE:
    210 	/*
    211 	 * dolen - find the length of the
    212 	 * argument
    213 	 */
    214 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
    215 		break;
    216 
    217 	case INCRTYPE:
    218 	/*
    219 	 * doincr - increment the value of the
    220 	 * argument
    221 	 */
    222 		if (argc > 2)
    223 			pbnum(atoi(argv[2]) + 1);
    224 		break;
    225 
    226 	case DECRTYPE:
    227 	/*
    228 	 * dodecr - decrement the value of the
    229 	 * argument
    230 	 */
    231 		if (argc > 2)
    232 			pbnum(atoi(argv[2]) - 1);
    233 		break;
    234 
    235 	case SYSCTYPE:
    236 	/*
    237 	 * dosys - execute system command
    238 	 */
    239 		if (argc > 2)
    240 			sysval = system(argv[2]);
    241 		break;
    242 
    243 	case SYSVTYPE:
    244 	/*
    245 	 * dosysval - return value of the last
    246 	 * system call.
    247 	 *
    248 	 */
    249 		pbnum(sysval);
    250 		break;
    251 
    252 	case ESYSCMDTYPE:
    253 		if (argc > 2)
    254 			doesyscmd(argv[2]);
    255 	    	break;
    256 	case INCLTYPE:
    257 		if (argc > 2)
    258 			if (!doincl(argv[2]))
    259 				err(1, "%s at line %lu: include(%s)",
    260 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
    261 		break;
    262 
    263 	case SINCTYPE:
    264 		if (argc > 2)
    265 			(void) doincl(argv[2]);
    266 		break;
    267 #ifdef EXTENDED
    268 	case PASTTYPE:
    269 		if (argc > 2)
    270 			if (!dopaste(argv[2]))
    271 				err(1, "%s at line %lu: paste(%s)",
    272 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
    273 		break;
    274 
    275 	case SPASTYPE:
    276 		if (argc > 2)
    277 			(void) dopaste(argv[2]);
    278 		break;
    279 #endif
    280 	case CHNQTYPE:
    281 		if (mimic_gnu)
    282 			gnu_dochq(argv, ac);
    283 		else
    284 			dochq(argv, argc);
    285 		break;
    286 
    287 	case CHNCTYPE:
    288 		if (mimic_gnu)
    289 			gnu_dochc(argv, ac);
    290 		else
    291 			dochc(argv, argc);
    292 		break;
    293 
    294 	case SUBSTYPE:
    295 	/*
    296 	 * dosub - select substring
    297 	 *
    298 	 */
    299 		if (argc > 3)
    300 			dosub(argv, argc);
    301 		break;
    302 
    303 	case SHIFTYPE:
    304 	/*
    305 	 * doshift - push back all arguments
    306 	 * except the first one (i.e. skip
    307 	 * argv[2])
    308 	 */
    309 		if (argc > 3) {
    310 			for (n = argc - 1; n > 3; n--) {
    311 				pbstr(rquote);
    312 				pbstr(argv[n]);
    313 				pbstr(lquote);
    314 				putback(COMMA);
    315 			}
    316 			pbstr(rquote);
    317 			pbstr(argv[3]);
    318 			pbstr(lquote);
    319 		}
    320 		break;
    321 
    322 	case DIVRTYPE:
    323 		if (argc > 2 && (n = atoi(argv[2])) != 0)
    324 			dodiv(n);
    325 		else {
    326 			active = stdout;
    327 			oindex = 0;
    328 		}
    329 		break;
    330 
    331 	case UNDVTYPE:
    332 		doundiv(argv, argc);
    333 		break;
    334 
    335 	case DIVNTYPE:
    336 	/*
    337 	 * dodivnum - return the number of
    338 	 * current output diversion
    339 	 */
    340 		pbnum(oindex);
    341 		break;
    342 
    343 	case UNDFTYPE:
    344 	/*
    345 	 * doundefine - undefine a previously
    346 	 * defined macro(s) or m4 keyword(s).
    347 	 */
    348 		if (argc > 2)
    349 			for (n = 2; n < argc; n++)
    350 				remhash(argv[n], ALL);
    351 		break;
    352 
    353 	case POPDTYPE:
    354 	/*
    355 	 * dopopdef - remove the topmost
    356 	 * definitions of macro(s) or m4
    357 	 * keyword(s).
    358 	 */
    359 		if (argc > 2)
    360 			for (n = 2; n < argc; n++)
    361 				remhash(argv[n], TOP);
    362 		break;
    363 
    364 	case MKTMTYPE:
    365 	/*
    366 	 * dotemp - create a temporary file
    367 	 */
    368 		if (argc > 2) {
    369 			int fd;
    370 			char *temp;
    371 
    372 			temp = xstrdup(argv[2]);
    373 
    374 			fd = mkstemp(temp);
    375 			if (fd == -1)
    376 				err(1,
    377 	    "%s at line %lu: couldn't make temp file %s",
    378 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
    379 			close(fd);
    380 			pbstr(temp);
    381 			free(temp);
    382 		}
    383 		break;
    384 
    385 	case TRNLTYPE:
    386 	/*
    387 	 * dotranslit - replace all characters in
    388 	 * the source string that appears in the
    389 	 * "from" string with the corresponding
    390 	 * characters in the "to" string.
    391 	 */
    392 		if (argc > 3) {
    393 			char temp[STRSPMAX+1];
    394 			if (argc > 4)
    395 				map(temp, argv[2], argv[3], argv[4]);
    396 			else
    397 				map(temp, argv[2], argv[3], null);
    398 			pbstr(temp);
    399 		} else if (argc > 2)
    400 			pbstr(argv[2]);
    401 		break;
    402 
    403 	case INDXTYPE:
    404 	/*
    405 	 * doindex - find the index of the second
    406 	 * argument string in the first argument
    407 	 * string. -1 if not present.
    408 	 */
    409 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
    410 		break;
    411 
    412 	case ERRPTYPE:
    413 	/*
    414 	 * doerrp - print the arguments to stderr
    415 	 * file
    416 	 */
    417 		if (argc > 2) {
    418 			for (n = 2; n < argc; n++)
    419 				fprintf(stderr, "%s ", argv[n]);
    420 			fprintf(stderr, "\n");
    421 		}
    422 		break;
    423 
    424 	case DNLNTYPE:
    425 	/*
    426 	 * dodnl - eat-up-to and including
    427 	 * newline
    428 	 */
    429 		while ((c = gpbc()) != '\n' && c != EOF)
    430 			;
    431 		break;
    432 
    433 	case M4WRTYPE:
    434 	/*
    435 	 * dom4wrap - set up for
    436 	 * wrap-up/wind-down activity
    437 	 */
    438 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
    439 		break;
    440 
    441 	case EXITTYPE:
    442 	/*
    443 	 * doexit - immediate exit from m4.
    444 	 */
    445 		killdiv();
    446 		exit((argc > 2) ? atoi(argv[2]) : 0);
    447 		break;
    448 
    449 	case DEFNTYPE:
    450 		if (argc > 2)
    451 			for (n = 2; n < argc; n++)
    452 				dodefn(argv[n]);
    453 		break;
    454 
    455 	case INDIRTYPE:	/* Indirect call */
    456 		if (argc > 2)
    457 			doindir(argv, argc);
    458 		break;
    459 
    460 	case BUILTINTYPE: /* Builtins only */
    461 		if (argc > 2)
    462 			dobuiltin(argv, argc);
    463 		break;
    464 
    465 	case PATSTYPE:
    466 		if (argc > 2)
    467 			dopatsubst(argv, argc);
    468 		break;
    469 	case REGEXPTYPE:
    470 		if (argc > 2)
    471 			doregexp(argv, argc);
    472 		break;
    473 	case LINETYPE:
    474 		doprintlineno(infile+ilevel);
    475 		break;
    476 	case FILENAMETYPE:
    477 		doprintfilename(infile+ilevel);
    478 		break;
    479 	case SELFTYPE:
    480 		pbstr(rquote);
    481 		pbstr(argv[1]);
    482 		pbstr(lquote);
    483 		break;
    484 	default:
    485 		errx(1, "%s at line %lu: eval: major botch.",
    486 			CURRENT_NAME, CURRENT_LINE);
    487 		break;
    488 	}
    489 }
    490 
    491 /*
    492  * expand_macro - user-defined macro expansion
    493  */
    494 void
    495 expand_macro(argv, argc)
    496 	const char *argv[];
    497 	int argc;
    498 {
    499 	const char *t;
    500 	const char *p;
    501 	int n;
    502 	int argno;
    503 
    504 	t = argv[0];		       /* defn string as a whole */
    505 	p = t;
    506 	while (*p)
    507 		p++;
    508 	p--;			       /* last character of defn */
    509 	while (p > t) {
    510 		if (*(p - 1) != ARGFLAG)
    511 			PUTBACK(*p);
    512 		else {
    513 			switch (*p) {
    514 
    515 			case '#':
    516 				pbnum(argc - 2);
    517 				break;
    518 			case '0':
    519 			case '1':
    520 			case '2':
    521 			case '3':
    522 			case '4':
    523 			case '5':
    524 			case '6':
    525 			case '7':
    526 			case '8':
    527 			case '9':
    528 				if ((argno = *p - '0') < argc - 1)
    529 					pbstr(argv[argno + 1]);
    530 				break;
    531 			case '*':
    532 				if (argc > 2) {
    533 					for (n = argc - 1; n > 2; n--) {
    534 						pbstr(argv[n]);
    535 						putback(COMMA);
    536 					}
    537 					pbstr(argv[2]);
    538 			    	}
    539 				break;
    540                         case '@':
    541 				if (argc > 2) {
    542 					for (n = argc - 1; n > 2; n--) {
    543 						pbstr(rquote);
    544 						pbstr(argv[n]);
    545 						pbstr(lquote);
    546 						putback(COMMA);
    547 					}
    548 					pbstr(rquote);
    549 					pbstr(argv[2]);
    550 					pbstr(lquote);
    551 				}
    552                                 break;
    553 			default:
    554 				PUTBACK(*p);
    555 				PUTBACK('$');
    556 				break;
    557 			}
    558 			p--;
    559 		}
    560 		p--;
    561 	}
    562 	if (p == t)		       /* do last character */
    563 		PUTBACK(*p);
    564 }
    565 
    566 /*
    567  * dodefine - install definition in the table
    568  */
    569 void
    570 dodefine(name, defn)
    571 	const char *name;
    572 	const char *defn;
    573 {
    574 	ndptr p;
    575 	int n;
    576 
    577 	if (!*name)
    578 		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
    579 		    CURRENT_LINE);
    580 	if ((p = lookup(name)) == nil)
    581 		p = addent(name);
    582 	else if (p->defn != null)
    583 		free((char *) p->defn);
    584 	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
    585 		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
    586 		if (n != -1) {
    587 			p->type = n & TYPEMASK;
    588 			if ((n & NOARGS) == 0)
    589 				p->type |= NEEDARGS;
    590 			p->defn = null;
    591 			return;
    592 		}
    593 	}
    594 	if (!*defn)
    595 		p->defn = null;
    596 	else
    597 		p->defn = xstrdup(defn);
    598 	p->type = MACRTYPE;
    599 	if (STREQ(name, defn))
    600 		p->type |= RECDEF;
    601 }
    602 
    603 /*
    604  * dodefn - push back a quoted definition of
    605  *      the given name.
    606  */
    607 static void
    608 dodefn(name)
    609 	const char *name;
    610 {
    611 	ndptr p;
    612 	const char *real;
    613 
    614 	if ((p = lookup(name)) != nil) {
    615 		if (p->defn != null) {
    616 			pbstr(rquote);
    617 			pbstr(p->defn);
    618 			pbstr(lquote);
    619 		} else if ((real = builtin_realname(p->type)) != NULL) {
    620 			pbstr(real);
    621 			pbstr(BUILTIN_MARKER);
    622 		}
    623 	}
    624 }
    625 
    626 /*
    627  * dopushdef - install a definition in the hash table
    628  *      without removing a previous definition. Since
    629  *      each new entry is entered in *front* of the
    630  *      hash bucket, it hides a previous definition from
    631  *      lookup.
    632  */
    633 static void
    634 dopushdef(name, defn)
    635 	const char *name;
    636 	const char *defn;
    637 {
    638 	ndptr p;
    639 
    640 	if (!*name)
    641 		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
    642 		    CURRENT_LINE);
    643 	p = addent(name);
    644 	if (!*defn)
    645 		p->defn = null;
    646 	else
    647 		p->defn = xstrdup(defn);
    648 	p->type = MACRTYPE;
    649 	if (STREQ(name, defn))
    650 		p->type |= RECDEF;
    651 }
    652 
    653 /*
    654  * dump_one_def - dump the specified definition.
    655  */
    656 static void
    657 dump_one_def(p)
    658 	ndptr p;
    659 {
    660 	const char *real;
    661 
    662 	if (mimic_gnu) {
    663 		if ((p->type & TYPEMASK) == MACRTYPE)
    664 			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
    665 		else {
    666 			real = builtin_realname(p->type);
    667 			if (real == NULL)
    668 				real = null;
    669 			fprintf(traceout, "%s:\t<%s>\n", p->name, real);
    670 	    	}
    671 	} else
    672 		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
    673 }
    674 
    675 /*
    676  * dodumpdef - dump the specified definitions in the hash
    677  *      table to stderr. If nothing is specified, the entire
    678  *      hash table is dumped.
    679  */
    680 static void
    681 dodump(argv, argc)
    682 	const char *argv[];
    683 	int argc;
    684 {
    685 	int n;
    686 	ndptr p;
    687 
    688 	if (argc > 2) {
    689 		for (n = 2; n < argc; n++)
    690 			if ((p = lookup(argv[n])) != nil)
    691 				dump_one_def(p);
    692 	} else {
    693 		for (n = 0; n < HASHSIZE; n++)
    694 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
    695 				dump_one_def(p);
    696 	}
    697 }
    698 
    699 /*
    700  * dotrace - mark some macros as traced/untraced depending upon on.
    701  */
    702 static void
    703 dotrace(argv, argc, on)
    704 	const char *argv[];
    705 	int argc;
    706 	int on;
    707 {
    708 	int n;
    709 
    710 	if (argc > 2) {
    711 		for (n = 2; n < argc; n++)
    712 			mark_traced(argv[n], on);
    713 	} else
    714 		mark_traced(NULL, on);
    715 }
    716 
    717 /*
    718  * doifelse - select one of two alternatives - loop.
    719  */
    720 static void
    721 doifelse(argv, argc)
    722 	const char *argv[];
    723 	int argc;
    724 {
    725 	cycle {
    726 		if (STREQ(argv[2], argv[3]))
    727 			pbstr(argv[4]);
    728 		else if (argc == 6)
    729 			pbstr(argv[5]);
    730 		else if (argc > 6) {
    731 			argv += 3;
    732 			argc -= 3;
    733 			continue;
    734 		}
    735 		break;
    736 	}
    737 }
    738 
    739 /*
    740  * doinclude - include a given file.
    741  */
    742 static int
    743 doincl(ifile)
    744 	const char *ifile;
    745 {
    746 	if (ilevel + 1 == MAXINP)
    747 		errx(1, "%s at line %lu: too many include files.",
    748 		    CURRENT_NAME, CURRENT_LINE);
    749 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
    750 		ilevel++;
    751 		bbase[ilevel] = bufbase = bp;
    752 		return (1);
    753 	} else
    754 		return (0);
    755 }
    756 
    757 #ifdef EXTENDED
    758 /*
    759  * dopaste - include a given file without any
    760  *           macro processing.
    761  */
    762 static int
    763 dopaste(pfile)
    764 	const char *pfile;
    765 {
    766 	FILE *pf;
    767 	int c;
    768 
    769 	if ((pf = fopen(pfile, "r")) != NULL) {
    770 		while ((c = getc(pf)) != EOF)
    771 			putc(c, active);
    772 		(void) fclose(pf);
    773 		return (1);
    774 	} else
    775 		return (0);
    776 }
    777 #endif
    778 
    779 static void
    780 gnu_dochq(argv, ac)
    781 	const char *argv[];
    782 	int ac;
    783 {
    784 	/* In gnu-m4 mode, the only way to restore quotes is to have no
    785 	 * arguments at all. */
    786 	if (ac == 2) {
    787 		lquote[0] = LQUOTE, lquote[1] = EOS;
    788 		rquote[0] = RQUOTE, rquote[1] = EOS;
    789 	} else {
    790 		strlcpy(lquote, argv[2], sizeof(lquote));
    791 		if(ac > 3)
    792 			strlcpy(rquote, argv[3], sizeof(rquote));
    793 		else
    794 			rquote[0] = EOS;
    795 	}
    796 }
    797 
    798 /*
    799  * dochq - change quote characters
    800  */
    801 static void
    802 dochq(argv, argc)
    803 	const char *argv[];
    804 	int argc;
    805 {
    806 	if (argc > 2) {
    807 		if (*argv[2])
    808 			strlcpy(lquote, argv[2], sizeof(lquote));
    809 		else {
    810 			lquote[0] = LQUOTE;
    811 			lquote[1] = EOS;
    812 		}
    813 		if (argc > 3) {
    814 			if (*argv[3])
    815 				strlcpy(rquote, argv[3], sizeof(rquote));
    816 		} else
    817 			strcpy(rquote, lquote);
    818 	} else {
    819 		lquote[0] = LQUOTE, lquote[1] = EOS;
    820 		rquote[0] = RQUOTE, rquote[1] = EOS;
    821 	}
    822 }
    823 
    824 static void
    825 gnu_dochc(argv, ac)
    826 	const char *argv[];
    827 	int ac;
    828 {
    829 	/* In gnu-m4 mode, no arguments mean no comment
    830 	 * arguments at all. */
    831 	if (ac == 2) {
    832 		scommt[0] = EOS;
    833 		ecommt[0] = EOS;
    834 	} else {
    835 		if (*argv[2])
    836 			strlcpy(scommt, argv[2], sizeof(scommt));
    837 		else
    838 			scommt[0] = SCOMMT, scommt[1] = EOS;
    839 		if(ac > 3 && *argv[3])
    840 			strlcpy(ecommt, argv[3], sizeof(ecommt));
    841 		else
    842 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
    843 	}
    844 }
    845 /*
    846  * dochc - change comment characters
    847  */
    848 static void
    849 dochc(argv, argc)
    850 	const char *argv[];
    851 	int argc;
    852 {
    853 	if (argc > 2) {
    854 		if (*argv[2])
    855 			strlcpy(scommt, argv[2], sizeof(scommt));
    856 		if (argc > 3) {
    857 			if (*argv[3])
    858 				strlcpy(ecommt, argv[3], sizeof(ecommt));
    859 		}
    860 		else
    861 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
    862 	}
    863 	else {
    864 		scommt[0] = SCOMMT, scommt[1] = EOS;
    865 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
    866 	}
    867 }
    868 
    869 /*
    870  * dodivert - divert the output to a temporary file
    871  */
    872 static void
    873 dodiv(n)
    874 	int n;
    875 {
    876 	int fd;
    877 
    878 	oindex = n;
    879 	if (n >= maxout) {
    880 		if (mimic_gnu)
    881 			resizedivs(n + 10);
    882 		else
    883 			n = 0;		/* bitbucket */
    884     	}
    885 
    886 	if (n < 0)
    887 		n = 0;		       /* bitbucket */
    888 	if (outfile[n] == NULL) {
    889 		char fname[] = _PATH_DIVNAME;
    890 
    891 		if ((fd = mkstemp(fname)) < 0 ||
    892 			(outfile[n] = fdopen(fd, "w+")) == NULL)
    893 				err(1, "%s: cannot divert", fname);
    894 		if (unlink(fname) == -1)
    895 			err(1, "%s: cannot unlink", fname);
    896 	}
    897 	active = outfile[n];
    898 }
    899 
    900 /*
    901  * doundivert - undivert a specified output, or all
    902  *              other outputs, in numerical order.
    903  */
    904 static void
    905 doundiv(argv, argc)
    906 	const char *argv[];
    907 	int argc;
    908 {
    909 	int ind;
    910 	int n;
    911 
    912 	if (argc > 2) {
    913 		for (ind = 2; ind < argc; ind++) {
    914 			n = atoi(argv[ind]);
    915 			if (n > 0 && n < maxout && outfile[n] != NULL)
    916 				getdiv(n);
    917 
    918 		}
    919 	}
    920 	else
    921 		for (n = 1; n < maxout; n++)
    922 			if (outfile[n] != NULL)
    923 				getdiv(n);
    924 }
    925 
    926 /*
    927  * dosub - select substring
    928  */
    929 static void
    930 dosub(argv, argc)
    931 	const char *argv[];
    932 	int argc;
    933 {
    934 	const char *ap, *fc, *k;
    935 	int nc;
    936 
    937 	ap = argv[2];		       /* target string */
    938 #ifdef EXPR
    939 	fc = ap + expr(argv[3]);       /* first char */
    940 #else
    941 	fc = ap + atoi(argv[3]);       /* first char */
    942 #endif
    943 	nc = strlen(fc);
    944 	if (argc >= 5)
    945 #ifdef EXPR
    946 		nc = min(nc, expr(argv[4]));
    947 #else
    948 		nc = min(nc, atoi(argv[4]));
    949 #endif
    950 	if (fc >= ap && fc < ap + strlen(ap))
    951 		for (k = fc + nc - 1; k >= fc; k--)
    952 			putback(*k);
    953 }
    954 
    955 /*
    956  * map:
    957  * map every character of s1 that is specified in from
    958  * into s3 and replace in s. (source s1 remains untouched)
    959  *
    960  * This is a standard implementation of map(s,from,to) function of ICON
    961  * language. Within mapvec, we replace every character of "from" with
    962  * the corresponding character in "to". If "to" is shorter than "from",
    963  * than the corresponding entries are null, which means that those
    964  * characters dissapear altogether. Furthermore, imagine
    965  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
    966  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
    967  * ultimately maps to `*'. In order to achieve this effect in an efficient
    968  * manner (i.e. without multiple passes over the destination string), we
    969  * loop over mapvec, starting with the initial source character. if the
    970  * character value (dch) in this location is different than the source
    971  * character (sch), sch becomes dch, once again to index into mapvec, until
    972  * the character value stabilizes (i.e. sch = dch, in other words
    973  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
    974  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
    975  * end, we restore mapvec* back to normal where mapvec[n] == n for
    976  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
    977  * about 5 times faster than any algorithm that makes multiple passes over
    978  * destination string.
    979  */
    980 static void
    981 map(dest, src, from, to)
    982 	char *dest;
    983 	const char *src;
    984 	const char *from;
    985 	const char *to;
    986 {
    987 	const char *tmp;
    988 	unsigned char sch, dch;
    989 	static char frombis[257];
    990 	static char tobis[257];
    991 	static unsigned char mapvec[256] = {
    992 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
    993 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
    994 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
    995 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
    996 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
    997 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
    998 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
    999 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
   1000 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
   1001 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
   1002 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
   1003 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
   1004 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
   1005 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
   1006 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
   1007 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
   1008 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
   1009 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
   1010 	};
   1011 
   1012 	if (*src) {
   1013 		if (mimic_gnu) {
   1014 			/*
   1015 			 * expand character ranges on the fly
   1016 			 */
   1017 			from = handledash(frombis, frombis + 256, from);
   1018 			to = handledash(tobis, tobis + 256, to);
   1019 		}
   1020 		tmp = from;
   1021 	/*
   1022 	 * create a mapping between "from" and
   1023 	 * "to"
   1024 	 */
   1025 		while (*from)
   1026 			mapvec[(unsigned char)(*from++)] = (*to) ?
   1027 				(unsigned char)(*to++) : 0;
   1028 
   1029 		while (*src) {
   1030 			sch = (unsigned char)(*src++);
   1031 			dch = mapvec[sch];
   1032 			while (dch != sch) {
   1033 				sch = dch;
   1034 				dch = mapvec[sch];
   1035 			}
   1036 			if ((*dest = (char)dch))
   1037 				dest++;
   1038 		}
   1039 	/*
   1040 	 * restore all the changed characters
   1041 	 */
   1042 		while (*tmp) {
   1043 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
   1044 			tmp++;
   1045 		}
   1046 	}
   1047 	*dest = '\0';
   1048 }
   1049 
   1050 
   1051 /*
   1052  * handledash:
   1053  *  use buffer to copy the src string, expanding character ranges
   1054  * on the way.
   1055  */
   1056 static const char *
   1057 handledash(buffer, end, src)
   1058 	char *buffer;
   1059 	char *end;
   1060 	const char *src;
   1061 {
   1062 	char *p;
   1063 
   1064 	p = buffer;
   1065 	while(*src) {
   1066 		if (src[1] == '-' && src[2]) {
   1067 			unsigned char i;
   1068 			for (i = (unsigned char)src[0];
   1069 			    i <= (unsigned char)src[2]; i++) {
   1070 				*p++ = i;
   1071 				if (p == end) {
   1072 					*p = '\0';
   1073 					return buffer;
   1074 				}
   1075 			}
   1076 			src += 3;
   1077 		} else
   1078 			*p++ = *src++;
   1079 		if (p == end)
   1080 			break;
   1081 	}
   1082 	*p = '\0';
   1083 	return buffer;
   1084 }
   1085 
   1086