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