xref: /haiku/src/bin/pc/pc.c (revision 91ec679f17eac49beb7356ef0eaa39df099fd639)
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