1*87c5c065Sespie /* $OpenBSD: eval.c,v 1.26 2000/03/18 01:06:55 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*87c5c065Sespie static char rcsid[] = "$OpenBSD: eval.c,v 1.26 2000/03/18 01:06:55 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 *)); 80*87c5c065Sespie 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 213df930be7Sderaadt case INCLTYPE: 214df930be7Sderaadt if (argc > 2) 215df930be7Sderaadt if (!doincl(argv[2])) 2160d3ffe1dSespie err(1, "%s at line %lu: include(%s)", 2170d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 218df930be7Sderaadt break; 219df930be7Sderaadt 220df930be7Sderaadt case SINCTYPE: 221df930be7Sderaadt if (argc > 2) 222df930be7Sderaadt (void) doincl(argv[2]); 223df930be7Sderaadt break; 224df930be7Sderaadt #ifdef EXTENDED 225df930be7Sderaadt case PASTTYPE: 226df930be7Sderaadt if (argc > 2) 227df930be7Sderaadt if (!dopaste(argv[2])) 2280d3ffe1dSespie err(1, "%s at line %lu: paste(%s)", 2290d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 230df930be7Sderaadt break; 231df930be7Sderaadt 232df930be7Sderaadt case SPASTYPE: 233df930be7Sderaadt if (argc > 2) 234df930be7Sderaadt (void) dopaste(argv[2]); 235df930be7Sderaadt break; 236df930be7Sderaadt #endif 237df930be7Sderaadt case CHNQTYPE: 238df930be7Sderaadt dochq(argv, argc); 239df930be7Sderaadt break; 240df930be7Sderaadt 241df930be7Sderaadt case CHNCTYPE: 242df930be7Sderaadt dochc(argv, argc); 243df930be7Sderaadt break; 244df930be7Sderaadt 245df930be7Sderaadt case SUBSTYPE: 246df930be7Sderaadt /* 247df930be7Sderaadt * dosub - select substring 248df930be7Sderaadt * 249df930be7Sderaadt */ 250df930be7Sderaadt if (argc > 3) 251df930be7Sderaadt dosub(argv, argc); 252df930be7Sderaadt break; 253df930be7Sderaadt 254df930be7Sderaadt case SHIFTYPE: 255df930be7Sderaadt /* 256df930be7Sderaadt * doshift - push back all arguments 257df930be7Sderaadt * except the first one (i.e. skip 258df930be7Sderaadt * argv[2]) 259df930be7Sderaadt */ 260df930be7Sderaadt if (argc > 3) { 261df930be7Sderaadt for (n = argc - 1; n > 3; n--) { 2623a73db8cSderaadt pbstr(rquote); 263df930be7Sderaadt pbstr(argv[n]); 2643a73db8cSderaadt pbstr(lquote); 265aa676ce1Smillert putback(COMMA); 266df930be7Sderaadt } 2673a73db8cSderaadt pbstr(rquote); 268df930be7Sderaadt pbstr(argv[3]); 2693a73db8cSderaadt pbstr(lquote); 270df930be7Sderaadt } 271df930be7Sderaadt break; 272df930be7Sderaadt 273df930be7Sderaadt case DIVRTYPE: 274df930be7Sderaadt if (argc > 2 && (n = atoi(argv[2])) != 0) 275df930be7Sderaadt dodiv(n); 276df930be7Sderaadt else { 277df930be7Sderaadt active = stdout; 278df930be7Sderaadt oindex = 0; 279df930be7Sderaadt } 280df930be7Sderaadt break; 281df930be7Sderaadt 282df930be7Sderaadt case UNDVTYPE: 283df930be7Sderaadt doundiv(argv, argc); 284df930be7Sderaadt break; 285df930be7Sderaadt 286df930be7Sderaadt case DIVNTYPE: 287df930be7Sderaadt /* 288df930be7Sderaadt * dodivnum - return the number of 289df930be7Sderaadt * current output diversion 290df930be7Sderaadt */ 291df930be7Sderaadt pbnum(oindex); 292df930be7Sderaadt break; 293df930be7Sderaadt 294df930be7Sderaadt case UNDFTYPE: 295df930be7Sderaadt /* 296df930be7Sderaadt * doundefine - undefine a previously 297df930be7Sderaadt * defined macro(s) or m4 keyword(s). 298df930be7Sderaadt */ 299df930be7Sderaadt if (argc > 2) 300df930be7Sderaadt for (n = 2; n < argc; n++) 301df930be7Sderaadt remhash(argv[n], ALL); 302df930be7Sderaadt break; 303df930be7Sderaadt 304df930be7Sderaadt case POPDTYPE: 305df930be7Sderaadt /* 306df930be7Sderaadt * dopopdef - remove the topmost 307df930be7Sderaadt * definitions of macro(s) or m4 308df930be7Sderaadt * keyword(s). 309df930be7Sderaadt */ 310df930be7Sderaadt if (argc > 2) 311df930be7Sderaadt for (n = 2; n < argc; n++) 312df930be7Sderaadt remhash(argv[n], TOP); 313df930be7Sderaadt break; 314df930be7Sderaadt 315df930be7Sderaadt case MKTMTYPE: 316df930be7Sderaadt /* 317df930be7Sderaadt * dotemp - create a temporary file 318df930be7Sderaadt */ 31901e71e69Sespie if (argc > 2) { 32001e71e69Sespie int fd; 321bb34cd6cSespie char *temp; 32201e71e69Sespie 323bb34cd6cSespie temp = xstrdup(argv[2]); 324bb34cd6cSespie 325bb34cd6cSespie fd = mkstemp(temp); 32601e71e69Sespie if (fd == -1) 3270d3ffe1dSespie err(1, 3280d3ffe1dSespie "%s at line %lu: couldn't make temp file %s", 3290d3ffe1dSespie CURRENT_NAME, CURRENT_LINE, argv[2]); 33001e71e69Sespie close(fd); 331bb34cd6cSespie pbstr(temp); 332bb34cd6cSespie free(temp); 33301e71e69Sespie } 334df930be7Sderaadt break; 335df930be7Sderaadt 336df930be7Sderaadt case TRNLTYPE: 337df930be7Sderaadt /* 338df930be7Sderaadt * dotranslit - replace all characters in 339df930be7Sderaadt * the source string that appears in the 340df930be7Sderaadt * "from" string with the corresponding 341df930be7Sderaadt * characters in the "to" string. 342df930be7Sderaadt */ 343df930be7Sderaadt if (argc > 3) { 3447d3e0b6bSderaadt char temp[STRSPMAX+1]; 345df930be7Sderaadt if (argc > 4) 346df930be7Sderaadt map(temp, argv[2], argv[3], argv[4]); 347df930be7Sderaadt else 348df930be7Sderaadt map(temp, argv[2], argv[3], null); 349df930be7Sderaadt pbstr(temp); 3507d3e0b6bSderaadt } else if (argc > 2) 351df930be7Sderaadt pbstr(argv[2]); 352df930be7Sderaadt break; 353df930be7Sderaadt 354df930be7Sderaadt case INDXTYPE: 355df930be7Sderaadt /* 356df930be7Sderaadt * doindex - find the index of the second 357df930be7Sderaadt * argument string in the first argument 358df930be7Sderaadt * string. -1 if not present. 359df930be7Sderaadt */ 360df930be7Sderaadt pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 361df930be7Sderaadt break; 362df930be7Sderaadt 363df930be7Sderaadt case ERRPTYPE: 364df930be7Sderaadt /* 365df930be7Sderaadt * doerrp - print the arguments to stderr 366df930be7Sderaadt * file 367df930be7Sderaadt */ 368df930be7Sderaadt if (argc > 2) { 369df930be7Sderaadt for (n = 2; n < argc; n++) 370df930be7Sderaadt fprintf(stderr, "%s ", argv[n]); 371df930be7Sderaadt fprintf(stderr, "\n"); 372df930be7Sderaadt } 373df930be7Sderaadt break; 374df930be7Sderaadt 375df930be7Sderaadt case DNLNTYPE: 376df930be7Sderaadt /* 377df930be7Sderaadt * dodnl - eat-up-to and including 378df930be7Sderaadt * newline 379df930be7Sderaadt */ 380df930be7Sderaadt while ((c = gpbc()) != '\n' && c != EOF) 381df930be7Sderaadt ; 382df930be7Sderaadt break; 383df930be7Sderaadt 384df930be7Sderaadt case M4WRTYPE: 385df930be7Sderaadt /* 386df930be7Sderaadt * dom4wrap - set up for 387df930be7Sderaadt * wrap-up/wind-down activity 388df930be7Sderaadt */ 389df930be7Sderaadt m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; 390df930be7Sderaadt break; 391df930be7Sderaadt 392df930be7Sderaadt case EXITTYPE: 393df930be7Sderaadt /* 394df930be7Sderaadt * doexit - immediate exit from m4. 395df930be7Sderaadt */ 396df930be7Sderaadt killdiv(); 397df930be7Sderaadt exit((argc > 2) ? atoi(argv[2]) : 0); 398df930be7Sderaadt break; 399df930be7Sderaadt 400df930be7Sderaadt case DEFNTYPE: 401df930be7Sderaadt if (argc > 2) 402df930be7Sderaadt for (n = 2; n < argc; n++) 403df930be7Sderaadt dodefn(argv[n]); 404df930be7Sderaadt break; 405df930be7Sderaadt 406b8161682Sespie case INDIRTYPE: /* Indirect call */ 407b8161682Sespie if (argc > 2) 408b8161682Sespie doindir(argv, argc); 409b8161682Sespie break; 410b8161682Sespie 411b8161682Sespie case BUILTINTYPE: /* Builtins only */ 412b8161682Sespie if (argc > 2) 413b8161682Sespie dobuiltin(argv, argc); 414b8161682Sespie break; 415b8161682Sespie 416b8161682Sespie case PATSTYPE: 417b8161682Sespie if (argc > 2) 418b8161682Sespie dopatsubst(argv, argc); 419b8161682Sespie break; 420b8161682Sespie case REGEXPTYPE: 421b8161682Sespie if (argc > 2) 422b8161682Sespie doregexp(argv, argc); 423b8161682Sespie break; 424b8161682Sespie case LINETYPE: 425b8161682Sespie doprintlineno(infile+ilevel); 426b8161682Sespie break; 427b8161682Sespie case FILENAMETYPE: 428b8161682Sespie doprintfilename(infile+ilevel); 429b8161682Sespie break; 430423624b7Sespie case SELFTYPE: 431423624b7Sespie pbstr(rquote); 432423624b7Sespie pbstr(argv[1]); 433423624b7Sespie pbstr(lquote); 434423624b7Sespie break; 435df930be7Sderaadt default: 4360d3ffe1dSespie errx(1, "%s at line %lu: eval: major botch.", 4370d3ffe1dSespie CURRENT_NAME, CURRENT_LINE); 438df930be7Sderaadt break; 439df930be7Sderaadt } 440df930be7Sderaadt } 441df930be7Sderaadt 442df930be7Sderaadt char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ 443df930be7Sderaadt 444df930be7Sderaadt /* 445df930be7Sderaadt * expand - user-defined macro expansion 446df930be7Sderaadt */ 447df930be7Sderaadt void 448df930be7Sderaadt expand(argv, argc) 449bb34cd6cSespie const char *argv[]; 45053f6f6bfSespie int argc; 451df930be7Sderaadt { 452bb34cd6cSespie const char *t; 453bb34cd6cSespie const char *p; 45453f6f6bfSespie int n; 45553f6f6bfSespie int argno; 456df930be7Sderaadt 457df930be7Sderaadt t = argv[0]; /* defn string as a whole */ 458df930be7Sderaadt p = t; 459df930be7Sderaadt while (*p) 460df930be7Sderaadt p++; 461df930be7Sderaadt p--; /* last character of defn */ 462df930be7Sderaadt while (p > t) { 463df930be7Sderaadt if (*(p - 1) != ARGFLAG) 464df930be7Sderaadt putback(*p); 465df930be7Sderaadt else { 466df930be7Sderaadt switch (*p) { 467df930be7Sderaadt 468df930be7Sderaadt case '#': 469df930be7Sderaadt pbnum(argc - 2); 470df930be7Sderaadt break; 471df930be7Sderaadt case '0': 472df930be7Sderaadt case '1': 473df930be7Sderaadt case '2': 474df930be7Sderaadt case '3': 475df930be7Sderaadt case '4': 476df930be7Sderaadt case '5': 477df930be7Sderaadt case '6': 478df930be7Sderaadt case '7': 479df930be7Sderaadt case '8': 480df930be7Sderaadt case '9': 481df930be7Sderaadt if ((argno = *p - '0') < argc - 1) 482df930be7Sderaadt pbstr(argv[argno + 1]); 483df930be7Sderaadt break; 484df930be7Sderaadt case '*': 485df930be7Sderaadt for (n = argc - 1; n > 2; n--) { 486df930be7Sderaadt pbstr(argv[n]); 487aa676ce1Smillert putback(COMMA); 488df930be7Sderaadt } 489df930be7Sderaadt pbstr(argv[2]); 490df930be7Sderaadt break; 491aa676ce1Smillert case '@': 492aa676ce1Smillert for (n = argc - 1; n > 2; n--) { 493aa676ce1Smillert pbstr(rquote); 494aa676ce1Smillert pbstr(argv[n]); 495aa676ce1Smillert pbstr(lquote); 496aa676ce1Smillert putback(COMMA); 497aa676ce1Smillert } 498aa676ce1Smillert pbstr(rquote); 499aa676ce1Smillert pbstr(argv[2]); 500aa676ce1Smillert pbstr(lquote); 501aa676ce1Smillert break; 502df930be7Sderaadt default: 503df930be7Sderaadt putback(*p); 504df930be7Sderaadt putback('$'); 505df930be7Sderaadt break; 506df930be7Sderaadt } 507df930be7Sderaadt p--; 508df930be7Sderaadt } 509df930be7Sderaadt p--; 510df930be7Sderaadt } 511df930be7Sderaadt if (p == t) /* do last character */ 512df930be7Sderaadt putback(*p); 513df930be7Sderaadt } 514df930be7Sderaadt 515df930be7Sderaadt /* 516df930be7Sderaadt * dodefine - install definition in the table 517df930be7Sderaadt */ 518df930be7Sderaadt void 519df930be7Sderaadt dodefine(name, defn) 520bb34cd6cSespie const char *name; 521bb34cd6cSespie const char *defn; 522df930be7Sderaadt { 52353f6f6bfSespie ndptr p; 524df930be7Sderaadt 525df930be7Sderaadt if (!*name) 5260d3ffe1dSespie errx(1, "%s at line %lu: null definition.", CURRENT_NAME, 5270d3ffe1dSespie CURRENT_LINE); 528df930be7Sderaadt if ((p = lookup(name)) == nil) 529df930be7Sderaadt p = addent(name); 530df930be7Sderaadt else if (p->defn != null) 531df930be7Sderaadt free((char *) p->defn); 532df930be7Sderaadt if (!*defn) 533df930be7Sderaadt p->defn = null; 534df930be7Sderaadt else 535df930be7Sderaadt p->defn = xstrdup(defn); 536df930be7Sderaadt p->type = MACRTYPE; 537718b194dSespie if (STREQ(name, defn)) 538718b194dSespie p->type |= RECDEF; 539df930be7Sderaadt } 540df930be7Sderaadt 541df930be7Sderaadt /* 542df930be7Sderaadt * dodefn - push back a quoted definition of 543df930be7Sderaadt * the given name. 544df930be7Sderaadt */ 545bb34cd6cSespie static void 546df930be7Sderaadt dodefn(name) 547bb34cd6cSespie const char *name; 548df930be7Sderaadt { 54953f6f6bfSespie ndptr p; 550df930be7Sderaadt 551df930be7Sderaadt if ((p = lookup(name)) != nil && p->defn != null) { 5523a73db8cSderaadt pbstr(rquote); 553df930be7Sderaadt pbstr(p->defn); 5543a73db8cSderaadt pbstr(lquote); 555df930be7Sderaadt } 556df930be7Sderaadt } 557df930be7Sderaadt 558df930be7Sderaadt /* 559df930be7Sderaadt * dopushdef - install a definition in the hash table 560df930be7Sderaadt * without removing a previous definition. Since 561df930be7Sderaadt * each new entry is entered in *front* of the 562df930be7Sderaadt * hash bucket, it hides a previous definition from 563df930be7Sderaadt * lookup. 564df930be7Sderaadt */ 565bb34cd6cSespie static void 566df930be7Sderaadt dopushdef(name, defn) 567bb34cd6cSespie const char *name; 568bb34cd6cSespie const char *defn; 569df930be7Sderaadt { 57053f6f6bfSespie ndptr p; 571df930be7Sderaadt 572df930be7Sderaadt if (!*name) 5730d3ffe1dSespie errx(1, "%s at line %lu: null definition", CURRENT_NAME, 5740d3ffe1dSespie CURRENT_LINE); 575df930be7Sderaadt p = addent(name); 576df930be7Sderaadt if (!*defn) 577df930be7Sderaadt p->defn = null; 578df930be7Sderaadt else 579df930be7Sderaadt p->defn = xstrdup(defn); 580df930be7Sderaadt p->type = MACRTYPE; 581718b194dSespie if (STREQ(name, defn)) 582718b194dSespie p->type |= RECDEF; 583df930be7Sderaadt } 584df930be7Sderaadt 585df930be7Sderaadt /* 586df930be7Sderaadt * dodumpdef - dump the specified definitions in the hash 587df930be7Sderaadt * table to stderr. If nothing is specified, the entire 588df930be7Sderaadt * hash table is dumped. 589df930be7Sderaadt */ 590bb34cd6cSespie static void 591df930be7Sderaadt dodump(argv, argc) 592bb34cd6cSespie const char *argv[]; 59353f6f6bfSespie int argc; 594df930be7Sderaadt { 59553f6f6bfSespie int n; 596df930be7Sderaadt ndptr p; 597df930be7Sderaadt 598df930be7Sderaadt if (argc > 2) { 599df930be7Sderaadt for (n = 2; n < argc; n++) 600df930be7Sderaadt if ((p = lookup(argv[n])) != nil) 601df930be7Sderaadt fprintf(stderr, dumpfmt, p->name, 602df930be7Sderaadt p->defn); 6037d3e0b6bSderaadt } else { 604df930be7Sderaadt for (n = 0; n < HASHSIZE; n++) 605df930be7Sderaadt for (p = hashtab[n]; p != nil; p = p->nxtptr) 606df930be7Sderaadt fprintf(stderr, dumpfmt, p->name, 607df930be7Sderaadt p->defn); 608df930be7Sderaadt } 609df930be7Sderaadt } 610df930be7Sderaadt 611df930be7Sderaadt /* 612df930be7Sderaadt * doifelse - select one of two alternatives - loop. 613df930be7Sderaadt */ 614bb34cd6cSespie static void 615df930be7Sderaadt doifelse(argv, argc) 616bb34cd6cSespie const char *argv[]; 61753f6f6bfSespie int argc; 618df930be7Sderaadt { 619df930be7Sderaadt cycle { 620df930be7Sderaadt if (STREQ(argv[2], argv[3])) 621df930be7Sderaadt pbstr(argv[4]); 622df930be7Sderaadt else if (argc == 6) 623df930be7Sderaadt pbstr(argv[5]); 624df930be7Sderaadt else if (argc > 6) { 625df930be7Sderaadt argv += 3; 626df930be7Sderaadt argc -= 3; 627df930be7Sderaadt continue; 628df930be7Sderaadt } 629df930be7Sderaadt break; 630df930be7Sderaadt } 631df930be7Sderaadt } 632df930be7Sderaadt 633df930be7Sderaadt /* 634df930be7Sderaadt * doinclude - include a given file. 635df930be7Sderaadt */ 636bb34cd6cSespie static int 637df930be7Sderaadt doincl(ifile) 638bb34cd6cSespie const char *ifile; 639df930be7Sderaadt { 640df930be7Sderaadt if (ilevel + 1 == MAXINP) 6410d3ffe1dSespie errx(1, "%s at line %lu: too many include files.", 6420d3ffe1dSespie CURRENT_NAME, CURRENT_LINE); 6430d3ffe1dSespie if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 644df930be7Sderaadt ilevel++; 645df930be7Sderaadt bbase[ilevel] = bufbase = bp; 646df930be7Sderaadt return (1); 6477d3e0b6bSderaadt } else 648df930be7Sderaadt return (0); 649df930be7Sderaadt } 650df930be7Sderaadt 651df930be7Sderaadt #ifdef EXTENDED 652df930be7Sderaadt /* 653df930be7Sderaadt * dopaste - include a given file without any 654df930be7Sderaadt * macro processing. 655df930be7Sderaadt */ 656bb34cd6cSespie static int 657df930be7Sderaadt dopaste(pfile) 658bb34cd6cSespie const char *pfile; 659df930be7Sderaadt { 660df930be7Sderaadt FILE *pf; 66153f6f6bfSespie int c; 662df930be7Sderaadt 663df930be7Sderaadt if ((pf = fopen(pfile, "r")) != NULL) { 664df930be7Sderaadt while ((c = getc(pf)) != EOF) 665df930be7Sderaadt putc(c, active); 666df930be7Sderaadt (void) fclose(pf); 667df930be7Sderaadt return (1); 6687d3e0b6bSderaadt } else 669df930be7Sderaadt return (0); 670df930be7Sderaadt } 671df930be7Sderaadt #endif 672df930be7Sderaadt 673df930be7Sderaadt /* 674df930be7Sderaadt * dochq - change quote characters 675df930be7Sderaadt */ 676bb34cd6cSespie static void 677df930be7Sderaadt dochq(argv, argc) 678bb34cd6cSespie const char *argv[]; 67953f6f6bfSespie int argc; 680df930be7Sderaadt { 681*87c5c065Sespie /* In gnu-m4 mode, having two empty arguments means no quotes at 682*87c5c065Sespie * all. */ 683*87c5c065Sespie if (mimic_gnu) { 684*87c5c065Sespie if (argc > 3 && !*argv[2] && !*argv[3]) { 685*87c5c065Sespie lquote[0] = EOS; 686*87c5c065Sespie rquote[0] = EOS; 687*87c5c065Sespie return; 688*87c5c065Sespie } 689*87c5c065Sespie } 690df930be7Sderaadt if (argc > 2) { 69118a1973bSderaadt if (*argv[2]) 692b81b15b2Sespie strlcpy(lquote, argv[2], sizeof(lquote)); 69318a1973bSderaadt else { 69418a1973bSderaadt lquote[0] = LQUOTE; 695f0484631Sespie lquote[1] = EOS; 69618a1973bSderaadt } 697df930be7Sderaadt if (argc > 3) { 698df930be7Sderaadt if (*argv[3]) 699b81b15b2Sespie strlcpy(rquote, argv[3], sizeof(rquote)); 7007d3e0b6bSderaadt } else 70129a0bfdcSderaadt strcpy(rquote, lquote); 7027d3e0b6bSderaadt } else { 703f0484631Sespie lquote[0] = LQUOTE, lquote[1] = EOS; 704f0484631Sespie rquote[0] = RQUOTE, rquote[1] = EOS; 705df930be7Sderaadt } 706df930be7Sderaadt } 707df930be7Sderaadt 708df930be7Sderaadt /* 709df930be7Sderaadt * dochc - change comment characters 710df930be7Sderaadt */ 711bb34cd6cSespie static void 712df930be7Sderaadt dochc(argv, argc) 713bb34cd6cSespie const char *argv[]; 71453f6f6bfSespie int argc; 715df930be7Sderaadt { 716df930be7Sderaadt if (argc > 2) { 717df930be7Sderaadt if (*argv[2]) 718b81b15b2Sespie strlcpy(scommt, argv[2], sizeof(scommt)); 719df930be7Sderaadt if (argc > 3) { 720df930be7Sderaadt if (*argv[3]) 721b81b15b2Sespie strlcpy(ecommt, argv[3], sizeof(ecommt)); 722df930be7Sderaadt } 723df930be7Sderaadt else 724f0484631Sespie ecommt[0] = ECOMMT, ecommt[1] = EOS; 725df930be7Sderaadt } 726df930be7Sderaadt else { 727f0484631Sespie scommt[0] = SCOMMT, scommt[1] = EOS; 728f0484631Sespie ecommt[0] = ECOMMT, ecommt[1] = EOS; 729df930be7Sderaadt } 730df930be7Sderaadt } 731df930be7Sderaadt 732df930be7Sderaadt /* 733df930be7Sderaadt * dodivert - divert the output to a temporary file 734df930be7Sderaadt */ 735bb34cd6cSespie static void 736df930be7Sderaadt dodiv(n) 73753f6f6bfSespie int n; 738df930be7Sderaadt { 739445b77f7Smillert int fd; 740445b77f7Smillert 7417d3e0b6bSderaadt oindex = n; 742df930be7Sderaadt if (n < 0 || n >= MAXOUT) 743df930be7Sderaadt n = 0; /* bitbucket */ 744df930be7Sderaadt if (outfile[n] == NULL) { 7453f42598dSespie char fname[] = _PATH_DIVNAME; 7463f42598dSespie 7473f42598dSespie if ((fd = mkstemp(fname)) < 0 || 7483f42598dSespie (outfile[n] = fdopen(fd, "w+")) == NULL) 7493f42598dSespie err(1, "%s: cannot divert", fname); 7503f42598dSespie if (unlink(fname) == -1) 7513f42598dSespie err(1, "%s: cannot unlink", fname); 752df930be7Sderaadt } 753df930be7Sderaadt active = outfile[n]; 754df930be7Sderaadt } 755df930be7Sderaadt 756df930be7Sderaadt /* 757df930be7Sderaadt * doundivert - undivert a specified output, or all 758df930be7Sderaadt * other outputs, in numerical order. 759df930be7Sderaadt */ 760bb34cd6cSespie static void 761df930be7Sderaadt doundiv(argv, argc) 762bb34cd6cSespie const char *argv[]; 76353f6f6bfSespie int argc; 764df930be7Sderaadt { 76553f6f6bfSespie int ind; 76653f6f6bfSespie int n; 767df930be7Sderaadt 768df930be7Sderaadt if (argc > 2) { 769df930be7Sderaadt for (ind = 2; ind < argc; ind++) { 770df930be7Sderaadt n = atoi(argv[ind]); 771df930be7Sderaadt if (n > 0 && n < MAXOUT && outfile[n] != NULL) 772df930be7Sderaadt getdiv(n); 773df930be7Sderaadt 774df930be7Sderaadt } 775df930be7Sderaadt } 776df930be7Sderaadt else 777df930be7Sderaadt for (n = 1; n < MAXOUT; n++) 778df930be7Sderaadt if (outfile[n] != NULL) 779df930be7Sderaadt getdiv(n); 780df930be7Sderaadt } 781df930be7Sderaadt 782df930be7Sderaadt /* 783df930be7Sderaadt * dosub - select substring 784df930be7Sderaadt */ 785bb34cd6cSespie static void 786df930be7Sderaadt dosub(argv, argc) 787bb34cd6cSespie const char *argv[]; 78853f6f6bfSespie int argc; 789df930be7Sderaadt { 790bb34cd6cSespie const char *ap, *fc, *k; 79153f6f6bfSespie int nc; 792df930be7Sderaadt 793df930be7Sderaadt if (argc < 5) 794df930be7Sderaadt nc = MAXTOK; 795df930be7Sderaadt else 796df930be7Sderaadt #ifdef EXPR 797df930be7Sderaadt nc = expr(argv[4]); 798df930be7Sderaadt #else 799df930be7Sderaadt nc = atoi(argv[4]); 800df930be7Sderaadt #endif 801df930be7Sderaadt ap = argv[2]; /* target string */ 802df930be7Sderaadt #ifdef EXPR 803df930be7Sderaadt fc = ap + expr(argv[3]); /* first char */ 804df930be7Sderaadt #else 805df930be7Sderaadt fc = ap + atoi(argv[3]); /* first char */ 806df930be7Sderaadt #endif 807df930be7Sderaadt if (fc >= ap && fc < ap + strlen(ap)) 808df930be7Sderaadt for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--) 809df930be7Sderaadt putback(*k); 810df930be7Sderaadt } 811df930be7Sderaadt 812df930be7Sderaadt /* 813df930be7Sderaadt * map: 814df930be7Sderaadt * map every character of s1 that is specified in from 815df930be7Sderaadt * into s3 and replace in s. (source s1 remains untouched) 816df930be7Sderaadt * 817df930be7Sderaadt * This is a standard implementation of map(s,from,to) function of ICON 818df930be7Sderaadt * language. Within mapvec, we replace every character of "from" with 819df930be7Sderaadt * the corresponding character in "to". If "to" is shorter than "from", 820df930be7Sderaadt * than the corresponding entries are null, which means that those 821df930be7Sderaadt * characters dissapear altogether. Furthermore, imagine 822df930be7Sderaadt * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 823df930be7Sderaadt * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 824df930be7Sderaadt * ultimately maps to `*'. In order to achieve this effect in an efficient 825df930be7Sderaadt * manner (i.e. without multiple passes over the destination string), we 826df930be7Sderaadt * loop over mapvec, starting with the initial source character. if the 827df930be7Sderaadt * character value (dch) in this location is different than the source 828df930be7Sderaadt * character (sch), sch becomes dch, once again to index into mapvec, until 829df930be7Sderaadt * the character value stabilizes (i.e. sch = dch, in other words 830df930be7Sderaadt * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 831df930be7Sderaadt * character, it will stabilize, since mapvec[0] == 0 at all times. At the 832df930be7Sderaadt * end, we restore mapvec* back to normal where mapvec[n] == n for 833df930be7Sderaadt * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 834df930be7Sderaadt * about 5 times faster than any algorithm that makes multiple passes over 835df930be7Sderaadt * destination string. 836df930be7Sderaadt */ 837bb34cd6cSespie static void 838df930be7Sderaadt map(dest, src, from, to) 83953f6f6bfSespie char *dest; 840bb34cd6cSespie const char *src; 841bb34cd6cSespie const char *from; 842bb34cd6cSespie const char *to; 843df930be7Sderaadt { 844bb34cd6cSespie const char *tmp; 845ee3599c7Sespie unsigned char sch, dch; 846*87c5c065Sespie static char frombis[257]; 847*87c5c065Sespie static char tobis[257]; 848ee3599c7Sespie static unsigned char mapvec[256] = { 849ee3599c7Sespie 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 850ee3599c7Sespie 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 851ee3599c7Sespie 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 852ee3599c7Sespie 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 853ee3599c7Sespie 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 854ee3599c7Sespie 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 855ee3599c7Sespie 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 856ee3599c7Sespie 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 857ee3599c7Sespie 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 858ee3599c7Sespie 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 859ee3599c7Sespie 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 860ee3599c7Sespie 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 861ee3599c7Sespie 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 862ee3599c7Sespie 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 863ee3599c7Sespie 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 864ee3599c7Sespie 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 865ee3599c7Sespie 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 866ee3599c7Sespie 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 867df930be7Sderaadt }; 868df930be7Sderaadt 869df930be7Sderaadt if (*src) { 870*87c5c065Sespie if (mimic_gnu) { 871*87c5c065Sespie /* 872*87c5c065Sespie * expand character ranges on the fly 873*87c5c065Sespie */ 874*87c5c065Sespie from = handledash(frombis, frombis + 256, from); 875*87c5c065Sespie to = handledash(tobis, tobis + 256, to); 876*87c5c065Sespie } 877df930be7Sderaadt tmp = from; 878df930be7Sderaadt /* 879df930be7Sderaadt * create a mapping between "from" and 880df930be7Sderaadt * "to" 881df930be7Sderaadt */ 882df930be7Sderaadt while (*from) 883ee3599c7Sespie mapvec[(unsigned char)(*from++)] = (*to) ? 884ee3599c7Sespie (unsigned char)(*to++) : 0; 885df930be7Sderaadt 886df930be7Sderaadt while (*src) { 887ee3599c7Sespie sch = (unsigned char)(*src++); 888df930be7Sderaadt dch = mapvec[sch]; 889df930be7Sderaadt while (dch != sch) { 890df930be7Sderaadt sch = dch; 891df930be7Sderaadt dch = mapvec[sch]; 892df930be7Sderaadt } 893ee3599c7Sespie if ((*dest = (char)dch)) 894df930be7Sderaadt dest++; 895df930be7Sderaadt } 896df930be7Sderaadt /* 897df930be7Sderaadt * restore all the changed characters 898df930be7Sderaadt */ 899df930be7Sderaadt while (*tmp) { 900ee3599c7Sespie mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 901df930be7Sderaadt tmp++; 902df930be7Sderaadt } 903df930be7Sderaadt } 904ee3599c7Sespie *dest = '\0'; 905df930be7Sderaadt } 906*87c5c065Sespie 907*87c5c065Sespie 908*87c5c065Sespie /* 909*87c5c065Sespie * handledash: 910*87c5c065Sespie * use buffer to copy the src string, expanding character ranges 911*87c5c065Sespie * on the way. 912*87c5c065Sespie */ 913*87c5c065Sespie static const char * 914*87c5c065Sespie handledash(buffer, end, src) 915*87c5c065Sespie char *buffer; 916*87c5c065Sespie char *end; 917*87c5c065Sespie const char *src; 918*87c5c065Sespie { 919*87c5c065Sespie char *p; 920*87c5c065Sespie 921*87c5c065Sespie p = buffer; 922*87c5c065Sespie while(*src) { 923*87c5c065Sespie if (src[1] == '-' && src[2]) { 924*87c5c065Sespie unsigned char i; 925*87c5c065Sespie for (i = (unsigned char)src[0]; 926*87c5c065Sespie i <= (unsigned char)src[2]; i++) { 927*87c5c065Sespie *p++ = i; 928*87c5c065Sespie if (p == end) { 929*87c5c065Sespie *p = '\0'; 930*87c5c065Sespie return buffer; 931*87c5c065Sespie } 932*87c5c065Sespie } 933*87c5c065Sespie src += 3; 934*87c5c065Sespie } else 935*87c5c065Sespie *p++ = *src++; 936*87c5c065Sespie if (p == end) 937*87c5c065Sespie break; 938*87c5c065Sespie } 939*87c5c065Sespie *p = '\0'; 940*87c5c065Sespie return buffer; 941*87c5c065Sespie } 942*87c5c065Sespie 943