| 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 | |
| 20 | #ifdef PC |
| 21 | enum cstate { PLAIN, IN_STRING, IN_QUOTE, S_COMM, COMMENT, E_COMM }; |
| 22 | |
| 23 | #define MAXNEST 32 |
| 24 | #define MAXDEF 128 |
| 25 | #define MAXLINE 2048 |
| 26 | #define GENEROUS 8192 |
| 27 | |
| 28 | #define debug(x,y) if (verbose) printf(x,y) |
| 29 | |
| 30 | static FILE *outpp /* = stdout */; |
| 31 | |
| 32 | static int if_truth[MAXNEST]; |
| 33 | static int printing[MAXNEST]; |
| 34 | static int if_depth, nr_defs, verbose = 0; |
| 35 | static enum cstate state = PLAIN; |
| 36 | static char Out1[GENEROUS], Out2[GENEROUS]; |
| 37 | |
| 38 | static struct Defines { |
| 39 | int exists; |
| 40 | char *src, *trg; |
| 41 | } d[MAXDEF]; |
| 42 | |
| 43 | static int process(char *, int, char *); |
| 44 | static int zpp_do(char *); |
| 45 | |
| 46 | extern char *emalloc(size_t); /* main.c */ |
| 47 | |
| 48 | static int |
| 49 | do_define(char *p) |
| 50 | { char *q, *r, *s; |
| 51 | |
| 52 | for (q = p+strlen(p)-1; q > p; q--) |
| 53 | if (*q == '\n' || *q == '\t' || *q == ' ') |
| 54 | *q = '\0'; |
| 55 | else |
| 56 | break; |
| 57 | |
| 58 | q = p + strspn(p, " \t"); |
| 59 | if (!(r = strchr(q, '\t'))) |
| 60 | r = strchr(q, ' '); |
| 61 | if (!r) { s = ""; goto adddef; } |
| 62 | s = r + strspn(r, " \t"); |
| 63 | *r = '\0'; |
| 64 | if (strchr(q, '(')) |
| 65 | { debug("zpp: #define with arguments %s\n", q); |
| 66 | return 0; |
| 67 | } |
| 68 | for (r = q+strlen(q)-1; r > q; r--) |
| 69 | if (*r == ' ' || *r == '\t') |
| 70 | *r = '\0'; |
| 71 | else |
| 72 | break; |
| 73 | if (nr_defs >= MAXDEF) |
| 74 | { debug("zpp: too many #defines (max %d)\n", nr_defs); |
| 75 | return 0; |
| 76 | } |
| 77 | if (strcmp(q, s) != 0) |
| 78 | { int j; |
| 79 | adddef: for (j = 0; j < nr_defs; j++) |
| 80 | if (!strcmp(d[j].src, q)) |
| 81 | d[j].exists = 0; |
| 82 | d[nr_defs].src = emalloc(strlen(q)+1); |
| 83 | d[nr_defs].trg = emalloc(strlen(s)+1); |
| 84 | strcpy(d[nr_defs].src, q); |
| 85 | strcpy(d[nr_defs].trg, s); |
| 86 | d[nr_defs++].exists = 1; |
| 87 | } |
| 88 | return 1; |
| 89 | } |
| 90 | |
| 91 | static int |
| 92 | isvalid(int c) |
| 93 | { |
| 94 | return (isalnum(c) || c == '_'); |
| 95 | } |
| 96 | |
| 97 | static char * |
| 98 | apply(char *p0) |
| 99 | { char *out, *in1, *in2, *startat; |
| 100 | int i, j; |
| 101 | |
| 102 | startat = in1 = Out2; strcpy(Out2, p0); |
| 103 | out = Out1; *out = '\0'; |
| 104 | |
| 105 | for (i = nr_defs-1; i >= 0; i--) |
| 106 | { if (!d[i].exists) continue; |
| 107 | j = (int) strlen(d[i].src); |
| 108 | more: in2 = strstr(startat, d[i].src); |
| 109 | if (!in2) /* no more matches */ |
| 110 | { startat = in1; |
| 111 | continue; |
| 112 | } |
| 113 | if ((in2 == in1 || !isvalid(*(in2-1))) |
| 114 | && (in2+j == '\0' || !isvalid(*(in2+j)))) |
| 115 | { *in2 = '\0'; |
| 116 | |
| 117 | if (strlen(in1)+strlen(d[i].trg)+strlen(in2+j) >= GENEROUS) |
| 118 | { |
| 119 | printf("spin: macro expansion overflow %s -> %s ?\n", |
| 120 | d[i].src, d[i].trg); |
| 121 | return in1; |
| 122 | } |
| 123 | strcat(out, in1); |
| 124 | strcat(out, d[i].trg); |
| 125 | strcat(out, in2+j); |
| 126 | if (in1 == Out2) |
| 127 | { startat = in1 = Out1; |
| 128 | out = Out2; |
| 129 | } else |
| 130 | { startat = in1 = Out2; |
| 131 | out = Out1; |
| 132 | } |
| 133 | *out = '\0'; |
| 134 | } else |
| 135 | { startat = in2+1; /* +1 not +j.. */ |
| 136 | } |
| 137 | goto more; /* recursive defines */ |
| 138 | } |
| 139 | return in1; |
| 140 | } |
| 141 | |
| 142 | static char * |
| 143 | do_common(char *p) |
| 144 | { char *q, *s; |
| 145 | |
| 146 | q = p + strspn(p, " \t"); |
| 147 | for (s = (q + strlen(q) - 1); s > q; s--) |
| 148 | if (*s == ' ' || *s == '\t' || *s == '\n') |
| 149 | *s = '\0'; |
| 150 | else |
| 151 | break; |
| 152 | return q; |
| 153 | } |
| 154 | |
| 155 | static int |
| 156 | do_undefine(char *p) |
| 157 | { int i; char *q = do_common(p); |
| 158 | |
| 159 | for (i = 0; i < nr_defs; i++) |
| 160 | if (!strcmp(d[i].src, q)) |
| 161 | d[i].exists = 0; |
| 162 | return 1; |
| 163 | } |
| 164 | |
| 165 | static char * |
| 166 | check_ifdef(char *p) |
| 167 | { int i; char *q = do_common(p); |
| 168 | |
| 169 | for (i = 0; i < nr_defs; i++) |
| 170 | if (d[i].exists |
| 171 | && !strcmp(d[i].src, q)) |
| 172 | return d[i].trg; |
| 173 | return (char *) 0; |
| 174 | } |
| 175 | |
| 176 | static int |
| 177 | do_ifdef(char *p) |
| 178 | { |
| 179 | if (++if_depth >= MAXNEST) |
| 180 | { debug("zpp: too deeply nested (max %d)\n", MAXNEST); |
| 181 | return 0; |
| 182 | } |
| 183 | if_truth[if_depth] = (check_ifdef(p) != (char *)0); |
| 184 | printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; |
| 185 | |
| 186 | return 1; |
| 187 | } |
| 188 | |
| 189 | static int |
| 190 | do_ifndef(char *p) |
| 191 | { |
| 192 | if (++if_depth >= MAXNEST) |
| 193 | { debug("zpp: too deeply nested (max %d)\n", MAXNEST); |
| 194 | return 0; |
| 195 | } |
| 196 | if_truth[if_depth] = (check_ifdef(p) == (char *)0); |
| 197 | printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; |
| 198 | |
| 199 | return 1; |
| 200 | } |
| 201 | |
| 202 | static int |
| 203 | is_simple(char *q) |
| 204 | { |
| 205 | if (!q) return 0; |
| 206 | if (strcmp(q, "0") == 0) |
| 207 | if_truth[if_depth] = 0; |
| 208 | else if (strcmp(q, "1") == 0) |
| 209 | if_truth[if_depth] = 1; |
| 210 | else |
| 211 | return 0; |
| 212 | return 1; |
| 213 | } |
| 214 | |
| 215 | static int |
| 216 | do_if(char *p) |
| 217 | { char *q = do_common(p); |
| 218 | if (++if_depth >= MAXNEST) |
| 219 | { debug("zpp: too deeply nested (max %d)\n", MAXNEST); |
| 220 | return 0; |
| 221 | } |
| 222 | if (!is_simple(q) |
| 223 | && !is_simple(check_ifdef(q))) |
| 224 | { debug("zpp: cannot handle #if %s\n", q); |
| 225 | return 0; |
| 226 | } |
| 227 | printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; |
| 228 | |
| 229 | return 1; |
| 230 | } |
| 231 | |
| 232 | static int |
| 233 | do_else(char *unused) |
| 234 | { |
| 235 | if_truth[if_depth] = 1-if_truth[if_depth]; |
| 236 | printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; |
| 237 | |
| 238 | return 1; |
| 239 | } |
| 240 | |
| 241 | static int |
| 242 | do_endif(char *p) |
| 243 | { |
| 244 | if (--if_depth < 0) |
| 245 | { debug("zpp: unbalanced #endif %s\n", p); |
| 246 | return 0; |
| 247 | } |
| 248 | return 1; |
| 249 | } |
| 250 | |
| 251 | static int |
| 252 | do_include(char *p) |
| 253 | { char *r, *q; |
| 254 | |
| 255 | q = strchr(p, '<'); |
| 256 | r = strrchr(p, '>'); |
| 257 | if (!q || !r) |
| 258 | { q = strchr (p, '\"'); |
| 259 | r = strrchr(p, '\"'); |
| 260 | if (!q || !r || q == r) |
| 261 | { debug("zpp: malformed #include %s", p); |
| 262 | return 0; |
| 263 | } } |
| 264 | *r = '\0'; |
| 265 | return zpp_do(++q); |
| 266 | } |
| 267 | |
| 268 | static int |
| 269 | in_comment(char *p) |
| 270 | { char *q = p; |
| 271 | |
| 272 | for (q = p; *q != '\n' && *q != '\0'; q++) |
| 273 | switch (state) { |
| 274 | case PLAIN: |
| 275 | switch (*q) { |
| 276 | case '"': state = IN_STRING; break; |
| 277 | case '\'': state = IN_QUOTE; break; |
| 278 | case '/': state = S_COMM; break; |
| 279 | case '\\': q++; break; |
| 280 | } |
| 281 | break; |
| 282 | case IN_STRING: |
| 283 | if (*q == '"') state = PLAIN; |
| 284 | else if (*q == '\\') q++; |
| 285 | break; |
| 286 | case IN_QUOTE: |
| 287 | if (*q == '\'') state = PLAIN; |
| 288 | else if (*q == '\\') q++; |
| 289 | break; |
| 290 | case S_COMM: |
| 291 | if (*q == '*') |
| 292 | { *(q-1) = *q = ' '; |
| 293 | state = COMMENT; |
| 294 | } else if (*q != '/') |
| 295 | state = PLAIN; |
| 296 | break; |
| 297 | case COMMENT: |
| 298 | state = (*q == '*') ? E_COMM: COMMENT; |
| 299 | *q = ' '; |
| 300 | break; |
| 301 | case E_COMM: |
| 302 | if (*q == '/') |
| 303 | state = PLAIN; |
| 304 | else if (*q != '*') |
| 305 | state = COMMENT; |
| 306 | *q = ' '; |
| 307 | break; |
| 308 | } |
| 309 | if (state == S_COMM) state = PLAIN; |
| 310 | else if (state == E_COMM) state = COMMENT; |
| 311 | return (state == COMMENT); |
| 312 | } |
| 313 | |
| 314 | static int |
| 315 | zpp_do(char *fnm) |
| 316 | { char buf[2048], buf2[MAXLINE], *p; int n, on; |
| 317 | FILE *inp; int lno = 0, nw_lno = 0; |
| 318 | |
| 319 | if ((inp = fopen(fnm, "r")) == NULL) |
| 320 | { fprintf(stdout, "spin: error, '%s': No such file\n", fnm); |
| 321 | return 0; /* 4.1.2 was stderr */ |
| 322 | } |
| 323 | printing[0] = if_truth[0] = 1; |
| 324 | fprintf(outpp, "#line %d \"%s\"\n", lno+1, fnm); |
| 325 | while (fgets(buf, MAXLINE, inp)) |
| 326 | { lno++; n = (int) strlen(buf); |
| 327 | on = 0; nw_lno = 0; |
| 328 | while (n > 2 && buf[n-2] == '\\') |
| 329 | { buf[n-2] = '\0'; |
| 330 | feedme: if (!fgets(buf2, MAXLINE, inp)) |
| 331 | { debug("zpp: unexpected EOF ln %d\n", lno); |
| 332 | return 0; /* switch to cpp */ |
| 333 | } |
| 334 | lno++; |
| 335 | if (n + (int) strlen(buf2) >= 2048) |
| 336 | { debug("zpp: line %d too long\n", lno); |
| 337 | return 0; |
| 338 | } |
| 339 | strcat(buf, buf2); |
| 340 | n = (int) strlen(buf); |
| 341 | } |
| 342 | if (in_comment(&buf[on])) |
| 343 | { buf[n-1] = '\0'; /* eat newline */ |
| 344 | on = n-1; nw_lno = 1; |
| 345 | goto feedme; |
| 346 | } |
| 347 | p = buf + strspn(buf, " \t"); |
| 348 | if (nw_lno && *p != '#') |
| 349 | fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); |
| 350 | if (*p == '#') |
| 351 | { if (!process(p+1, lno+1, fnm)) |
| 352 | return 0; |
| 353 | } else if (printing[if_depth]) |
| 354 | fprintf(outpp, "%s", apply(buf)); |
| 355 | } |
| 356 | fclose(inp); |
| 357 | return 1; |
| 358 | } |
| 359 | |
| 360 | int |
| 361 | try_zpp(char *fnm, char *onm) |
| 362 | { int r; |
| 363 | if ((outpp = fopen(onm, "w")) == NULL) |
| 364 | return 0; |
| 365 | r = zpp_do(fnm); |
| 366 | fclose(outpp); |
| 367 | return r; /* 1 = ok; 0 = use cpp */ |
| 368 | } |
| 369 | |
| 370 | static struct Directives { |
| 371 | int len; |
| 372 | char *directive; |
| 373 | int (*handler)(char *); |
| 374 | int interp; |
| 375 | } s[] = { |
| 376 | { 6, "define", do_define, 1 }, |
| 377 | { 4, "else", do_else, 0 }, |
| 378 | { 5, "endif", do_endif, 0 }, |
| 379 | { 5, "ifdef", do_ifdef, 0 }, |
| 380 | { 6, "ifndef", do_ifndef, 0 }, |
| 381 | { 2, "if", do_if, 0 }, |
| 382 | { 7, "include", do_include, 1 }, |
| 383 | { 8, "undefine", do_undefine, 1 }, |
| 384 | }; |
| 385 | |
| 386 | static int |
| 387 | process(char *q, int lno, char *fnm) |
| 388 | { char *p; int i, r; |
| 389 | |
| 390 | for (p = q; *p; p++) |
| 391 | if (*p != ' ' && *p != '\t') |
| 392 | break; |
| 393 | for (i = 0; i < (int) (sizeof(s)/sizeof(struct Directives)); i++) |
| 394 | if (!strncmp(s[i].directive, p, s[i].len)) |
| 395 | { if (s[i].interp |
| 396 | && !printing[if_depth]) |
| 397 | return 1; |
| 398 | fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); |
| 399 | r = s[i].handler(p + s[i].len); |
| 400 | if (i == 6) /* include */ |
| 401 | fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); |
| 402 | return r; |
| 403 | } |
| 404 | |
| 405 | debug("zpp: unrecognized directive: %s", p); |
| 406 | return 0; |
| 407 | } |
| 408 | #endif |