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