xref: /plan9/sys/src/cmd/spin/pc_zpp.c (revision 00d970127b9d44d2b22f4f656717a212dec1f1d2)
1 /***** spin: pc_zpp.c *****/
2 
3 /* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories.     */
4 /* All Rights Reserved.  This software is for educational purposes only.  */
5 /* No guarantee whatsoever is expressed or implied by the distribution of */
6 /* this code.  Permission is given to distribute this code provided that  */
7 /* this introductory message is not removed and no monies are exchanged.  */
8 /* Software written by Gerard J. Holzmann.  For tool documentation see:   */
9 /*             http://spinroot.com/                                       */
10 /* Send all bug-reports and/or questions to: bugs@spinroot.com            */
11 
12 /* pc_zpp.c is only used in the PC version of Spin                        */
13 /* it is included to avoid too great a reliance on an external cpp        */
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include "spin.h"
20 
21 #ifdef PC
22 enum cstate { PLAIN, IN_STRING, IN_QUOTE, S_COMM, COMMENT, E_COMM };
23 
24 #define MAXNEST	32
25 #define MAXDEF	128
26 #define MAXLINE	2048
27 #define GENEROUS 8192
28 
29 #define debug(x,y)	if (verbose) printf(x,y)
30 
31 static FILE *outpp /* = stdout */;
32 
33 static int if_truth[MAXNEST];
34 static int printing[MAXNEST];
35 static int if_depth, nr_defs, verbose = 0;
36 static enum cstate state = PLAIN;
37 static char Out1[GENEROUS], Out2[GENEROUS];
38 
39 static struct Defines {
40 	int exists;
41 	char *src, *trg;
42 } d[MAXDEF];
43 
44 static int process(char *, int, char *);
45 static int zpp_do(char *);
46 
47 extern char *emalloc(size_t);	/* main.c */
48 
49 static int
do_define(char * p)50 do_define(char *p)
51 {	char *q, *r, *s;
52 
53 	for (q = p+strlen(p)-1; q > p; q--)
54 		if (*q == '\n' || *q == '\t' || *q == ' ')
55 			*q = '\0';
56 		else
57 			break;
58 
59 	q = p + strspn(p, " \t");
60 	if (!(r = strchr(q, '\t')))
61 		r = strchr(q, ' ');
62 	if (!r) { s = ""; goto adddef; }
63 	s = r + strspn(r, " \t");
64 	*r = '\0';
65 	if (strchr(q, '('))
66 	{	debug("zpp: #define with arguments %s\n", q);
67 		return 0;
68 	}
69 	for (r = q+strlen(q)-1; r > q; r--)
70 		if (*r == ' ' || *r == '\t')
71 			*r = '\0';
72 		else
73 			break;
74 	if (nr_defs >= MAXDEF)
75 	{	debug("zpp: too many #defines (max %d)\n", nr_defs);
76 		return 0;
77 	}
78 	if (strcmp(q, s) != 0)
79 	{	int j;
80 adddef:		for (j = 0; j < nr_defs; j++)
81 			if (!strcmp(d[j].src, q))
82 				d[j].exists = 0;
83 		d[nr_defs].src = emalloc(strlen(q)+1);
84 		d[nr_defs].trg = emalloc(strlen(s)+1);
85 		strcpy(d[nr_defs].src, q);
86 		strcpy(d[nr_defs].trg, s);
87 		d[nr_defs++].exists = 1;
88 	}
89 	return 1;
90 }
91 
92 static int
isvalid(int c)93 isvalid(int c)
94 {
95 	return (isalnum(c) || c == '_');
96 }
97 
98 static char *
apply(char * p0)99 apply(char *p0)
100 {	char *out, *in1, *in2, *startat;
101 	int i, j;
102 
103 	startat = in1 = Out2; strcpy(Out2, p0);
104 	out = Out1; *out = '\0';
105 
106 	for (i = nr_defs-1; i >= 0; i--)
107 	{	if (!d[i].exists) continue;
108 		j = (int) strlen(d[i].src);
109 more:		in2 = strstr(startat, d[i].src);
110 		if (!in2)	/* no more matches */
111 		{	startat = in1;
112 			continue;
113 		}
114 		if ((in2 == in1 || !isvalid(*(in2-1)))
115 		&&  (in2+j == '\0' || !isvalid(*(in2+j))))
116 		{	*in2 = '\0';
117 
118 			if (strlen(in1)+strlen(d[i].trg)+strlen(in2+j) >= GENEROUS)
119 			{
120 				printf("spin: macro expansion overflow %s -> %s ?\n",
121 					d[i].src, d[i].trg);
122 				return in1;
123 			}
124 			strcat(out, in1);
125 			strcat(out, d[i].trg);
126 			strcat(out, in2+j);
127 			if (in1 == Out2)
128 			{	startat = in1 = Out1;
129 				out = Out2;
130 			} else
131 			{	startat = in1 = Out2;
132 				out = Out1;
133 			}
134 			*out = '\0';
135 		} else
136 		{	startat = in2+1;	/* +1 not +j.. */
137 		}
138 		goto more; /* recursive defines */
139 	}
140 	return in1;
141 }
142 
143 static char *
do_common(char * p)144 do_common(char *p)
145 {	char *q, *s;
146 
147 	q = p + strspn(p, " \t");
148 	for (s = (q + strlen(q) - 1); s > q; s--)
149 		if (*s == ' ' || *s == '\t' || *s == '\n')
150 			*s = '\0';
151 		else
152 			break;
153 	return q;
154 }
155 
156 static int
do_undefine(char * p)157 do_undefine(char *p)
158 {	int i; char *q = do_common(p);
159 
160 	for (i = 0; i < nr_defs; i++)
161 		if (!strcmp(d[i].src, q))
162 			d[i].exists = 0;
163 	return 1;
164 }
165 
166 static char *
check_ifdef(char * p)167 check_ifdef(char *p)
168 {	int i; char *q = do_common(p);
169 
170 	for (i = 0; i < nr_defs; i++)
171 		if (d[i].exists
172 		&&  !strcmp(d[i].src, q))
173 			return d[i].trg;
174 	return (char *) 0;
175 }
176 
177 static int
do_ifdef(char * p)178 do_ifdef(char *p)
179 {
180 	if (++if_depth >= MAXNEST)
181 	{	debug("zpp: too deeply nested (max %d)\n", MAXNEST);
182 		return 0;
183 	}
184 	if_truth[if_depth] = (check_ifdef(p) != (char *)0);
185 	printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
186 
187 	return 1;
188 }
189 
190 static int
do_ifndef(char * p)191 do_ifndef(char *p)
192 {
193 	if (++if_depth >= MAXNEST)
194 	{	debug("zpp: too deeply nested (max %d)\n", MAXNEST);
195 		return 0;
196 	}
197 	if_truth[if_depth] = (check_ifdef(p) == (char *)0);
198 	printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
199 
200 	return 1;
201 }
202 
203 static int
is_simple(char * q)204 is_simple(char *q)
205 {
206 	if (!q) return 0;
207 	if (strcmp(q, "0") == 0)
208 		if_truth[if_depth] = 0;
209 	else if (strcmp(q, "1") == 0)
210 		if_truth[if_depth] = 1;
211 	else
212 		return 0;
213 	return 1;
214 }
215 
216 static int
do_if(char * p)217 do_if(char *p)
218 {	char *q = do_common(p);
219 	if (++if_depth >= MAXNEST)
220 	{	debug("zpp: too deeply nested (max %d)\n", MAXNEST);
221 		return 0;
222 	}
223 	if (!is_simple(q)
224 	&&  !is_simple(check_ifdef(q)))
225 	{	debug("zpp: cannot handle #if %s\n", q);
226 		return 0;
227 	}
228 	printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
229 
230 	return 1;
231 }
232 
233 static int
do_else(char * p)234 do_else(char *p)
235 {
236 	debug("zpp: do_else %s", p);
237 	if_truth[if_depth] = 1-if_truth[if_depth];
238 	printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
239 
240 	return 1;
241 }
242 
243 static int
do_endif(char * p)244 do_endif(char *p)
245 {
246 	if (--if_depth < 0)
247 	{	debug("zpp: unbalanced #endif %s\n", p);
248 		return 0;
249 	}
250 	return 1;
251 }
252 
253 static int
do_include(char * p)254 do_include(char *p)
255 {	char *r, *q;
256 
257 	q = strchr(p, '<');
258 	r = strrchr(p, '>');
259 	if (!q || !r)
260 	{	q = strchr (p, '\"');
261 		r = strrchr(p, '\"');
262 		if (!q || !r || q == r)
263 		{	debug("zpp: malformed #include %s", p);
264 			return 0;
265 	}	}
266 	*r = '\0';
267 	return zpp_do(++q);
268 }
269 
270 static int
in_comment(char * p)271 in_comment(char *p)
272 {	char *q = p;
273 
274 	for (q = p; *q != '\n' && *q != '\0'; q++)
275 		switch (state) {
276 		case PLAIN:
277 			switch (*q) {
278 			case  '"': state = IN_STRING; break;
279 			case '\'': state = IN_QUOTE; break;
280 			case  '/': state = S_COMM; break;
281 			case '\\': q++; break;
282 			}
283 			break;
284 		case IN_STRING:
285 			if (*q == '"') state = PLAIN;
286 			else if (*q == '\\') q++;
287 			break;
288 		case IN_QUOTE:
289 			if (*q == '\'') state = PLAIN;
290 			else if (*q == '\\') q++;
291 			break;
292 		case S_COMM:
293 			if (*q == '*')
294 			{	*(q-1) = *q = ' ';
295 				state = COMMENT;
296 			} else if (*q != '/')
297 				state = PLAIN;
298 			break;
299 		case COMMENT:
300 			state = (*q == '*') ? E_COMM: COMMENT;
301 			*q = ' ';
302 			break;
303 		case E_COMM:
304 			if (*q == '/')
305 				state = PLAIN;
306 			else if (*q != '*')
307 				state = COMMENT;
308 			*q = ' ';
309 			break;
310 		}
311 	if (state == S_COMM) state = PLAIN;
312 	else if (state == E_COMM) state = COMMENT;
313 	return (state == COMMENT);
314 }
315 
316 static int
strip_cpp_comments(char * p)317 strip_cpp_comments(char *p)
318 {	char *q;
319 
320 	q = strstr(p, "//");
321 	if (q)
322 	{	if (q > p && *(q-1) == '\\')
323 		{	return strip_cpp_comments(q+1);
324 		}
325 		*q = '\n';
326 		*(q+1) = '\0';
327 		return 1;
328 	}
329 	return 0;
330 }
331 
332 static int
zpp_do(char * fnm)333 zpp_do(char *fnm)
334 {	char buf[2048], buf2[MAXLINE], *p; int n, on;
335 	FILE *inp; int lno = 0, nw_lno = 0;
336 
337 	if ((inp = fopen(fnm, "r")) == NULL)
338 	{	fprintf(stdout, "spin: error: No file '%s'\n", fnm);
339 		exit(1);	/* 4.1.2 was stderr */
340 	}
341 	printing[0] = if_truth[0] = 1;
342 	fprintf(outpp, "#line %d \"%s\"\n", lno+1, fnm);
343 	while (fgets(buf, MAXLINE, inp))
344 	{	lno++; n = (int) strlen(buf);
345 		on = 0; nw_lno = 0;
346 		while (n > 2 && buf[n-2] == '\\')
347 		{	buf[n-2] = '\0';
348 feedme:
349 			if (!fgets(buf2, MAXLINE, inp))
350 			{	debug("zpp: unexpected EOF ln %d\n", lno);
351 				return 0;	/* switch to cpp */
352 			}
353 			lno++;
354 			if (n + (int) strlen(buf2) >= 2048)
355 			{	debug("zpp: line %d too long\n", lno);
356 				return 0;
357 			}
358 			strcat(buf, buf2);
359 			n = (int) strlen(buf);
360 		}
361 
362 		if (strip_cpp_comments(&buf[on]))
363 			n = (int) strlen(buf);
364 
365 		if (in_comment(&buf[on]))
366 		{	buf[n-1] = '\0'; /* eat newline */
367 			on = n-1; nw_lno = 1;
368 			goto feedme;
369 		}
370 		p = buf + strspn(buf, " \t");
371 		if (nw_lno && *p != '#')
372 			fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
373 		if (*p == '#')
374 		{	if (!process(p+1, lno+1, fnm))
375 				return 0;
376 		} else if (printing[if_depth])
377 			fprintf(outpp, "%s", apply(buf));
378 	}
379 	fclose(inp);
380 	return 1;
381 }
382 
383 int
try_zpp(char * fnm,char * onm)384 try_zpp(char *fnm, char *onm)
385 {	int r;
386 	if ((outpp = fopen(onm, MFLAGS)) == NULL)
387 		return 0;
388 	r = zpp_do(fnm);
389 	fclose(outpp);
390 	return r;	/* 1 = ok; 0 = use cpp */
391 }
392 
393 static struct Directives {
394 	int len;
395 	char *directive;
396 	int (*handler)(char *);
397 	int interp;
398 } s[] = {
399 	{ 6, "define",	 do_define,	1 },
400 	{ 4, "else",	 do_else,	0 },
401 	{ 5, "endif",	 do_endif,	0 },
402 	{ 5, "ifdef",	 do_ifdef,	0 },
403 	{ 6, "ifndef",   do_ifndef,	0 },
404 	{ 2, "if",	 do_if,		0 },
405 	{ 7, "include",  do_include,	1 },
406 	{ 8, "undefine", do_undefine,	1 },
407 };
408 
409 static int
process(char * q,int lno,char * fnm)410 process(char *q, int lno, char *fnm)
411 {	char *p; int i, r;
412 
413 	for (p = q; *p; p++)
414 		if (*p != ' ' && *p != '\t')
415 			break;
416 
417 	if (strncmp(p, "line", 4) == 0)
418 	{	p += 4;
419 		while (*p == ' ' || *p == '\t')
420 		{	p++;
421 		}
422 		lno = atoi(p);
423 		return 1;	/* line directive */
424 	}
425 	if (isdigit((int) *p))
426 	{	lno = atoi(p);
427 		return 1;
428 	}
429 	if (strncmp(p, "error", 5) == 0)
430 	{	printf("spin: %s", p);
431 		exit(1);
432 	}
433 	if (strncmp(p, "warning", 7) == 0)
434 	{	printf("spin: %s", p);
435 		return 1;
436 	}
437 	for (i = 0; i < (int) (sizeof(s)/sizeof(struct Directives)); i++)
438 		if (!strncmp(s[i].directive, p, s[i].len))
439 		{	if (s[i].interp
440 			&&  !printing[if_depth])
441 				return 1;
442 			fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
443 			r = s[i].handler(p +  s[i].len);
444 			if (i == 6)	/* include */
445 				fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
446 			return r;
447 		}
448 
449 	debug("zpp: unrecognized directive: %s", p);
450 	return 0;
451 }
452 #endif
453