xref: /original-bsd/usr.bin/m4/serv.c (revision 333da485)
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