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