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