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