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 */
expand(argv,argc)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 */
dodefine(name,defn)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
dodefn(name)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 */
dopushdef(name,defn)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 */
dodump(argv,argc)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 */
doifelse(argv,argc)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 */
doincl(ifile)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 */
dopaste(pfile)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 */
dochq(argv,argc)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 */
dochc(argv,argc)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 */
dodiv(n)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 */
doundiv(argv,argc)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 */
dosub(argv,argc)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
map(dest,src,from,to)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