break out special local mail processing (e.g., mapping to the
[unix-history] / usr / src / usr.bin / m4 / serv.c
CommitLineData
995d74d4 1/*
adb35f79
KB
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.
995d74d4
KB
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Ozan Yigit.
12 *
6ecf3d85 13 * %sccs.include.redist.c%
995d74d4
KB
14 */
15
16#ifndef lint
adb35f79 17static char sccsid[] = "@(#)serv.c 5.4 (Berkeley) %G%";
995d74d4
KB
18#endif /* not lint */
19
20/*
21 * serv.c
22 * Facility: m4 macro processor
23 * by: oz
24 */
25
50841d34
KB
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
995d74d4
KB
29#include "mdef.h"
30#include "extr.h"
50841d34 31#include "pathnames.h"
995d74d4
KB
32
33extern ndptr lookup();
34extern ndptr addent();
995d74d4
KB
35
36char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */
37
38/*
39 * expand - user-defined macro expansion
40 *
41 */
42expand(argv, argc)
43register char *argv[];
44register 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 */
101dodefine(name, defn)
102register char *name;
103register 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
50841d34 118 p->defn = strdup(defn);
995d74d4
KB
119 p->type = MACRTYPE;
120}
121
122/*
123 * dodefn - push back a quoted definition of
124 * the given name.
125 */
126
127dodefn(name)
128char *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 */
146dopushdef(name, defn)
147register char *name;
148register 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
50841d34 160 p->defn = strdup(defn);
995d74d4
KB
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 */
170dodump(argv, argc)
171register char *argv[];
172register 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 */
195doifelse(argv,argc)
196register char *argv[];
197register 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 */
217doincl(ifile)
218char *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 */
235dopaste(pfile)
236char *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 */
256dochq(argv, argc)
257register char *argv[];
258register 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 */
280dochc(argv, argc)
281register char *argv[];
282register 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 */
304dodiv(n)
305register 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 */
322doundiv(argv, argc)
323register char *argv[];
324register 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 */
347dosub (argv, argc)
348register char *argv[];
349register 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
400map(dest,src,from,to)
401register char *dest;
402register char *src;
403register char *from;
404register 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}