1 /* 2 PC -- programmer's calculator. 3 4 This program implements a simple recursive descent parser that 5 understands pretty much all standard C language math and logic 6 expressions. It handles the usual add, subtract, multiply, divide, 7 and mod sort of stuff. It can also deal with logical/relational 8 operations and expressions. The logic/relational operations AND, OR, 9 NOT, and EXCLUSIVE OR, &&, ||, ==, !=, <, >, <=, and >= are all 10 supported. It also handles parens and nested expresions as well as 11 left and right shifts. There are variables and assignments (as well 12 as assignment operators like "*="). 13 14 The other useful feature is that you can use "." in an expression 15 to refer to the value from the previous expression (just like bc). 16 17 Multiple statements can be separated by semi-colons (;) on a single 18 line (though a single statement can't span multiple lines). 19 20 This calculator is mainly a programmers calculator because it 21 doesn't work in floating point and only deals with integers. 22 23 I wrote this because the standard unix calculator (bc) doesn't 24 offer a useful modulo, it doesn't have left and right shifts and 25 sometimes it is a pain in the ass to use (but I still use bc for 26 things that require any kind of floating point). This program is 27 great when you have to do address calculations and bit-wise 28 masking/shifting as you do when working on kernel type code. It's 29 also handy for doing quick conversions between decimal, hex and ascii 30 (and if you want to see octal for some reason, just put it in the 31 printf string). 32 33 The parser is completely separable and could be spliced into other 34 code very easy. The routine parse_expression() just expects a char 35 pointer and returns the value. Implementing command line editing 36 would be easy using a readline() type of library. 37 38 This isn't the world's best parser or anything, but it works and 39 suits my needs. It faithfully implements C style precedence of 40 operators for: 41 42 ++ -- ~ ! * / % + - << >> < > <= >= == != & ^ | && || 43 44 (in that order, from greatest to least precedence). 45 46 Note: The ! unary operator is a logical negation, not a bitwise 47 negation (if you want bitwise negation, use ~). 48 49 I've been working on adding variables and assignments, and I've 50 just (10/26/94) got it working right and with code I'm not ashamed of. 51 Now you can have variables (no restrictions on length) and assign to 52 them and use them in expressions. Variable names have the usual C 53 rules (i.e. alpha or underscore followed by alpha-numeric and 54 underscore). Variables are initialized to zero and created as needed. 55 You can have any number of variables. Here are some examples: 56 57 x = 5 58 x = y = 10 59 x = (y + 5) * 2 60 (y * 2) + (x & 0xffeef) 61 62 63 Assignment operators also work. The allowable assignment operators 64 are (just as in C): 65 66 +=, -=, *=, /=, %=, &=, ^=, |=, <<=, and >>= 67 68 69 The basic ideas for this code came from the book "Compiler Design 70 in C", by Allen I. Holub, but I've extended them significantly. 71 72 If you find bugs or parsing bogosites, I'd like to know about them 73 so I can fix them. Other comments and criticism of the code are 74 welcome as well. 75 76 Thanks go to Joel Tesler (joel@engr.sgi.com) for adding the ability 77 to pass command line arguments and have pc evaluate them instead of reading 78 from stdin. 79 80 Dominic Giampaolo 81 dbg@be.com (though this was written while I was at sgi) 82 */ 83 #include <stdio.h> 84 #include <stdlib.h> 85 #include <limits.h> 86 #include <string.h> 87 #include <ctype.h> 88 #include "lex.h" 89 90 91 /* 92 * You should #define USE_LONG_LONG if your compiler supports the long long 93 * data type, has strtoull(), and a %lld conversion specifier for printf. 94 * Otherwise just comment out the #define and pc will use plain longs. 95 */ 96 97 #define USE_LONG_LONG 98 99 100 #ifdef USE_LONG_LONG 101 #define LONG long long 102 #define ULONG unsigned long long 103 #define STRTOUL strtoull 104 #define STR1 "%20llu 0x%.16llx signed: %20lld" 105 #define STR2 "%20llu 0x%.16llx" 106 #define STR3 " char: " 107 #define STR4 "%20lu 0x%-16.8lx signed: %20ld" 108 #define STR5 "%20lu 0x%-16.8lx" 109 #else 110 #define LONG long 111 #define ULONG unsigned long 112 #define STRTOUL strtoul 113 #define STR1 "%10lu\t 0x%.8lx\t signed: %10ld" 114 #define STR2 "%10lu\t 0x%.8lx" 115 #define STR3 " char: " 116 #define STR4 STR1 117 #define STR5 STR2 118 #endif 119 120 121 ULONG parse_expression(char *str); /* top-level interface to parser */ 122 ULONG assignment_expr(char **str); /* assignments =, +=, *=, etc */ 123 ULONG do_assignment_operator(char **str, char *var_name); 124 ULONG logical_or_expr(char **str); /* logical OR `||' */ 125 ULONG logical_and_expr(char **str); /* logical AND `&&' */ 126 ULONG or_expr(char **str); /* OR `|' */ 127 ULONG xor_expr(char **str); /* XOR `^' */ 128 ULONG and_expr(char **str); /* AND `&' */ 129 ULONG equality_expr(char **str); /* equality ==, != */ 130 ULONG relational_expr(char **str); /* relational <, >, <=, >= */ 131 ULONG shift_expr(char **str); /* shifts <<, >> */ 132 ULONG add_expression(char **str); /* addition/subtraction +, - */ 133 ULONG term(char **str); /* multiplication/division *,%,/ */ 134 ULONG factor(char **str); /* negation, logical not ~, ! */ 135 ULONG get_value(char **str); 136 int get_var(char *name, ULONG *val); /* external interfaces to vars */ 137 void set_var(char *name, ULONG val); 138 139 void do_input(void); /* reads stdin and calls parser */ 140 char *skipwhite(char *str); /* skip over input white space */ 141 142 143 144 /* 145 * Variables are kept in a simple singly-linked list. Not high 146 * performance, but it's also an extremely small implementation. 147 * 148 * New variables get added to the head of the list. Variables are 149 * never deleted, though it wouldn't be hard to do that. 150 * 151 */ 152 typedef struct variable 153 { 154 char *name; 155 ULONG value; 156 struct variable *next; 157 }variable; 158 159 variable dummy = { NULL, 0L, NULL }; 160 variable *vars=&dummy; 161 162 variable *lookup_var(char *name); 163 variable *add_var(char *name, ULONG value); 164 char *get_var_name(char **input_str); 165 void parse_args(int argc, char *argv[]); 166 int (*set_var_lookup_hook(int (*func)(char *name, ULONG *val))) 167 (char *name, ULONG *val); 168 169 /* 170 * last_result is equal to the result of the last expression and 171 * expressions can refer to it as `.' (just like bc). 172 */ 173 ULONG last_result = 0; 174 175 176 static int 177 special_vars(char *name, ULONG *val) 178 { 179 if (strcmp(name, "time") == 0) 180 *val = (ULONG)time(NULL); 181 else if (strcmp(name, "rand") == 0) 182 *val = (ULONG)rand(); 183 else if (strcmp(name, "dbg") == 0) 184 *val = 0x82969; 185 else 186 return 0; 187 188 return 1; 189 } 190 191 192 int 193 main(int argc, char *argv[]) 194 { 195 196 set_var_lookup_hook(special_vars); 197 198 if (argc > 1) 199 parse_args(argc, argv); 200 else 201 do_input(); 202 203 return 0; 204 } 205 206 /* 207 This function prints the result of the expression. 208 It tries to be smart about printing numbers so it 209 only uses the necessary number of digits. If you 210 have long long (i.e. 64 bit numbers) it's very 211 annoying to have lots of leading zeros when they 212 aren't necessary. By doing the somewhat bizarre 213 casting and comparisons we can determine if a value 214 will fit in a 32 bit quantity and only print that. 215 */ 216 static void 217 print_result(ULONG value) 218 { 219 int i; 220 ULONG ch, shift; 221 222 if ((signed LONG)value < 0) 223 { 224 if ((signed LONG)value < (signed LONG)(LONG_MIN)) 225 printf(STR1, value, value, value); 226 else 227 printf(STR4, (long)value, (long)value, (long)value); 228 } 229 else if ((ULONG)value <= (ULONG)ULONG_MAX) 230 printf(STR5, (long)value, (long)value); 231 else 232 printf(STR2, value, value); 233 234 /* 235 Print any printable character (and print dots for unprintable chars 236 */ 237 printf(STR3); 238 for(i=sizeof(ULONG)-1; i >= 0; i--) 239 { 240 shift = i * 8; 241 ch = ((ULONG)value & ((ULONG)0xff << shift)) >> shift; 242 243 if (isprint((int)ch)) 244 printf("%c", (char)(ch)); 245 else 246 printf("."); 247 } 248 249 printf("\n"); 250 } 251 252 void 253 parse_args(int argc, char *argv[]) 254 { 255 int i, len; 256 char *buff; 257 ULONG value; 258 259 for(i=1, len=0; i < argc; i++) 260 len += strlen(argv[i]); 261 len++; 262 263 buff = malloc(len*sizeof(char)); 264 if (buff == NULL) 265 return; 266 267 buff[0] = '\0'; 268 while (--argc > 0) 269 { 270 strcat(buff, *++argv); 271 } 272 value = parse_expression(buff); 273 274 print_result(value); 275 276 free(buff); 277 } 278 279 void 280 do_input(void) 281 { 282 ULONG value; 283 char buff[256], *ptr; 284 285 while(fgets(buff, 256, stdin) != NULL) 286 { 287 if (buff[0] != '\0' && buff[strlen(buff)-1] == '\n') 288 buff[strlen(buff)-1] = '\0'; /* kill the newline character */ 289 290 for(ptr=buff; isspace(*ptr) && *ptr; ptr++) 291 /* skip whitespace */; 292 293 if (*ptr == '\0') /* hmmm, an empty line, just skip it */ 294 continue; 295 296 value = parse_expression(buff); 297 298 print_result(value); 299 } 300 } 301 302 303 ULONG 304 parse_expression(char *str) 305 { 306 ULONG val; 307 char *ptr = str; 308 309 ptr = skipwhite(ptr); 310 if (*ptr == '\0') 311 return last_result; 312 313 val = assignment_expr(&ptr); 314 315 ptr = skipwhite(ptr); 316 while (*ptr == SEMI_COLON && *ptr != '\0') 317 { 318 ptr++; 319 if (*ptr == '\0') /* reached the end of the string, stop parsing */ 320 continue; 321 322 val = assignment_expr(&ptr); 323 } 324 325 last_result = val; 326 return val; 327 } 328 329 330 ULONG 331 assignment_expr(char **str) 332 { 333 ULONG val; 334 char *orig_str; 335 char *var_name; 336 variable *v; 337 338 *str = skipwhite(*str); 339 orig_str = *str; 340 341 var_name = get_var_name(str); 342 343 *str = skipwhite(*str); 344 if (**str == EQUAL && *(*str+1) != EQUAL) 345 { 346 *str = skipwhite(*str + 1); /* skip the equal sign */ 347 348 val = assignment_expr(str); /* go recursive! */ 349 350 if ((v = lookup_var(var_name)) == NULL) 351 add_var(var_name, val); 352 else 353 v->value = val; 354 } 355 else if (((**str == PLUS || **str == MINUS || **str == OR || 356 **str == TIMES || **str == DIVISION || **str == MODULO || 357 **str == AND || **str == XOR) && *(*str+1) == EQUAL) || 358 strncmp(*str, "<<=", 3) == 0 || strncmp(*str, ">>=", 3) == 0) 359 { 360 val = do_assignment_operator(str, var_name); 361 } 362 else 363 { 364 *str = orig_str; 365 val = logical_or_expr(str); /* no equal sign, just get var value */ 366 367 *str = skipwhite(*str); 368 if (**str == EQUAL) 369 { 370 fprintf(stderr, "Left hand side of expression is not assignable.\n"); 371 } 372 } 373 374 if (var_name) 375 free(var_name); 376 377 return val; 378 } 379 380 381 ULONG 382 do_assignment_operator(char **str, char *var_name) 383 { 384 ULONG val; 385 variable *v; 386 char operator; 387 388 operator = **str; 389 390 if (operator == SHIFT_L || operator == SHIFT_R) 391 *str = skipwhite(*str + 3); 392 else 393 *str = skipwhite(*str + 2); /* skip the assignment operator */ 394 395 val = assignment_expr(str); /* go recursive! */ 396 397 v = lookup_var(var_name); 398 if (v == NULL) 399 { 400 v = add_var(var_name, 0); 401 if (v == NULL) 402 return 0; 403 } 404 405 if (operator == PLUS) 406 v->value += val; 407 else if (operator == MINUS) 408 v->value -= val; 409 else if (operator == AND) 410 v->value &= val; 411 else if (operator == XOR) 412 v->value ^= val; 413 else if (operator == OR) 414 v->value |= val; 415 else if (operator == SHIFT_L) 416 v->value <<= val; 417 else if (operator == SHIFT_R) 418 v->value >>= val; 419 else if (operator == TIMES) 420 v->value *= val; 421 else if (operator == DIVISION) 422 { 423 if (val == 0) /* check for it, but still get the result */ 424 fprintf(stderr, "Divide by zero!\n"); 425 426 v->value /= val; 427 } 428 else if (operator == MODULO) 429 { 430 if (val == 0) /* check for it, but still get the result */ 431 fprintf(stderr, "Modulo by zero!\n"); 432 433 v->value %= val; 434 } 435 else 436 { 437 fprintf(stderr, "Unknown operator: %c\n", operator); 438 v->value = 0; 439 } 440 441 return v->value; 442 } 443 444 445 ULONG 446 logical_or_expr(char **str) 447 { 448 ULONG val, sum = 0; 449 450 *str = skipwhite(*str); 451 452 sum = logical_and_expr(str); 453 454 *str = skipwhite(*str); 455 while(**str == OR && *(*str + 1) == OR) 456 { 457 *str = skipwhite(*str + 2); /* advance over the operator */ 458 459 val = logical_and_expr(str); 460 461 sum = (val || sum); 462 } 463 464 return sum; 465 } 466 467 468 469 ULONG 470 logical_and_expr(char **str) 471 { 472 ULONG val, sum = 0; 473 474 *str = skipwhite(*str); 475 476 sum = or_expr(str); 477 478 *str = skipwhite(*str); 479 while(**str == AND && *(*str + 1) == AND) 480 { 481 *str = skipwhite(*str + 2); /* advance over the operator */ 482 483 val = or_expr(str); 484 485 sum = (val && sum); 486 } 487 488 return sum; 489 } 490 491 492 ULONG 493 or_expr(char **str) 494 { 495 ULONG val, sum = 0; 496 497 *str = skipwhite(*str); 498 499 sum = xor_expr(str); 500 501 *str = skipwhite(*str); 502 while(**str == OR && *(*str+1) != OR) 503 { 504 *str = skipwhite(*str + 1); /* advance over the operator */ 505 506 val = xor_expr(str); 507 508 sum |= val; 509 } 510 511 return sum; 512 } 513 514 515 516 ULONG 517 xor_expr(char **str) 518 { 519 ULONG val, sum = 0; 520 521 *str = skipwhite(*str); 522 523 sum = and_expr(str); 524 525 *str = skipwhite(*str); 526 while(**str == XOR) 527 { 528 *str = skipwhite(*str + 1); /* advance over the operator */ 529 530 val = and_expr(str); 531 532 sum ^= val; 533 } 534 535 return sum; 536 } 537 538 539 540 ULONG 541 and_expr(char **str) 542 { 543 ULONG val, sum = 0; 544 545 *str = skipwhite(*str); 546 547 sum = equality_expr(str); 548 549 *str = skipwhite(*str); 550 while(**str == AND && *(*str+1) != AND) 551 { 552 *str = skipwhite(*str + 1); /* advance over the operator */ 553 554 val = equality_expr(str); 555 556 sum &= val; 557 } 558 559 return sum; 560 } 561 562 563 ULONG 564 equality_expr(char **str) 565 { 566 ULONG val, sum = 0; 567 char op; 568 569 *str = skipwhite(*str); 570 571 sum = relational_expr(str); 572 573 *str = skipwhite(*str); 574 while((**str == EQUAL && *(*str+1) == EQUAL) || 575 (**str == BANG && *(*str+1) == EQUAL)) 576 { 577 op = **str; 578 579 *str = skipwhite(*str + 2); /* advance over the operator */ 580 581 val = relational_expr(str); 582 583 if (op == EQUAL) 584 sum = (sum == val); 585 else if (op == BANG) 586 sum = (sum != val); 587 } 588 589 return sum; 590 } 591 592 593 ULONG 594 relational_expr(char **str) 595 { 596 ULONG val, sum = 0; 597 char op, equal_to=0; 598 599 *str = skipwhite(*str); 600 601 sum = shift_expr(str); 602 603 *str = skipwhite(*str); 604 while(**str == LESS_THAN || **str == GREATER_THAN) 605 { 606 equal_to = 0; 607 op = **str; 608 609 if (*(*str+1) == EQUAL) 610 { 611 equal_to = 1; 612 *str = *str+1; /* skip initial operator */ 613 } 614 615 *str = skipwhite(*str + 1); /* advance over the operator */ 616 617 val = shift_expr(str); 618 619 /* 620 Notice that we do the relational expressions as signed 621 comparisons. This is because of expressions like: 622 0 > -1 623 which would not return the expected value if we did the 624 comparison as unsigned. This may not always be the 625 desired behavior, but aside from adding casting to epxressions, 626 there isn't much of a way around it. 627 */ 628 if (op == LESS_THAN && equal_to == 0) 629 sum = ((LONG)sum < (LONG)val); 630 else if (op == LESS_THAN && equal_to == 1) 631 sum = ((LONG)sum <= (LONG)val); 632 else if (op == GREATER_THAN && equal_to == 0) 633 sum = ((LONG)sum > (LONG)val); 634 else if (op == GREATER_THAN && equal_to == 1) 635 sum = ((LONG)sum >= (LONG)val); 636 } 637 638 return sum; 639 } 640 641 642 ULONG 643 shift_expr(char **str) 644 { 645 ULONG val, sum = 0; 646 char op; 647 648 *str = skipwhite(*str); 649 650 sum = add_expression(str); 651 652 *str = skipwhite(*str); 653 while((strncmp(*str, "<<", 2) == 0) || (strncmp(*str, ">>", 2) == 0)) 654 { 655 op = **str; 656 657 *str = skipwhite(*str + 2); /* advance over the operator */ 658 659 val = add_expression(str); 660 661 if (op == SHIFT_L) 662 sum <<= val; 663 else if (op == SHIFT_R) 664 sum >>= val; 665 } 666 667 return sum; 668 } 669 670 671 672 673 ULONG 674 add_expression(char **str) 675 { 676 ULONG val, sum = 0; 677 char op; 678 679 *str = skipwhite(*str); 680 681 sum = term(str); 682 683 *str = skipwhite(*str); 684 while(**str == PLUS || **str == MINUS) 685 { 686 op = **str; 687 688 *str = skipwhite(*str + 1); /* advance over the operator */ 689 690 val = term(str); 691 692 if (op == PLUS) 693 sum += val; 694 else if (op == MINUS) 695 sum -= val; 696 } 697 698 return sum; 699 } 700 701 702 703 704 ULONG 705 term(char **str) 706 { 707 ULONG val, sum = 0; 708 char op; 709 710 711 sum = factor(str); 712 *str = skipwhite(*str); 713 714 /* 715 * We're at the bottom of the parse. At this point we either have 716 * an operator or we're through with this string. Otherwise it's 717 * an error and we print a message. 718 */ 719 if (**str != TIMES && **str != DIVISION && **str != MODULO && 720 **str != PLUS && **str != MINUS && **str != OR && 721 **str != AND && **str != XOR && **str != BANG && 722 **str != NEGATIVE && **str != TWIDDLE && **str != RPAREN && 723 **str != LESS_THAN && **str != GREATER_THAN && **str != SEMI_COLON && 724 strncmp(*str, "<<", 2) != 0 && strncmp(*str, ">>", 2) && 725 **str != EQUAL && **str != '\0') 726 { 727 fprintf(stderr, "Parsing stopped: unknown operator %s\n", *str); 728 return sum; 729 } 730 731 while(**str == TIMES || **str == DIVISION || **str == MODULO) 732 { 733 op = **str; 734 *str = skipwhite(*str + 1); 735 val = factor(str); 736 737 if (op == TIMES) 738 sum *= val; 739 else if (op == DIVISION) 740 { 741 if (val == 0) 742 fprintf(stderr, "Divide by zero!\n"); 743 744 sum /= val; 745 } 746 else if (op == MODULO) 747 { 748 if (val == 0) 749 fprintf(stderr, "Modulo by zero!\n"); 750 751 sum %= val; 752 } 753 } 754 755 return sum; 756 } 757 758 759 ULONG 760 factor(char **str) 761 { 762 ULONG val=0; 763 char op = NOTHING, have_special=0; 764 char *var_name, *var_name_ptr; 765 variable *v; 766 767 if (**str == NEGATIVE || **str == PLUS || **str == TWIDDLE || **str == BANG) 768 { 769 op = **str; /* must be a unary op */ 770 771 if ((op == NEGATIVE && *(*str + 1) == NEGATIVE) || /* look for ++/-- */ 772 (op == PLUS && *(*str + 1) == PLUS)) 773 { 774 *str = *str + 1; 775 have_special = 1; 776 } 777 778 *str = skipwhite(*str + 1); 779 var_name_ptr = *str; /* save where the varname should be */ 780 } 781 782 val = get_value(str); 783 784 *str = skipwhite(*str); 785 786 /* 787 * Now is the time to actually do the unary operation if one 788 * was present. 789 */ 790 if (have_special) /* we've got a ++ or -- */ 791 { 792 var_name = get_var_name(&var_name_ptr); 793 if (var_name == NULL) 794 { 795 fprintf(stderr, "Can only use ++/-- on variables.\n"); 796 return val; 797 } 798 if ((v = lookup_var(var_name)) == NULL) 799 { 800 v = add_var(var_name, 0); 801 if (v == NULL) 802 return val; 803 } 804 free(var_name); 805 806 if (op == PLUS) 807 val = ++v->value; 808 else 809 val = --v->value; 810 } 811 else /* normal unary operator */ 812 { 813 switch(op) 814 { 815 case NEGATIVE : val *= -1; 816 break; 817 818 case BANG : val = !val; 819 break; 820 821 case TWIDDLE : val = ~val; 822 break; 823 } 824 } 825 826 return val; 827 } 828 829 830 831 ULONG 832 get_value(char **str) 833 { 834 ULONG val; 835 char *var_name; 836 variable *v; 837 838 if (**str == SINGLE_QUOTE) /* a character constant */ 839 { 840 unsigned int i; 841 842 *str = *str + 1; /* advance over the leading quote */ 843 val = 0; 844 for(i=0; **str && **str != SINGLE_QUOTE && i < sizeof(LONG); *str+=1,i++) 845 { 846 if (**str == '\\') /* escape the next char */ 847 *str += 1; 848 849 val <<= 8; 850 val |= (ULONG)((unsigned)**str); 851 } 852 853 if (**str != SINGLE_QUOTE) /* constant must have been too long */ 854 { 855 fprintf(stderr, "Warning: character constant not terminated or too " 856 "long (max len == %ld bytes)\n", sizeof(LONG)); 857 while(**str && **str != SINGLE_QUOTE) 858 *str += 1; 859 } 860 else if (**str != '\0') 861 *str += 1; 862 } 863 else if (isdigit(**str)) /* a regular number */ 864 { 865 val = STRTOUL(*str, str, 0); 866 867 *str = skipwhite(*str); 868 } 869 else if (**str == USE_LAST_RESULT) /* a `.' meaning use the last result */ 870 { 871 val = last_result; 872 *str = skipwhite(*str+1); 873 } 874 else if (**str == LPAREN) /* a parenthesized expression */ 875 { 876 *str = skipwhite(*str + 1); 877 878 val = assignment_expr(str); /* start at top and come back down */ 879 880 if (**str == RPAREN) 881 *str = *str + 1; 882 else 883 fprintf(stderr, "Mismatched paren's\n"); 884 } 885 else if (isalpha(**str) || **str == '_') /* a variable name */ 886 { 887 if ((var_name = get_var_name(str)) == NULL) 888 { 889 fprintf(stderr, "Can't get var name!\n"); 890 return 0; 891 } 892 893 if (get_var(var_name, &val) == 0) 894 { 895 fprintf(stderr, "No such variable: %s (assigning value of zero)\n", 896 var_name); 897 898 val = 0; 899 v = add_var(var_name, val); 900 if (v == NULL) 901 return 0; 902 } 903 904 *str = skipwhite(*str); 905 if (strncmp(*str, "++", 2) == 0 || strncmp(*str, "--", 2) == 0) 906 { 907 if ((v = lookup_var(var_name)) != NULL) 908 { 909 val = v->value; 910 if (**str == '+') 911 v->value++; 912 else 913 v->value--; 914 *str = *str + 2; 915 } 916 else 917 { 918 fprintf(stderr, "%s is a read-only variable\n", var_name); 919 } 920 } 921 922 free(var_name); 923 } 924 else 925 { 926 fprintf(stderr, "Expecting left paren, unary op, constant or variable."); 927 fprintf(stderr, " Got: `%s'\n", *str); 928 return 0; 929 } 930 931 932 return val; 933 } 934 935 936 /* 937 * Here are the functions that manipulate variables. 938 */ 939 940 /* 941 this is a hook function for external read-only 942 variables. If it is set and we don't find a 943 variable name in our name space, we call it to 944 look for the variable. If it finds the name, it 945 fills in val and returns 1. If it returns 0, it 946 didn't find the variable. 947 */ 948 static int (*external_var_lookup)(char *name, ULONG *val) = NULL; 949 950 /* 951 this very ugly function declaration is for the function 952 set_var_lookup_hook which accepts one argument, "func", which 953 is a pointer to a function that returns int (and accepts a 954 char * and ULONG *). set_var_lookup_hook returns a pointer to 955 a function that returns int and accepts char * and ULONG *. 956 957 It's very ugly looking but fairly basic in what it does. You 958 pass in a function to set as the variable name lookup up hook 959 and it passes back to you the old function (which you should 960 call if it is non-null and your function fails to find the 961 variable name). 962 */ 963 int (*set_var_lookup_hook(int (*func)(char *name, ULONG *val)))(char *name, ULONG *val) 964 { 965 int (*old_func)(char *name, ULONG *val) = external_var_lookup; 966 967 external_var_lookup = func; 968 969 return old_func; 970 } 971 972 973 variable * 974 lookup_var(char *name) 975 { 976 variable *v; 977 978 for(v=vars; v; v=v->next) 979 if (v->name && strcmp(v->name, name) == 0) 980 return v; 981 982 return NULL; 983 } 984 985 986 variable * 987 add_var(char *name, ULONG value) 988 { 989 variable *v; 990 ULONG tmp; 991 992 /* first make sure this isn't an external read-only variable */ 993 if (external_var_lookup) 994 if (external_var_lookup(name, &tmp) != 0) 995 { 996 fprintf(stderr, "Can't assign/create %s, it is a read-only var\n",name); 997 return NULL; 998 } 999 1000 1001 v = (variable *)malloc(sizeof(variable)); 1002 if (v == NULL) 1003 { 1004 fprintf(stderr, "No memory to add variable: %s\n", name); 1005 return NULL; 1006 } 1007 1008 v->name = strdup(name); 1009 v->value = value; 1010 v->next = vars; 1011 1012 vars = v; /* set head of list to the new guy */ 1013 1014 return v; 1015 } 1016 1017 /* 1018 This routine and the companion get_var() are external 1019 interfaces to the variable manipulation routines. 1020 */ 1021 void 1022 set_var(char *name, ULONG val) 1023 { 1024 variable *v; 1025 1026 v = lookup_var(name); 1027 if (v != NULL) 1028 v->value = val; 1029 else 1030 add_var(name, val); 1031 } 1032 1033 1034 /* 1035 This function returns 1 on success of finding 1036 a variable and 0 on failure to find a variable. 1037 If a variable is found, val is filled with its 1038 value. 1039 */ 1040 int 1041 get_var(char *name, ULONG *val) 1042 { 1043 variable *v; 1044 1045 v = lookup_var(name); 1046 if (v != NULL) 1047 { 1048 *val = v->value; 1049 return 1; 1050 } 1051 else if (external_var_lookup != NULL) 1052 { 1053 return external_var_lookup(name, val); 1054 } 1055 1056 return 0; 1057 } 1058 1059 #define DEFAULT_LEN 32 1060 1061 char * 1062 get_var_name(char **str) 1063 { 1064 int i, len=DEFAULT_LEN; 1065 char *buff; 1066 1067 if (isalpha(**str) == 0 && **str != '_') 1068 return NULL; 1069 1070 buff = (char *)malloc(len*sizeof(char)); 1071 if (buff == NULL) 1072 return NULL; 1073 1074 /* 1075 * First get the variable name 1076 */ 1077 i=0; 1078 while(**str && (isalnum(**str) || **str == '_')) 1079 { 1080 if (i >= len-1) 1081 { 1082 len *= 2; 1083 buff = (char *)realloc(buff, len); 1084 if (buff == NULL) 1085 return NULL; 1086 } 1087 1088 buff[i++] = **str; 1089 *str = *str+1; 1090 } 1091 1092 buff[i] = '\0'; /* null terminate */ 1093 1094 while (isalnum(**str) || **str == '_') /* skip over any remaining junk */ 1095 *str = *str+1; 1096 1097 return buff; 1098 } 1099 1100 1101 1102 char * 1103 skipwhite(char *str) 1104 { 1105 if (str == NULL) 1106 return NULL; 1107 1108 while(*str && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\f')) 1109 str++; 1110 1111 return str; 1112 } 1113