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