1/* 2 * 3 * Copyright 1992 Network Computing Devices, Inc. 4 * 5 * Permission to use, copy, modify, and distribute this software and its 6 * documentation for any purpose and without fee is hereby granted, provided 7 * that the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Network Computing Devices may not be 10 * used in advertising or publicity pertaining to distribution of the software 11 * without specific, written prior permission. Network Computing Devices makes 12 * no representations about the suitability of this software for any purpose. 13 * It is provided ``as is'' without express or implied warranty. 14 * 15 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 17 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, 18 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 19 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 20 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Author: Jim Fulton 24 * Network Computing Devices, Inc. 25 * 26 * Simple if statement processor 27 * 28 * This module can be used to evaluate string representations of C language 29 * if constructs. It accepts the following grammar: 30 * 31 * EXPRESSION := VALUE 32 * | VALUE BINOP EXPRESSION 33 * | VALUE '?' EXPRESSION ':' EXPRESSION 34 * 35 * VALUE := '(' EXPRESSION ')' 36 * | '!' VALUE 37 * | '-' VALUE 38 * | '+' VALUE 39 * | '~' VALUE 40 * | 'defined' '(' variable ')' 41 * | 'defined' variable 42 * | # variable '(' variable-list ')' 43 * | variable 44 * | number 45 * 46 * BINOP := '*' | '/' | '%' 47 * | '+' | '-' 48 * | '<<' | '>>' 49 * | '<' | '>' | '<=' | '>=' 50 * | '==' | '!=' 51 * | '&' | '^' | '|' 52 * | '&&' | '||' 53 * 54 * The normal C order of precedence is supported. 55 * 56 * 57 * External Entry Points: 58 * 59 * ParseIfExpression parse a string for #if 60 */ 61 62#include "ifparser.h" 63#include <ctype.h> 64#include <stdlib.h> 65#include <string.h> 66#include <limits.h> 67 68/**************************************************************************** 69 Internal Macros and Utilities for Parser 70 ****************************************************************************/ 71 72#define DO(val) if (!(val)) return NULL 73#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) 74#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ 75#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') 76 77 78static const char * 79parse_variable(IfParser *g, const char *cp, const char **varp) 80{ 81 SKIPSPACE(cp); 82 83 if (!isvarfirstletter(*cp)) 84 return CALLFUNC(g, handle_error) (g, cp, "variable name"); 85 86 *varp = cp; 87 /* EMPTY */ 88 for (cp++; isalnum(*cp) || *cp == '_'; cp++); 89 return cp; 90} 91 92static const char * 93parse_number(IfParser *g, const char *cp, long *valp) 94{ 95 long base = 10; 96 97 SKIPSPACE(cp); 98 99 if (!isdigit(*cp)) 100 return CALLFUNC(g, handle_error) (g, cp, "number"); 101 102 *valp = 0; 103 104 if (*cp == '0') { 105 cp++; 106 if ((*cp == 'x') || (*cp == 'X')) { 107 base = 16; 108 cp++; 109 } 110 else { 111 base = 8; 112 } 113 } 114 115 /* Ignore overflows and assume ASCII, what source is usually written in */ 116 while (1) { 117 int increment = -1; 118 119 if (base == 8) { 120 if ((*cp >= '0') && (*cp <= '7')) 121 increment = *cp++ - '0'; 122 } 123 else if (base == 16) { 124 if ((*cp >= '0') && (*cp <= '9')) 125 increment = *cp++ - '0'; 126 else if ((*cp >= 'A') && (*cp <= 'F')) 127 increment = *cp++ - ('A' - 10); 128 else if ((*cp >= 'a') && (*cp <= 'f')) 129 increment = *cp++ - ('a' - 10); 130 } 131 else { /* Decimal */ 132 if ((*cp >= '0') && (*cp <= '9')) 133 increment = *cp++ - '0'; 134 } 135 if (increment < 0) 136 break; 137 *valp = (*valp * base) + increment; 138 } 139 140 /* Skip trailing qualifiers */ 141 while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') 142 cp++; 143 return cp; 144} 145 146static const char * 147parse_character(IfParser *g, const char *cp, long *valp) 148{ 149 char val; 150 151 SKIPSPACE(cp); 152 if (*cp == '\\') 153 switch (cp[1]) { 154 case 'n': 155 val = '\n'; 156 break; 157 case 't': 158 val = '\t'; 159 break; 160 case 'v': 161 val = '\v'; 162 break; 163 case 'b': 164 val = '\b'; 165 break; 166 case 'r': 167 val = '\r'; 168 break; 169 case 'f': 170 val = '\f'; 171 break; 172 case 'a': 173 val = '\a'; 174 break; 175 case '\\': 176 val = '\\'; 177 break; 178 case '?': 179 val = '\?'; 180 break; 181 case '\'': 182 val = '\''; 183 break; 184 case '\"': 185 val = '\"'; 186 break; 187 case 'x': 188 val = (char) strtol(cp + 2, NULL, 16); 189 break; 190 default: 191 val = (char) strtol(cp + 1, NULL, 8); 192 break; 193 } 194 else 195 val = *cp; 196 while (*cp != '\'') 197 cp++; 198 *valp = (long) val; 199 return cp; 200} 201 202static const char * 203parse_value(IfParser *g, const char *cp, long *valp) 204{ 205 const char *var; 206 207 *valp = 0; 208 209 SKIPSPACE(cp); 210 if (!*cp) 211 return cp; 212 213 switch (*cp) { 214 case '(': 215 DO(cp = ParseIfExpression(g, cp + 1, valp)); 216 SKIPSPACE(cp); 217 if (*cp != ')') 218 return CALLFUNC(g, handle_error) (g, cp, ")"); 219 220 return cp + 1; /* skip the right paren */ 221 222 case '!': 223 DO(cp = parse_value(g, cp + 1, valp)); 224 *valp = !(*valp); 225 return cp; 226 227 case '-': 228 DO(cp = parse_value(g, cp + 1, valp)); 229 *valp = -(*valp); 230 return cp; 231 232 case '+': 233 DO(cp = parse_value(g, cp + 1, valp)); 234 return cp; 235 236 case '~': 237 DO(cp = parse_value(g, cp + 1, valp)); 238 *valp = ~(*valp); 239 return cp; 240 241 case '#': 242 DO(cp = parse_variable(g, cp + 1, &var)); 243 SKIPSPACE(cp); 244 if (*cp != '(') 245 return CALLFUNC(g, handle_error) (g, cp, "("); 246 do { 247 DO(cp = parse_variable(g, cp + 1, &var)); 248 SKIPSPACE(cp); 249 } while (*cp && *cp != ')'); 250 if (*cp != ')') 251 return CALLFUNC(g, handle_error) (g, cp, ")"); 252 *valp = 1; /* XXX */ 253 return cp + 1; 254 255 case '\'': 256 DO(cp = parse_character(g, cp + 1, valp)); 257 if (*cp != '\'') 258 return CALLFUNC(g, handle_error) (g, cp, "'"); 259 return cp + 1; 260 261 case 'd': 262 if (strncmp(cp, "defined", 7) == 0 && !isalnum(cp[7])) { 263 int paren = 0; 264 int len; 265 266 cp += 7; 267 SKIPSPACE(cp); 268 if (*cp == '(') { 269 paren = 1; 270 cp++; 271 } 272 DO(cp = parse_variable(g, cp, &var)); 273 len = cp - var; 274 SKIPSPACE(cp); 275 if (paren && *cp != ')') 276 return CALLFUNC(g, handle_error) (g, cp, ")"); 277 *valp = (*(g->funcs.eval_defined)) (g, var, len); 278 return cp + paren; /* skip the right paren */ 279 } 280 /* fall out */ 281 } 282 283 if (isdigit(*cp)) { 284 DO(cp = parse_number(g, cp, valp)); 285 } 286 else if (!isvarfirstletter(*cp)) 287 return CALLFUNC(g, handle_error) (g, cp, "variable or number"); 288 else { 289 const char *varend; 290 291 DO(cp = parse_variable(g, cp, &var)); 292 varend = cp; 293 SKIPSPACE(cp); 294 if (*cp != '(') { 295 *valp = (*(g->funcs.eval_variable)) (g, var, varend - var); 296 } 297 else { 298 do { 299 long dummy; 300 301 DO(cp = ParseIfExpression(g, cp + 1, &dummy)); 302 SKIPSPACE(cp); 303 if (*cp == ')') 304 break; 305 if (*cp != ',') 306 return CALLFUNC(g, handle_error) (g, cp, ","); 307 } while (1); 308 309 *valp = 1; /* XXX */ 310 cp++; 311 } 312 } 313 314 return cp; 315} 316 317static const char * 318parse_product(IfParser *g, const char *cp, long *valp) 319{ 320 long rightval; 321 322 DO(cp = parse_value(g, cp, valp)); 323 SKIPSPACE(cp); 324 325 switch (*cp) { 326 case '*': 327 DO(cp = parse_product(g, cp + 1, &rightval)); 328 *valp = (*valp * rightval); 329 break; 330 331 case '/': 332 DO(cp = parse_product(g, cp + 1, &rightval)); 333 if (rightval) 334 *valp = (*valp / rightval); 335 else 336 *valp = LONG_MAX; 337 break; 338 339 case '%': 340 DO(cp = parse_product(g, cp + 1, &rightval)); 341 if (rightval) 342 *valp = (*valp % rightval); 343 else 344 *valp = LONG_MAX; 345 break; 346 } 347 return cp; 348} 349 350static const char * 351parse_sum(IfParser *g, const char *cp, long *valp) 352{ 353 long rightval; 354 355 DO(cp = parse_product(g, cp, valp)); 356 SKIPSPACE(cp); 357 358 switch (*cp) { 359 case '+': 360 DO(cp = parse_sum(g, cp + 1, &rightval)); 361 *valp = (*valp + rightval); 362 break; 363 364 case '-': 365 DO(cp = parse_sum(g, cp + 1, &rightval)); 366 *valp = (*valp - rightval); 367 break; 368 } 369 return cp; 370} 371 372static const char * 373parse_shift(IfParser *g, const char *cp, long *valp) 374{ 375 long rightval; 376 377 DO(cp = parse_sum(g, cp, valp)); 378 SKIPSPACE(cp); 379 380 switch (*cp) { 381 case '<': 382 if (cp[1] == '<') { 383 DO(cp = parse_shift(g, cp + 2, &rightval)); 384 *valp = (*valp << rightval); 385 } 386 break; 387 388 case '>': 389 if (cp[1] == '>') { 390 DO(cp = parse_shift(g, cp + 2, &rightval)); 391 *valp = (*valp >> rightval); 392 } 393 break; 394 } 395 return cp; 396} 397 398static const char * 399parse_inequality(IfParser *g, const char *cp, long *valp) 400{ 401 long rightval; 402 403 DO(cp = parse_shift(g, cp, valp)); 404 SKIPSPACE(cp); 405 406 switch (*cp) { 407 case '<': 408 if (cp[1] == '=') { 409 DO(cp = parse_inequality(g, cp + 2, &rightval)); 410 *valp = (*valp <= rightval); 411 } 412 else { 413 DO(cp = parse_inequality(g, cp + 1, &rightval)); 414 *valp = (*valp < rightval); 415 } 416 break; 417 418 case '>': 419 if (cp[1] == '=') { 420 DO(cp = parse_inequality(g, cp + 2, &rightval)); 421 *valp = (*valp >= rightval); 422 } 423 else { 424 DO(cp = parse_inequality(g, cp + 1, &rightval)); 425 *valp = (*valp > rightval); 426 } 427 break; 428 } 429 return cp; 430} 431 432static const char * 433parse_equality(IfParser *g, const char *cp, long *valp) 434{ 435 long rightval; 436 437 DO(cp = parse_inequality(g, cp, valp)); 438 SKIPSPACE(cp); 439 440 switch (*cp) { 441 case '=': 442 if (cp[1] == '=') 443 cp++; 444 DO(cp = parse_equality(g, cp + 1, &rightval)); 445 *valp = (*valp == rightval); 446 break; 447 448 case '!': 449 if (cp[1] != '=') 450 break; 451 DO(cp = parse_equality(g, cp + 2, &rightval)); 452 *valp = (*valp != rightval); 453 break; 454 } 455 return cp; 456} 457 458static const char * 459parse_band(IfParser *g, const char *cp, long *valp) 460{ 461 long rightval; 462 463 DO(cp = parse_equality(g, cp, valp)); 464 SKIPSPACE(cp); 465 466 switch (*cp) { 467 case '&': 468 if (cp[1] != '&') { 469 DO(cp = parse_band(g, cp + 1, &rightval)); 470 *valp = (*valp & rightval); 471 } 472 break; 473 } 474 return cp; 475} 476 477static const char * 478parse_bxor(IfParser *g, const char *cp, long *valp) 479{ 480 long rightval; 481 482 DO(cp = parse_band(g, cp, valp)); 483 SKIPSPACE(cp); 484 485 switch (*cp) { 486 case '^': 487 DO(cp = parse_bxor(g, cp + 1, &rightval)); 488 *valp = (*valp ^ rightval); 489 break; 490 } 491 return cp; 492} 493 494static const char * 495parse_bor(IfParser *g, const char *cp, long *valp) 496{ 497 long rightval; 498 499 DO(cp = parse_bxor(g, cp, valp)); 500 SKIPSPACE(cp); 501 502 switch (*cp) { 503 case '|': 504 if (cp[1] != '|') { 505 DO(cp = parse_bor(g, cp + 1, &rightval)); 506 *valp = (*valp | rightval); 507 } 508 break; 509 } 510 return cp; 511} 512 513static const char * 514parse_land(IfParser *g, const char *cp, long *valp) 515{ 516 long rightval; 517 518 DO(cp = parse_bor(g, cp, valp)); 519 SKIPSPACE(cp); 520 521 switch (*cp) { 522 case '&': 523 if (cp[1] != '&') 524 return CALLFUNC(g, handle_error) (g, cp, "&&"); 525 DO(cp = parse_land(g, cp + 2, &rightval)); 526 *valp = (*valp && rightval); 527 break; 528 } 529 return cp; 530} 531 532static const char * 533parse_lor(IfParser *g, const char *cp, long *valp) 534{ 535 long rightval; 536 537 DO(cp = parse_land(g, cp, valp)); 538 SKIPSPACE(cp); 539 540 switch (*cp) { 541 case '|': 542 if (cp[1] != '|') 543 return CALLFUNC(g, handle_error) (g, cp, "||"); 544 DO(cp = parse_lor(g, cp + 2, &rightval)); 545 *valp = (*valp || rightval); 546 break; 547 } 548 return cp; 549} 550 551static const char * 552parse_cond(IfParser *g, const char *cp, long *valp) 553{ 554 long trueval, falseval; 555 556 DO(cp = parse_lor(g, cp, valp)); 557 SKIPSPACE(cp); 558 559 switch (*cp) { 560 case '?': 561 DO(cp = parse_cond(g, cp + 1, &trueval)); 562 SKIPSPACE(cp); 563 if (*cp != ':') 564 return CALLFUNC(g, handle_error) (g, cp, ":"); 565 DO(cp = parse_cond(g, cp + 1, &falseval)); 566 *valp = (*valp ? trueval : falseval); 567 break; 568 } 569 return cp; 570} 571 572 573/**************************************************************************** 574 External Entry Points 575 ****************************************************************************/ 576 577const char * 578ParseIfExpression(IfParser *g, const char *cp, long *valp) 579{ 580 return parse_cond(g, cp, valp); 581} 582