1 /* 2 * Copyright (c) 1989 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Ozan Yigit. 12 * 13 * %sccs.include.redist.c% 14 */ 15 16 #ifndef lint 17 static char sccsid[] = "@(#)serv.c 5.4 (Berkeley) 01/21/94"; 18 #endif /* not lint */ 19 20 /* 21 * serv.c 22 * Facility: m4 macro processor 23 * by: oz 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include "mdef.h" 30 #include "extr.h" 31 #include "pathnames.h" 32 33 extern ndptr lookup(); 34 extern ndptr addent(); 35 36 char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ 37 38 /* 39 * expand - user-defined macro expansion 40 * 41 */ 42 expand(argv, argc) 43 register char *argv[]; 44 register int argc; 45 { 46 register char *t; 47 register char *p; 48 register int n; 49 register int argno; 50 51 t = argv[0]; /* defn string as a whole */ 52 p = t; 53 while (*p) 54 p++; 55 p--; /* last character of defn */ 56 while (p > t) { 57 if (*(p-1) != ARGFLAG) 58 putback(*p); 59 else { 60 switch (*p) { 61 62 case '#': 63 pbnum(argc-2); 64 break; 65 case '0': 66 case '1': 67 case '2': 68 case '3': 69 case '4': 70 case '5': 71 case '6': 72 case '7': 73 case '8': 74 case '9': 75 if ((argno = *p - '0') < argc-1) 76 pbstr(argv[argno+1]); 77 break; 78 case '*': 79 for (n = argc - 1; n > 2; n--) { 80 pbstr(argv[n]); 81 putback(','); 82 } 83 pbstr(argv[2]); 84 break; 85 default : 86 putback(*p); 87 break; 88 } 89 p--; 90 } 91 p--; 92 } 93 if (p == t) /* do last character */ 94 putback(*p); 95 } 96 97 /* 98 * dodefine - install definition in the table 99 * 100 */ 101 dodefine(name, defn) 102 register char *name; 103 register char *defn; 104 { 105 register ndptr p; 106 107 if (!*name) 108 error("m4: null definition."); 109 if (strcmp(name, defn) == 0) 110 error("m4: recursive definition."); 111 if ((p = lookup(name)) == nil) 112 p = addent(name); 113 else if (p->defn != null) 114 free(p->defn); 115 if (!*defn) 116 p->defn = null; 117 else 118 p->defn = strdup(defn); 119 p->type = MACRTYPE; 120 } 121 122 /* 123 * dodefn - push back a quoted definition of 124 * the given name. 125 */ 126 127 dodefn(name) 128 char *name; 129 { 130 register ndptr p; 131 132 if ((p = lookup(name)) != nil && p->defn != null) { 133 putback(rquote); 134 pbstr(p->defn); 135 putback(lquote); 136 } 137 } 138 139 /* 140 * dopushdef - install a definition in the hash table 141 * without removing a previous definition. Since 142 * each new entry is entered in *front* of the 143 * hash bucket, it hides a previous definition from 144 * lookup. 145 */ 146 dopushdef(name, defn) 147 register char *name; 148 register char *defn; 149 { 150 register ndptr p; 151 152 if (!*name) 153 error("m4: null definition"); 154 if (strcmp(name, defn) == 0) 155 error("m4: recursive definition."); 156 p = addent(name); 157 if (!*defn) 158 p->defn = null; 159 else 160 p->defn = strdup(defn); 161 p->type = MACRTYPE; 162 } 163 164 /* 165 * dodumpdef - dump the specified definitions in the hash 166 * table to stderr. If nothing is specified, the entire 167 * hash table is dumped. 168 * 169 */ 170 dodump(argv, argc) 171 register char *argv[]; 172 register int argc; 173 { 174 register int n; 175 ndptr p; 176 177 if (argc > 2) { 178 for (n = 2; n < argc; n++) 179 if ((p = lookup(argv[n])) != nil) 180 fprintf(stderr, dumpfmt, p->name, 181 p->defn); 182 } 183 else { 184 for (n = 0; n < HASHSIZE; n++) 185 for (p = hashtab[n]; p != nil; p = p->nxtptr) 186 fprintf(stderr, dumpfmt, p->name, 187 p->defn); 188 } 189 } 190 191 /* 192 * doifelse - select one of two alternatives - loop. 193 * 194 */ 195 doifelse(argv,argc) 196 register char *argv[]; 197 register int argc; 198 { 199 cycle { 200 if (strcmp(argv[2], argv[3]) == 0) 201 pbstr(argv[4]); 202 else if (argc == 6) 203 pbstr(argv[5]); 204 else if (argc > 6) { 205 argv += 3; 206 argc -= 3; 207 continue; 208 } 209 break; 210 } 211 } 212 213 /* 214 * doinclude - include a given file. 215 * 216 */ 217 doincl(ifile) 218 char *ifile; 219 { 220 if (ilevel+1 == MAXINP) 221 error("m4: too many include files."); 222 if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) { 223 ilevel++; 224 return (1); 225 } 226 else 227 return (0); 228 } 229 230 #ifdef EXTENDED 231 /* 232 * dopaste - include a given file without any 233 * macro processing. 234 */ 235 dopaste(pfile) 236 char *pfile; 237 { 238 FILE *pf; 239 register int c; 240 241 if ((pf = fopen(pfile, "r")) != NULL) { 242 while((c = getc(pf)) != EOF) 243 putc(c, active); 244 (void) fclose(pf); 245 return(1); 246 } 247 else 248 return(0); 249 } 250 #endif 251 252 /* 253 * dochq - change quote characters 254 * 255 */ 256 dochq(argv, argc) 257 register char *argv[]; 258 register int argc; 259 { 260 if (argc > 2) { 261 if (*argv[2]) 262 lquote = *argv[2]; 263 if (argc > 3) { 264 if (*argv[3]) 265 rquote = *argv[3]; 266 } 267 else 268 rquote = lquote; 269 } 270 else { 271 lquote = LQUOTE; 272 rquote = RQUOTE; 273 } 274 } 275 276 /* 277 * dochc - change comment characters 278 * 279 */ 280 dochc(argv, argc) 281 register char *argv[]; 282 register int argc; 283 { 284 if (argc > 2) { 285 if (*argv[2]) 286 scommt = *argv[2]; 287 if (argc > 3) { 288 if (*argv[3]) 289 ecommt = *argv[3]; 290 } 291 else 292 ecommt = ECOMMT; 293 } 294 else { 295 scommt = SCOMMT; 296 ecommt = ECOMMT; 297 } 298 } 299 300 /* 301 * dodivert - divert the output to a temporary file 302 * 303 */ 304 dodiv(n) 305 register int n; 306 { 307 if (n < 0 || n >= MAXOUT) 308 n = 0; /* bitbucket */ 309 if (outfile[n] == NULL) { 310 m4temp[UNIQUE] = n + '0'; 311 if ((outfile[n] = fopen(m4temp, "w")) == NULL) 312 error("m4: cannot divert."); 313 } 314 oindex = n; 315 active = outfile[n]; 316 } 317 318 /* 319 * doundivert - undivert a specified output, or all 320 * other outputs, in numerical order. 321 */ 322 doundiv(argv, argc) 323 register char *argv[]; 324 register int argc; 325 { 326 register int ind; 327 register int n; 328 329 if (argc > 2) { 330 for (ind = 2; ind < argc; ind++) { 331 n = atoi(argv[ind]); 332 if (n > 0 && n < MAXOUT && outfile[n] != NULL) 333 getdiv(n); 334 335 } 336 } 337 else 338 for (n = 1; n < MAXOUT; n++) 339 if (outfile[n] != NULL) 340 getdiv(n); 341 } 342 343 /* 344 * dosub - select substring 345 * 346 */ 347 dosub (argv, argc) 348 register char *argv[]; 349 register int argc; 350 { 351 register char *ap, *fc, *k; 352 register int nc; 353 354 if (argc < 5) 355 nc = MAXTOK; 356 else 357 #ifdef EXPR 358 nc = expr(argv[4]); 359 #else 360 nc = atoi(argv[4]); 361 #endif 362 ap = argv[2]; /* target string */ 363 #ifdef EXPR 364 fc = ap + expr(argv[3]); /* first char */ 365 #else 366 fc = ap + atoi(argv[3]); /* first char */ 367 #endif 368 if (fc >= ap && fc < ap+strlen(ap)) 369 for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--) 370 putback(*k); 371 } 372 373 /* 374 * map: 375 * map every character of s1 that is specified in from 376 * into s3 and replace in s. (source s1 remains untouched) 377 * 378 * This is a standard implementation of map(s,from,to) function of ICON 379 * language. Within mapvec, we replace every character of "from" with 380 * the corresponding character in "to". If "to" is shorter than "from", 381 * than the corresponding entries are null, which means that those 382 * characters dissapear altogether. Furthermore, imagine 383 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 384 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 385 * ultimately maps to `*'. In order to achieve this effect in an efficient 386 * manner (i.e. without multiple passes over the destination string), we 387 * loop over mapvec, starting with the initial source character. if the 388 * character value (dch) in this location is different than the source 389 * character (sch), sch becomes dch, once again to index into mapvec, until 390 * the character value stabilizes (i.e. sch = dch, in other words 391 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 392 * character, it will stabilize, since mapvec[0] == 0 at all times. At the 393 * end, we restore mapvec* back to normal where mapvec[n] == n for 394 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 395 * about 5 times faster than any algorithm that makes multiple passes over 396 * destination string. 397 * 398 */ 399 400 map(dest,src,from,to) 401 register char *dest; 402 register char *src; 403 register char *from; 404 register char *to; 405 { 406 register char *tmp; 407 register char sch, dch; 408 static char mapvec[128] = { 409 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 410 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 411 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 412 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 413 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 414 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 415 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 416 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 417 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 418 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 419 120, 121, 122, 123, 124, 125, 126, 127 420 }; 421 422 if (*src) { 423 tmp = from; 424 /* 425 * create a mapping between "from" and "to" 426 */ 427 while (*from) 428 mapvec[*from++] = (*to) ? *to++ : (char) 0; 429 430 while (*src) { 431 sch = *src++; 432 dch = mapvec[sch]; 433 while (dch != sch) { 434 sch = dch; 435 dch = mapvec[sch]; 436 } 437 if (*dest = dch) 438 dest++; 439 } 440 /* 441 * restore all the changed characters 442 */ 443 while (*tmp) { 444 mapvec[*tmp] = *tmp; 445 tmp++; 446 } 447 } 448 *dest = (char) 0; 449 } 450