1*25afcddbSespie /* $OpenBSD: eval.c,v 1.28 2000/07/27 17:44:32 espie Exp $ */ 2aa676ce1Smillert /* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1989, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * This code is derived from software contributed to Berkeley by 9df930be7Sderaadt * Ozan Yigit at York University. 10df930be7Sderaadt * 11df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 12df930be7Sderaadt * modification, are permitted provided that the following conditions 13df930be7Sderaadt * are met: 14df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 15df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 16df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 17df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 18df930be7Sderaadt * documentation and/or other materials provided with the distribution. 19df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 20df930be7Sderaadt * must display the following acknowledgement: 21df930be7Sderaadt * This product includes software developed by the University of 22df930be7Sderaadt * California, Berkeley and its contributors. 23df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 24df930be7Sderaadt * may be used to endorse or promote products derived from this software 25df930be7Sderaadt * without specific prior written permission. 26df930be7Sderaadt * 27df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37df930be7Sderaadt * SUCH DAMAGE. 38df930be7Sderaadt */ 39df930be7Sderaadt 40df930be7Sderaadt #ifndef lint 41df930be7Sderaadt #if 0 42df930be7Sderaadt static char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95"; 43df930be7Sderaadt #else 44*25afcddbSespie static char rcsid[] = "$OpenBSD: eval.c,v 1.28 2000/07/27 17:44:32 espie Exp $"; 45df930be7Sderaadt #endif 46df930be7Sderaadt #endif /* not lint */ 47df930be7Sderaadt 48df930be7Sderaadt /* 49df930be7Sderaadt * eval.c 50df930be7Sderaadt * Facility: m4 macro processor 51df930be7Sderaadt * by: oz 52df930be7Sderaadt */ 53df930be7Sderaadt 54df930be7Sderaadt #include <sys/types.h> 55df930be7Sderaadt #include <errno.h> 56df930be7Sderaadt #include <unistd.h> 57df930be7Sderaadt #include <stdio.h> 58df930be7Sderaadt #include <stdlib.h> 593f42598dSespie #include <stddef.h> 60df930be7Sderaadt #include <string.h> 61445b77f7Smillert #include <fcntl.h> 6281c2181eSespie #include <err.h> 63df930be7Sderaadt #include "mdef.h" 64df930be7Sderaadt #include "stdd.h" 65df930be7Sderaadt #include "extern.h" 66df930be7Sderaadt #include "pathnames.h" 67df930be7Sderaadt 68bb34cd6cSespie static void dodefn __P((const char *)); 69bb34cd6cSespie static void dopushdef __P((const char *, const char *)); 70bb34cd6cSespie static void dodump __P((const char *[], int)); 71bb34cd6cSespie static void doifelse __P((const char *[], int)); 72bb34cd6cSespie static int doincl __P((const char *)); 73bb34cd6cSespie static int dopaste __P((const char *)); 74bb34cd6cSespie static void dochq __P((const char *[], int)); 75bb34cd6cSespie static void dochc __P((const char *[], int)); 76bb34cd6cSespie static void dodiv __P((int)); 77bb34cd6cSespie static void doundiv __P((const char *[], int)); 78bb34cd6cSespie static void dosub __P((const char *[], int)); 79bb34cd6cSespie static void map __P((char *, const char *, const char *, const char *)); 8087c5c065Sespie static const char *handledash __P((char *, char *, const char *)); 81df930be7Sderaadt /* 82df930be7Sderaadt * eval - evaluate built-in macros. 83df930be7Sderaadt * argc - number of elements in argv. 84df930be7Sderaadt * argv - element vector : 85df930be7Sderaadt * argv[0] = definition of a user 86df930be7Sderaadt * macro or nil if built-in. 87df930be7Sderaadt * argv[1] = name of the macro or 88df930be7Sderaadt * built-in. 89df930be7Sderaadt * argv[2] = parameters to user-defined 90df930be7Sderaadt * . macro or built-in. 91df930be7Sderaadt * . 92df930be7Sderaadt * 93df930be7Sderaadt * Note that the minimum value for argc is 3. A call in the form 94df930be7Sderaadt * of macro-or-builtin() will result in: 95df930be7Sderaadt * argv[0] = nullstr 96df930be7Sderaadt * argv[1] = macro-or-builtin 97df930be7Sderaadt * argv[2] = nullstr 98df930be7Sderaadt */ 99df930be7Sderaadt 100df930be7Sderaadt void 101df930be7Sderaadt eval(argv, argc, td) 102bb34cd6cSespie const char *argv[]; 10353f6f6bfSespie int argc; 10453f6f6bfSespie int td; 105df930be7Sderaadt { 10653f6f6bfSespie int c, n; 107df930be7Sderaadt static int sysval = 0; 108df930be7Sderaadt 109df930be7Sderaadt #ifdef DEBUG 110df930be7Sderaadt printf("argc = %d\n", argc); 111df930be7Sderaadt for (n = 0; n < argc; n++) 112df930be7Sderaadt printf("argv[%d] = %s\n", n, argv[n]); 113df930be7Sderaadt #endif 114718b194dSespie 115718b194dSespie if (td & RECDEF) 1160d3ffe1dSespie errx(1, "%s at line %lu: expanding recursive definition for %s", 1170d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[1]); 118df930be7Sderaadt /* 119df930be7Sderaadt * if argc == 3 and argv[2] is null, then we 120df930be7Sderaadt * have macro-or-builtin() type call. We adjust 121df930be7Sderaadt * argc to avoid further checking.. 122df930be7Sderaadt */ 123df930be7Sderaadt if (argc == 3 && !*(argv[2])) 124df930be7Sderaadt argc--; 125df930be7Sderaadt 126718b194dSespie switch (td & TYPEMASK) { 127df930be7Sderaadt 128df930be7Sderaadt case DEFITYPE: 129df930be7Sderaadt if (argc > 2) 130df930be7Sderaadt dodefine(argv[2], (argc > 3) ? argv[3] : null); 131df930be7Sderaadt break; 132df930be7Sderaadt 133df930be7Sderaadt case PUSDTYPE: 134df930be7Sderaadt if (argc > 2) 135df930be7Sderaadt dopushdef(argv[2], (argc > 3) ? argv[3] : null); 136df930be7Sderaadt break; 137df930be7Sderaadt 138df930be7Sderaadt case DUMPTYPE: 139df930be7Sderaadt dodump(argv, argc); 140df930be7Sderaadt break; 141df930be7Sderaadt 142df930be7Sderaadt case EXPRTYPE: 143df930be7Sderaadt /* 144df930be7Sderaadt * doexpr - evaluate arithmetic 145df930be7Sderaadt * expression 146df930be7Sderaadt */ 147df930be7Sderaadt if (argc > 2) 148df930be7Sderaadt pbnum(expr(argv[2])); 149df930be7Sderaadt break; 150df930be7Sderaadt 151df930be7Sderaadt case IFELTYPE: 152df930be7Sderaadt if (argc > 4) 153df930be7Sderaadt doifelse(argv, argc); 154df930be7Sderaadt break; 155df930be7Sderaadt 156df930be7Sderaadt case IFDFTYPE: 157df930be7Sderaadt /* 158df930be7Sderaadt * doifdef - select one of two 159df930be7Sderaadt * alternatives based on the existence of 160df930be7Sderaadt * another definition 161df930be7Sderaadt */ 162df930be7Sderaadt if (argc > 3) { 163df930be7Sderaadt if (lookup(argv[2]) != nil) 164df930be7Sderaadt pbstr(argv[3]); 165df930be7Sderaadt else if (argc > 4) 166df930be7Sderaadt pbstr(argv[4]); 167df930be7Sderaadt } 168df930be7Sderaadt break; 169df930be7Sderaadt 170df930be7Sderaadt case LENGTYPE: 171df930be7Sderaadt /* 172df930be7Sderaadt * dolen - find the length of the 173df930be7Sderaadt * argument 174df930be7Sderaadt */ 175df930be7Sderaadt pbnum((argc > 2) ? strlen(argv[2]) : 0); 176df930be7Sderaadt break; 177df930be7Sderaadt 178df930be7Sderaadt case INCRTYPE: 179df930be7Sderaadt /* 180df930be7Sderaadt * doincr - increment the value of the 181df930be7Sderaadt * argument 182df930be7Sderaadt */ 183df930be7Sderaadt if (argc > 2) 184df930be7Sderaadt pbnum(atoi(argv[2]) + 1); 185df930be7Sderaadt break; 186df930be7Sderaadt 187df930be7Sderaadt case DECRTYPE: 188df930be7Sderaadt /* 189df930be7Sderaadt * dodecr - decrement the value of the 190df930be7Sderaadt * argument 191df930be7Sderaadt */ 192df930be7Sderaadt if (argc > 2) 193df930be7Sderaadt pbnum(atoi(argv[2]) - 1); 194df930be7Sderaadt break; 195df930be7Sderaadt 196df930be7Sderaadt case SYSCTYPE: 197df930be7Sderaadt /* 198df930be7Sderaadt * dosys - execute system command 199df930be7Sderaadt */ 200df930be7Sderaadt if (argc > 2) 201df930be7Sderaadt sysval = system(argv[2]); 202df930be7Sderaadt break; 203df930be7Sderaadt 204df930be7Sderaadt case SYSVTYPE: 205df930be7Sderaadt /* 206df930be7Sderaadt * dosysval - return value of the last 207df930be7Sderaadt * system call. 208df930be7Sderaadt * 209df930be7Sderaadt */ 210df930be7Sderaadt pbnum(sysval); 211df930be7Sderaadt break; 212df930be7Sderaadt 213c91edbbbSespie case ESYSCMDTYPE: 214c91edbbbSespie if (argc > 2) 215c91edbbbSespie doesyscmd(argv[2]); 216c91edbbbSespie break; 217df930be7Sderaadt case INCLTYPE: 218df930be7Sderaadt if (argc > 2) 219df930be7Sderaadt if (!doincl(argv[2])) 2200d3ffe1dSespie err(1, "%s at line %lu: include(%s)", 2210d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 222df930be7Sderaadt break; 223df930be7Sderaadt 224df930be7Sderaadt case SINCTYPE: 225df930be7Sderaadt if (argc > 2) 226df930be7Sderaadt (void) doincl(argv[2]); 227df930be7Sderaadt break; 228df930be7Sderaadt #ifdef EXTENDED 229df930be7Sderaadt case PASTTYPE: 230df930be7Sderaadt if (argc > 2) 231df930be7Sderaadt if (!dopaste(argv[2])) 2320d3ffe1dSespie err(1, "%s at line %lu: paste(%s)", 2330d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 234df930be7Sderaadt break; 235df930be7Sderaadt 236df930be7Sderaadt case SPASTYPE: 237df930be7Sderaadt if (argc > 2) 238df930be7Sderaadt (void) dopaste(argv[2]); 239df930be7Sderaadt break; 240df930be7Sderaadt #endif 241df930be7Sderaadt case CHNQTYPE: 242df930be7Sderaadt dochq(argv, argc); 243df930be7Sderaadt break; 244df930be7Sderaadt 245df930be7Sderaadt case CHNCTYPE: 246df930be7Sderaadt dochc(argv, argc); 247df930be7Sderaadt break; 248df930be7Sderaadt 249df930be7Sderaadt case SUBSTYPE: 250df930be7Sderaadt /* 251df930be7Sderaadt * dosub - select substring 252df930be7Sderaadt * 253df930be7Sderaadt */ 254df930be7Sderaadt if (argc > 3) 255df930be7Sderaadt dosub(argv, argc); 256df930be7Sderaadt break; 257df930be7Sderaadt 258df930be7Sderaadt case SHIFTYPE: 259df930be7Sderaadt /* 260df930be7Sderaadt * doshift - push back all arguments 261df930be7Sderaadt * except the first one (i.e. skip 262df930be7Sderaadt * argv[2]) 263df930be7Sderaadt */ 264df930be7Sderaadt if (argc > 3) { 265df930be7Sderaadt for (n = argc - 1; n > 3; n--) { 2663a73db8cSderaadt pbstr(rquote); 267df930be7Sderaadt pbstr(argv[n]); 2683a73db8cSderaadt pbstr(lquote); 269aa676ce1Smillert putback(COMMA); 270df930be7Sderaadt } 2713a73db8cSderaadt pbstr(rquote); 272df930be7Sderaadt pbstr(argv[3]); 2733a73db8cSderaadt pbstr(lquote); 274df930be7Sderaadt } 275df930be7Sderaadt break; 276df930be7Sderaadt 277df930be7Sderaadt case DIVRTYPE: 278df930be7Sderaadt if (argc > 2 && (n = atoi(argv[2])) != 0) 279df930be7Sderaadt dodiv(n); 280df930be7Sderaadt else { 281df930be7Sderaadt active = stdout; 282df930be7Sderaadt oindex = 0; 283df930be7Sderaadt } 284df930be7Sderaadt break; 285df930be7Sderaadt 286df930be7Sderaadt case UNDVTYPE: 287df930be7Sderaadt doundiv(argv, argc); 288df930be7Sderaadt break; 289df930be7Sderaadt 290df930be7Sderaadt case DIVNTYPE: 291df930be7Sderaadt /* 292df930be7Sderaadt * dodivnum - return the number of 293df930be7Sderaadt * current output diversion 294df930be7Sderaadt */ 295df930be7Sderaadt pbnum(oindex); 296df930be7Sderaadt break; 297df930be7Sderaadt 298df930be7Sderaadt case UNDFTYPE: 299df930be7Sderaadt /* 300df930be7Sderaadt * doundefine - undefine a previously 301df930be7Sderaadt * defined macro(s) or m4 keyword(s). 302df930be7Sderaadt */ 303df930be7Sderaadt if (argc > 2) 304df930be7Sderaadt for (n = 2; n < argc; n++) 305df930be7Sderaadt remhash(argv[n], ALL); 306df930be7Sderaadt break; 307df930be7Sderaadt 308df930be7Sderaadt case POPDTYPE: 309df930be7Sderaadt /* 310df930be7Sderaadt * dopopdef - remove the topmost 311df930be7Sderaadt * definitions of macro(s) or m4 312df930be7Sderaadt * keyword(s). 313df930be7Sderaadt */ 314df930be7Sderaadt if (argc > 2) 315df930be7Sderaadt for (n = 2; n < argc; n++) 316df930be7Sderaadt remhash(argv[n], TOP); 317df930be7Sderaadt break; 318df930be7Sderaadt 319df930be7Sderaadt case MKTMTYPE: 320df930be7Sderaadt /* 321df930be7Sderaadt * dotemp - create a temporary file 322df930be7Sderaadt */ 32301e71e69Sespie if (argc > 2) { 32401e71e69Sespie int fd; 325bb34cd6cSespie char *temp; 32601e71e69Sespie 327bb34cd6cSespie temp = xstrdup(argv[2]); 328bb34cd6cSespie 329bb34cd6cSespie fd = mkstemp(temp); 33001e71e69Sespie if (fd == -1) 3310d3ffe1dSespie err(1, 3320d3ffe1dSespie "%s at line %lu: couldn't make temp file %s", 3330d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 33401e71e69Sespie close(fd); 335bb34cd6cSespie pbstr(temp); 336bb34cd6cSespie free(temp); 33701e71e69Sespie } 338df930be7Sderaadt break; 339df930be7Sderaadt 340df930be7Sderaadt case TRNLTYPE: 341df930be7Sderaadt /* 342df930be7Sderaadt * dotranslit - replace all characters in 343df930be7Sderaadt * the source string that appears in the 344df930be7Sderaadt * "from" string with the corresponding 345df930be7Sderaadt * characters in the "to" string. 346df930be7Sderaadt */ 347df930be7Sderaadt if (argc > 3) { 3487d3e0b6bSderaadt char temp[STRSPMAX+1]; 349df930be7Sderaadt if (argc > 4) 350df930be7Sderaadt map(temp, argv[2], argv[3], argv[4]); 351df930be7Sderaadt else 352df930be7Sderaadt map(temp, argv[2], argv[3], null); 353df930be7Sderaadt pbstr(temp); 3547d3e0b6bSderaadt } else if (argc > 2) 355df930be7Sderaadt pbstr(argv[2]); 356df930be7Sderaadt break; 357df930be7Sderaadt 358df930be7Sderaadt case INDXTYPE: 359df930be7Sderaadt /* 360df930be7Sderaadt * doindex - find the index of the second 361df930be7Sderaadt * argument string in the first argument 362df930be7Sderaadt * string. -1 if not present. 363df930be7Sderaadt */ 364df930be7Sderaadt pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 365df930be7Sderaadt break; 366df930be7Sderaadt 367df930be7Sderaadt case ERRPTYPE: 368df930be7Sderaadt /* 369df930be7Sderaadt * doerrp - print the arguments to stderr 370df930be7Sderaadt * file 371df930be7Sderaadt */ 372df930be7Sderaadt if (argc > 2) { 373df930be7Sderaadt for (n = 2; n < argc; n++) 374df930be7Sderaadt fprintf(stderr, "%s ", argv[n]); 375df930be7Sderaadt fprintf(stderr, "\n"); 376df930be7Sderaadt } 377df930be7Sderaadt break; 378df930be7Sderaadt 379df930be7Sderaadt case DNLNTYPE: 380df930be7Sderaadt /* 381df930be7Sderaadt * dodnl - eat-up-to and including 382df930be7Sderaadt * newline 383df930be7Sderaadt */ 384df930be7Sderaadt while ((c = gpbc()) != '\n' && c != EOF) 385df930be7Sderaadt ; 386df930be7Sderaadt break; 387df930be7Sderaadt 388df930be7Sderaadt case M4WRTYPE: 389df930be7Sderaadt /* 390df930be7Sderaadt * dom4wrap - set up for 391df930be7Sderaadt * wrap-up/wind-down activity 392df930be7Sderaadt */ 393df930be7Sderaadt m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; 394df930be7Sderaadt break; 395df930be7Sderaadt 396df930be7Sderaadt case EXITTYPE: 397df930be7Sderaadt /* 398df930be7Sderaadt * doexit - immediate exit from m4. 399df930be7Sderaadt */ 400df930be7Sderaadt killdiv(); 401df930be7Sderaadt exit((argc > 2) ? atoi(argv[2]) : 0); 402df930be7Sderaadt break; 403df930be7Sderaadt 404df930be7Sderaadt case DEFNTYPE: 405df930be7Sderaadt if (argc > 2) 406df930be7Sderaadt for (n = 2; n < argc; n++) 407df930be7Sderaadt dodefn(argv[n]); 408df930be7Sderaadt break; 409df930be7Sderaadt 410b8161682Sespie case INDIRTYPE: /* Indirect call */ 411b8161682Sespie if (argc > 2) 412b8161682Sespie doindir(argv, argc); 413b8161682Sespie break; 414b8161682Sespie 415b8161682Sespie case BUILTINTYPE: /* Builtins only */ 416b8161682Sespie if (argc > 2) 417b8161682Sespie dobuiltin(argv, argc); 418b8161682Sespie break; 419b8161682Sespie 420b8161682Sespie case PATSTYPE: 421b8161682Sespie if (argc > 2) 422b8161682Sespie dopatsubst(argv, argc); 423b8161682Sespie break; 424b8161682Sespie case REGEXPTYPE: 425b8161682Sespie if (argc > 2) 426b8161682Sespie doregexp(argv, argc); 427b8161682Sespie break; 428b8161682Sespie case LINETYPE: 429b8161682Sespie doprintlineno(infile+ilevel); 430b8161682Sespie break; 431b8161682Sespie case FILENAMETYPE: 432b8161682Sespie doprintfilename(infile+ilevel); 433b8161682Sespie break; 434423624b7Sespie case SELFTYPE: 435423624b7Sespie pbstr(rquote); 436423624b7Sespie pbstr(argv[1]); 437423624b7Sespie pbstr(lquote); 438423624b7Sespie break; 439df930be7Sderaadt default: 4400d3ffe1dSespie errx(1, "%s at line %lu: eval: major botch.", 4410d3ffe1dSespie CURRENT_NAME, CURRENT_LINE); 442df930be7Sderaadt break; 443df930be7Sderaadt } 444df930be7Sderaadt } 445df930be7Sderaadt 446df930be7Sderaadt char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ 447df930be7Sderaadt 448df930be7Sderaadt /* 449df930be7Sderaadt * expand - user-defined macro expansion 450df930be7Sderaadt */ 451df930be7Sderaadt void 452df930be7Sderaadt expand(argv, argc) 453bb34cd6cSespie const char *argv[]; 45453f6f6bfSespie int argc; 455df930be7Sderaadt { 456bb34cd6cSespie const char *t; 457bb34cd6cSespie const char *p; 45853f6f6bfSespie int n; 45953f6f6bfSespie int argno; 460df930be7Sderaadt 461df930be7Sderaadt t = argv[0]; /* defn string as a whole */ 462df930be7Sderaadt p = t; 463df930be7Sderaadt while (*p) 464df930be7Sderaadt p++; 465df930be7Sderaadt p--; /* last character of defn */ 466df930be7Sderaadt while (p > t) { 467df930be7Sderaadt if (*(p - 1) != ARGFLAG) 468df930be7Sderaadt putback(*p); 469df930be7Sderaadt else { 470df930be7Sderaadt switch (*p) { 471df930be7Sderaadt 472df930be7Sderaadt case '#': 473df930be7Sderaadt pbnum(argc - 2); 474df930be7Sderaadt break; 475df930be7Sderaadt case '0': 476df930be7Sderaadt case '1': 477df930be7Sderaadt case '2': 478df930be7Sderaadt case '3': 479df930be7Sderaadt case '4': 480df930be7Sderaadt case '5': 481df930be7Sderaadt case '6': 482df930be7Sderaadt case '7': 483df930be7Sderaadt case '8': 484df930be7Sderaadt case '9': 485df930be7Sderaadt if ((argno = *p - '0') < argc - 1) 486df930be7Sderaadt pbstr(argv[argno + 1]); 487df930be7Sderaadt break; 488df930be7Sderaadt case '*': 489df930be7Sderaadt for (n = argc - 1; n > 2; n--) { 490df930be7Sderaadt pbstr(argv[n]); 491aa676ce1Smillert putback(COMMA); 492df930be7Sderaadt } 493df930be7Sderaadt pbstr(argv[2]); 494df930be7Sderaadt break; 495aa676ce1Smillert case '@': 496aa676ce1Smillert for (n = argc - 1; n > 2; n--) { 497aa676ce1Smillert pbstr(rquote); 498aa676ce1Smillert pbstr(argv[n]); 499aa676ce1Smillert pbstr(lquote); 500aa676ce1Smillert putback(COMMA); 501aa676ce1Smillert } 502aa676ce1Smillert pbstr(rquote); 503aa676ce1Smillert pbstr(argv[2]); 504aa676ce1Smillert pbstr(lquote); 505aa676ce1Smillert break; 506df930be7Sderaadt default: 507df930be7Sderaadt putback(*p); 508df930be7Sderaadt putback('$'); 509df930be7Sderaadt break; 510df930be7Sderaadt } 511df930be7Sderaadt p--; 512df930be7Sderaadt } 513df930be7Sderaadt p--; 514df930be7Sderaadt } 515df930be7Sderaadt if (p == t) /* do last character */ 516df930be7Sderaadt putback(*p); 517df930be7Sderaadt } 518df930be7Sderaadt 519df930be7Sderaadt /* 520df930be7Sderaadt * dodefine - install definition in the table 521df930be7Sderaadt */ 522df930be7Sderaadt void 523df930be7Sderaadt dodefine(name, defn) 524bb34cd6cSespie const char *name; 525bb34cd6cSespie const char *defn; 526df930be7Sderaadt { 52753f6f6bfSespie ndptr p; 528df930be7Sderaadt 529df930be7Sderaadt if (!*name) 5300d3ffe1dSespie errx(1, "%s at line %lu: null definition.", CURRENT_NAME, 5310d3ffe1dSespie CURRENT_LINE); 532df930be7Sderaadt if ((p = lookup(name)) == nil) 533df930be7Sderaadt p = addent(name); 534df930be7Sderaadt else if (p->defn != null) 535df930be7Sderaadt free((char *) p->defn); 536df930be7Sderaadt if (!*defn) 537df930be7Sderaadt p->defn = null; 538df930be7Sderaadt else 539df930be7Sderaadt p->defn = xstrdup(defn); 540df930be7Sderaadt p->type = MACRTYPE; 541718b194dSespie if (STREQ(name, defn)) 542718b194dSespie p->type |= RECDEF; 543df930be7Sderaadt } 544df930be7Sderaadt 545df930be7Sderaadt /* 546df930be7Sderaadt * dodefn - push back a quoted definition of 547df930be7Sderaadt * the given name. 548df930be7Sderaadt */ 549bb34cd6cSespie static void 550df930be7Sderaadt dodefn(name) 551bb34cd6cSespie const char *name; 552df930be7Sderaadt { 55353f6f6bfSespie ndptr p; 554df930be7Sderaadt 555df930be7Sderaadt if ((p = lookup(name)) != nil && p->defn != null) { 5563a73db8cSderaadt pbstr(rquote); 557df930be7Sderaadt pbstr(p->defn); 5583a73db8cSderaadt pbstr(lquote); 559df930be7Sderaadt } 560df930be7Sderaadt } 561df930be7Sderaadt 562df930be7Sderaadt /* 563df930be7Sderaadt * dopushdef - install a definition in the hash table 564df930be7Sderaadt * without removing a previous definition. Since 565df930be7Sderaadt * each new entry is entered in *front* of the 566df930be7Sderaadt * hash bucket, it hides a previous definition from 567df930be7Sderaadt * lookup. 568df930be7Sderaadt */ 569bb34cd6cSespie static void 570df930be7Sderaadt dopushdef(name, defn) 571bb34cd6cSespie const char *name; 572bb34cd6cSespie const char *defn; 573df930be7Sderaadt { 57453f6f6bfSespie ndptr p; 575df930be7Sderaadt 576df930be7Sderaadt if (!*name) 5770d3ffe1dSespie errx(1, "%s at line %lu: null definition", CURRENT_NAME, 5780d3ffe1dSespie CURRENT_LINE); 579df930be7Sderaadt p = addent(name); 580df930be7Sderaadt if (!*defn) 581df930be7Sderaadt p->defn = null; 582df930be7Sderaadt else 583df930be7Sderaadt p->defn = xstrdup(defn); 584df930be7Sderaadt p->type = MACRTYPE; 585718b194dSespie if (STREQ(name, defn)) 586718b194dSespie p->type |= RECDEF; 587df930be7Sderaadt } 588df930be7Sderaadt 589df930be7Sderaadt /* 590df930be7Sderaadt * dodumpdef - dump the specified definitions in the hash 591df930be7Sderaadt * table to stderr. If nothing is specified, the entire 592df930be7Sderaadt * hash table is dumped. 593df930be7Sderaadt */ 594bb34cd6cSespie static void 595df930be7Sderaadt dodump(argv, argc) 596bb34cd6cSespie const char *argv[]; 59753f6f6bfSespie int argc; 598df930be7Sderaadt { 59953f6f6bfSespie int n; 600df930be7Sderaadt ndptr p; 601df930be7Sderaadt 602df930be7Sderaadt if (argc > 2) { 603df930be7Sderaadt for (n = 2; n < argc; n++) 604df930be7Sderaadt if ((p = lookup(argv[n])) != nil) 605df930be7Sderaadt fprintf(stderr, dumpfmt, p->name, 606df930be7Sderaadt p->defn); 6077d3e0b6bSderaadt } else { 608df930be7Sderaadt for (n = 0; n < HASHSIZE; n++) 609df930be7Sderaadt for (p = hashtab[n]; p != nil; p = p->nxtptr) 610df930be7Sderaadt fprintf(stderr, dumpfmt, p->name, 611df930be7Sderaadt p->defn); 612df930be7Sderaadt } 613df930be7Sderaadt } 614df930be7Sderaadt 615df930be7Sderaadt /* 616df930be7Sderaadt * doifelse - select one of two alternatives - loop. 617df930be7Sderaadt */ 618bb34cd6cSespie static void 619df930be7Sderaadt doifelse(argv, argc) 620bb34cd6cSespie const char *argv[]; 62153f6f6bfSespie int argc; 622df930be7Sderaadt { 623df930be7Sderaadt cycle { 624df930be7Sderaadt if (STREQ(argv[2], argv[3])) 625df930be7Sderaadt pbstr(argv[4]); 626df930be7Sderaadt else if (argc == 6) 627df930be7Sderaadt pbstr(argv[5]); 628df930be7Sderaadt else if (argc > 6) { 629df930be7Sderaadt argv += 3; 630df930be7Sderaadt argc -= 3; 631df930be7Sderaadt continue; 632df930be7Sderaadt } 633df930be7Sderaadt break; 634df930be7Sderaadt } 635df930be7Sderaadt } 636df930be7Sderaadt 637df930be7Sderaadt /* 638df930be7Sderaadt * doinclude - include a given file. 639df930be7Sderaadt */ 640bb34cd6cSespie static int 641df930be7Sderaadt doincl(ifile) 642bb34cd6cSespie const char *ifile; 643df930be7Sderaadt { 644df930be7Sderaadt if (ilevel + 1 == MAXINP) 6450d3ffe1dSespie errx(1, "%s at line %lu: too many include files.", 6460d3ffe1dSespie CURRENT_NAME, CURRENT_LINE); 6470d3ffe1dSespie if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 648df930be7Sderaadt ilevel++; 649df930be7Sderaadt bbase[ilevel] = bufbase = bp; 650df930be7Sderaadt return (1); 6517d3e0b6bSderaadt } else 652df930be7Sderaadt return (0); 653df930be7Sderaadt } 654df930be7Sderaadt 655df930be7Sderaadt #ifdef EXTENDED 656df930be7Sderaadt /* 657df930be7Sderaadt * dopaste - include a given file without any 658df930be7Sderaadt * macro processing. 659df930be7Sderaadt */ 660bb34cd6cSespie static int 661df930be7Sderaadt dopaste(pfile) 662bb34cd6cSespie const char *pfile; 663df930be7Sderaadt { 664df930be7Sderaadt FILE *pf; 66553f6f6bfSespie int c; 666df930be7Sderaadt 667df930be7Sderaadt if ((pf = fopen(pfile, "r")) != NULL) { 668df930be7Sderaadt while ((c = getc(pf)) != EOF) 669df930be7Sderaadt putc(c, active); 670df930be7Sderaadt (void) fclose(pf); 671df930be7Sderaadt return (1); 6727d3e0b6bSderaadt } else 673df930be7Sderaadt return (0); 674df930be7Sderaadt } 675df930be7Sderaadt #endif 676df930be7Sderaadt 677df930be7Sderaadt /* 678df930be7Sderaadt * dochq - change quote characters 679df930be7Sderaadt */ 680bb34cd6cSespie static void 681df930be7Sderaadt dochq(argv, argc) 682bb34cd6cSespie const char *argv[]; 68353f6f6bfSespie int argc; 684df930be7Sderaadt { 68587c5c065Sespie /* In gnu-m4 mode, having two empty arguments means no quotes at 68687c5c065Sespie * all. */ 68787c5c065Sespie if (mimic_gnu) { 68887c5c065Sespie if (argc > 3 && !*argv[2] && !*argv[3]) { 68987c5c065Sespie lquote[0] = EOS; 69087c5c065Sespie rquote[0] = EOS; 69187c5c065Sespie return; 69287c5c065Sespie } 69387c5c065Sespie } 694df930be7Sderaadt if (argc > 2) { 69518a1973bSderaadt if (*argv[2]) 696b81b15b2Sespie strlcpy(lquote, argv[2], sizeof(lquote)); 69718a1973bSderaadt else { 69818a1973bSderaadt lquote[0] = LQUOTE; 699f0484631Sespie lquote[1] = EOS; 70018a1973bSderaadt } 701df930be7Sderaadt if (argc > 3) { 702df930be7Sderaadt if (*argv[3]) 703b81b15b2Sespie strlcpy(rquote, argv[3], sizeof(rquote)); 7047d3e0b6bSderaadt } else 70529a0bfdcSderaadt strcpy(rquote, lquote); 7067d3e0b6bSderaadt } else { 707f0484631Sespie lquote[0] = LQUOTE, lquote[1] = EOS; 708f0484631Sespie rquote[0] = RQUOTE, rquote[1] = EOS; 709df930be7Sderaadt } 710df930be7Sderaadt } 711df930be7Sderaadt 712df930be7Sderaadt /* 713df930be7Sderaadt * dochc - change comment characters 714df930be7Sderaadt */ 715bb34cd6cSespie static void 716df930be7Sderaadt dochc(argv, argc) 717bb34cd6cSespie const char *argv[]; 71853f6f6bfSespie int argc; 719df930be7Sderaadt { 720df930be7Sderaadt if (argc > 2) { 721df930be7Sderaadt if (*argv[2]) 722b81b15b2Sespie strlcpy(scommt, argv[2], sizeof(scommt)); 723df930be7Sderaadt if (argc > 3) { 724df930be7Sderaadt if (*argv[3]) 725b81b15b2Sespie strlcpy(ecommt, argv[3], sizeof(ecommt)); 726df930be7Sderaadt } 727df930be7Sderaadt else 728f0484631Sespie ecommt[0] = ECOMMT, ecommt[1] = EOS; 729df930be7Sderaadt } 730df930be7Sderaadt else { 731f0484631Sespie scommt[0] = SCOMMT, scommt[1] = EOS; 732f0484631Sespie ecommt[0] = ECOMMT, ecommt[1] = EOS; 733df930be7Sderaadt } 734df930be7Sderaadt } 735df930be7Sderaadt 736df930be7Sderaadt /* 737df930be7Sderaadt * dodivert - divert the output to a temporary file 738df930be7Sderaadt */ 739bb34cd6cSespie static void 740df930be7Sderaadt dodiv(n) 74153f6f6bfSespie int n; 742df930be7Sderaadt { 743445b77f7Smillert int fd; 744445b77f7Smillert 7457d3e0b6bSderaadt oindex = n; 746*25afcddbSespie if (n >= maxout) { 747*25afcddbSespie if (mimic_gnu) 748*25afcddbSespie resizedivs(n + 10); 749*25afcddbSespie else 750*25afcddbSespie n = 0; /* bitbucket */ 751*25afcddbSespie } 752*25afcddbSespie 753*25afcddbSespie if (n < 0) 754df930be7Sderaadt n = 0; /* bitbucket */ 755df930be7Sderaadt if (outfile[n] == NULL) { 7563f42598dSespie char fname[] = _PATH_DIVNAME; 7573f42598dSespie 7583f42598dSespie if ((fd = mkstemp(fname)) < 0 || 7593f42598dSespie (outfile[n] = fdopen(fd, "w+")) == NULL) 7603f42598dSespie err(1, "%s: cannot divert", fname); 7613f42598dSespie if (unlink(fname) == -1) 7623f42598dSespie err(1, "%s: cannot unlink", fname); 763df930be7Sderaadt } 764df930be7Sderaadt active = outfile[n]; 765df930be7Sderaadt } 766df930be7Sderaadt 767df930be7Sderaadt /* 768df930be7Sderaadt * doundivert - undivert a specified output, or all 769df930be7Sderaadt * other outputs, in numerical order. 770df930be7Sderaadt */ 771bb34cd6cSespie static void 772df930be7Sderaadt doundiv(argv, argc) 773bb34cd6cSespie const char *argv[]; 77453f6f6bfSespie int argc; 775df930be7Sderaadt { 77653f6f6bfSespie int ind; 77753f6f6bfSespie int n; 778df930be7Sderaadt 779df930be7Sderaadt if (argc > 2) { 780df930be7Sderaadt for (ind = 2; ind < argc; ind++) { 781df930be7Sderaadt n = atoi(argv[ind]); 782*25afcddbSespie if (n > 0 && n < maxout && outfile[n] != NULL) 783df930be7Sderaadt getdiv(n); 784df930be7Sderaadt 785df930be7Sderaadt } 786df930be7Sderaadt } 787df930be7Sderaadt else 788*25afcddbSespie for (n = 1; n < maxout; n++) 789df930be7Sderaadt if (outfile[n] != NULL) 790df930be7Sderaadt getdiv(n); 791df930be7Sderaadt } 792df930be7Sderaadt 793df930be7Sderaadt /* 794df930be7Sderaadt * dosub - select substring 795df930be7Sderaadt */ 796bb34cd6cSespie static void 797df930be7Sderaadt dosub(argv, argc) 798bb34cd6cSespie const char *argv[]; 79953f6f6bfSespie int argc; 800df930be7Sderaadt { 801bb34cd6cSespie const char *ap, *fc, *k; 80253f6f6bfSespie int nc; 803df930be7Sderaadt 804df930be7Sderaadt if (argc < 5) 805df930be7Sderaadt nc = MAXTOK; 806df930be7Sderaadt else 807df930be7Sderaadt #ifdef EXPR 808df930be7Sderaadt nc = expr(argv[4]); 809df930be7Sderaadt #else 810df930be7Sderaadt nc = atoi(argv[4]); 811df930be7Sderaadt #endif 812df930be7Sderaadt ap = argv[2]; /* target string */ 813df930be7Sderaadt #ifdef EXPR 814df930be7Sderaadt fc = ap + expr(argv[3]); /* first char */ 815df930be7Sderaadt #else 816df930be7Sderaadt fc = ap + atoi(argv[3]); /* first char */ 817df930be7Sderaadt #endif 818df930be7Sderaadt if (fc >= ap && fc < ap + strlen(ap)) 819df930be7Sderaadt for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--) 820df930be7Sderaadt putback(*k); 821df930be7Sderaadt } 822df930be7Sderaadt 823df930be7Sderaadt /* 824df930be7Sderaadt * map: 825df930be7Sderaadt * map every character of s1 that is specified in from 826df930be7Sderaadt * into s3 and replace in s. (source s1 remains untouched) 827df930be7Sderaadt * 828df930be7Sderaadt * This is a standard implementation of map(s,from,to) function of ICON 829df930be7Sderaadt * language. Within mapvec, we replace every character of "from" with 830df930be7Sderaadt * the corresponding character in "to". If "to" is shorter than "from", 831df930be7Sderaadt * than the corresponding entries are null, which means that those 832df930be7Sderaadt * characters dissapear altogether. Furthermore, imagine 833df930be7Sderaadt * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 834df930be7Sderaadt * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 835df930be7Sderaadt * ultimately maps to `*'. In order to achieve this effect in an efficient 836df930be7Sderaadt * manner (i.e. without multiple passes over the destination string), we 837df930be7Sderaadt * loop over mapvec, starting with the initial source character. if the 838df930be7Sderaadt * character value (dch) in this location is different than the source 839df930be7Sderaadt * character (sch), sch becomes dch, once again to index into mapvec, until 840df930be7Sderaadt * the character value stabilizes (i.e. sch = dch, in other words 841df930be7Sderaadt * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 842df930be7Sderaadt * character, it will stabilize, since mapvec[0] == 0 at all times. At the 843df930be7Sderaadt * end, we restore mapvec* back to normal where mapvec[n] == n for 844df930be7Sderaadt * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 845df930be7Sderaadt * about 5 times faster than any algorithm that makes multiple passes over 846df930be7Sderaadt * destination string. 847df930be7Sderaadt */ 848bb34cd6cSespie static void 849df930be7Sderaadt map(dest, src, from, to) 85053f6f6bfSespie char *dest; 851bb34cd6cSespie const char *src; 852bb34cd6cSespie const char *from; 853bb34cd6cSespie const char *to; 854df930be7Sderaadt { 855bb34cd6cSespie const char *tmp; 856ee3599c7Sespie unsigned char sch, dch; 85787c5c065Sespie static char frombis[257]; 85887c5c065Sespie static char tobis[257]; 859ee3599c7Sespie static unsigned char mapvec[256] = { 860ee3599c7Sespie 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 861ee3599c7Sespie 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 862ee3599c7Sespie 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 863ee3599c7Sespie 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 864ee3599c7Sespie 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 865ee3599c7Sespie 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 866ee3599c7Sespie 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 867ee3599c7Sespie 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 868ee3599c7Sespie 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 869ee3599c7Sespie 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 870ee3599c7Sespie 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 871ee3599c7Sespie 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 872ee3599c7Sespie 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 873ee3599c7Sespie 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 874ee3599c7Sespie 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 875ee3599c7Sespie 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 876ee3599c7Sespie 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 877ee3599c7Sespie 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 878df930be7Sderaadt }; 879df930be7Sderaadt 880df930be7Sderaadt if (*src) { 88187c5c065Sespie if (mimic_gnu) { 88287c5c065Sespie /* 88387c5c065Sespie * expand character ranges on the fly 88487c5c065Sespie */ 88587c5c065Sespie from = handledash(frombis, frombis + 256, from); 88687c5c065Sespie to = handledash(tobis, tobis + 256, to); 88787c5c065Sespie } 888df930be7Sderaadt tmp = from; 889df930be7Sderaadt /* 890df930be7Sderaadt * create a mapping between "from" and 891df930be7Sderaadt * "to" 892df930be7Sderaadt */ 893df930be7Sderaadt while (*from) 894ee3599c7Sespie mapvec[(unsigned char)(*from++)] = (*to) ? 895ee3599c7Sespie (unsigned char)(*to++) : 0; 896df930be7Sderaadt 897df930be7Sderaadt while (*src) { 898ee3599c7Sespie sch = (unsigned char)(*src++); 899df930be7Sderaadt dch = mapvec[sch]; 900df930be7Sderaadt while (dch != sch) { 901df930be7Sderaadt sch = dch; 902df930be7Sderaadt dch = mapvec[sch]; 903df930be7Sderaadt } 904ee3599c7Sespie if ((*dest = (char)dch)) 905df930be7Sderaadt dest++; 906df930be7Sderaadt } 907df930be7Sderaadt /* 908df930be7Sderaadt * restore all the changed characters 909df930be7Sderaadt */ 910df930be7Sderaadt while (*tmp) { 911ee3599c7Sespie mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 912df930be7Sderaadt tmp++; 913df930be7Sderaadt } 914df930be7Sderaadt } 915ee3599c7Sespie *dest = '\0'; 916df930be7Sderaadt } 91787c5c065Sespie 91887c5c065Sespie 91987c5c065Sespie /* 92087c5c065Sespie * handledash: 92187c5c065Sespie * use buffer to copy the src string, expanding character ranges 92287c5c065Sespie * on the way. 92387c5c065Sespie */ 92487c5c065Sespie static const char * 92587c5c065Sespie handledash(buffer, end, src) 92687c5c065Sespie char *buffer; 92787c5c065Sespie char *end; 92887c5c065Sespie const char *src; 92987c5c065Sespie { 93087c5c065Sespie char *p; 93187c5c065Sespie 93287c5c065Sespie p = buffer; 93387c5c065Sespie while(*src) { 93487c5c065Sespie if (src[1] == '-' && src[2]) { 93587c5c065Sespie unsigned char i; 93687c5c065Sespie for (i = (unsigned char)src[0]; 93787c5c065Sespie i <= (unsigned char)src[2]; i++) { 93887c5c065Sespie *p++ = i; 93987c5c065Sespie if (p == end) { 94087c5c065Sespie *p = '\0'; 94187c5c065Sespie return buffer; 94287c5c065Sespie } 94387c5c065Sespie } 94487c5c065Sespie src += 3; 94587c5c065Sespie } else 94687c5c065Sespie *p++ = *src++; 94787c5c065Sespie if (p == end) 94887c5c065Sespie break; 94987c5c065Sespie } 95087c5c065Sespie *p = '\0'; 95187c5c065Sespie return buffer; 95287c5c065Sespie } 95387c5c065Sespie 954