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