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
special_vars(char * name,ULONG * val)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
main(int argc,char * argv[])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
print_result(ULONG value)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
parse_args(int argc,char * argv[])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
do_input(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
parse_expression(char * str)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
assignment_expr(char ** str)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
do_assignment_operator(char ** str,char * var_name)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
logical_or_expr(char ** str)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
logical_and_expr(char ** str)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
or_expr(char ** str)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
xor_expr(char ** str)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
and_expr(char ** str)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
equality_expr(char ** str)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
relational_expr(char ** str)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
shift_expr(char ** str)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
add_expression(char ** str)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
term(char ** str)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
factor(char ** str)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
get_value(char ** str)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 */
set_var_lookup_hook(int (* func)(char * name,ULONG * val))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 *
lookup_var(char * name)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 *
add_var(char * name,ULONG value)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
set_var(char * name,ULONG val)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
get_var(char * name,ULONG * val)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 *
get_var_name(char ** str)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 *
skipwhite(char * str)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