1/*
2               PC -- programmer's calculator.
3
4   This program implements a simple recursive descent parser that
5understands pretty much all standard C language math and logic
6expressions.  It handles the usual add, subtract, multiply, divide,
7and mod sort of stuff.  It can also deal with logical/relational
8operations and expressions.  The logic/relational operations AND, OR,
9NOT, and EXCLUSIVE OR, &&, ||, ==, !=, <, >, <=, and >= are all
10supported.  It also handles parens and nested expresions as well as
11left and right shifts.  There are variables and assignments (as well
12as assignment operators like "*=").
13
14   The other useful feature is that you can use "." in an expression
15to refer to the value from the previous expression (just like bc).
16
17   Multiple statements can be separated by semi-colons (;) on a single
18line (though a single statement can't span multiple lines).
19
20   This calculator is mainly a programmers calculator because it
21doesn't work in floating point and only deals with integers.
22
23   I wrote this because the standard unix calculator (bc) doesn't
24offer a useful modulo, it doesn't have left and right shifts and
25sometimes it is a pain in the ass to use (but I still use bc for
26things that require any kind of floating point).  This program is
27great when you have to do address calculations and bit-wise
28masking/shifting as you do when working on kernel type code.  It's
29also 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
31printf string).
32
33   The parser is completely separable and could be spliced into other
34code very easy.  The routine parse_expression() just expects a char
35pointer and returns the value.  Implementing command line editing
36would be easy using a readline() type of library.
37
38   This isn't the world's best parser or anything, but it works and
39suits my needs.  It faithfully implements C style precedence of
40operators 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
47negation (if you want bitwise negation, use ~).
48
49   I've been working on adding variables and assignments, and I've
50just (10/26/94) got it working right and with code I'm not ashamed of.
51Now you can have variables (no restrictions on length) and assign to
52them and use them in expressions.  Variable names have the usual C
53rules (i.e. alpha or underscore followed by alpha-numeric and
54underscore).  Variables are initialized to zero and created as needed.
55You 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
64are (just as in C):
65
66       +=, -=, *=, /=, %=, &=, ^=, |=, <<=, and >>=
67
68
69   The basic ideas for this code came from the book "Compiler Design
70in 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
73so I can fix them.  Other comments and criticism of the code are
74welcome as well.
75
76   Thanks go to Joel Tesler (joel@engr.sgi.com) for adding the ability
77to pass command line arguments and have pc evaluate them instead of reading
78from 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
121ULONG parse_expression(char *str);   /* top-level interface to parser */
122ULONG assignment_expr(char **str);   /* assignments =, +=, *=, etc */
123ULONG do_assignment_operator(char **str, char *var_name);
124ULONG logical_or_expr(char **str);   /* logical OR  `||' */
125ULONG logical_and_expr(char **str);  /* logical AND  `&&' */
126ULONG or_expr(char **str);           /* OR  `|' */
127ULONG xor_expr(char **str);          /* XOR `^' */
128ULONG and_expr(char **str);          /* AND `&' */
129ULONG equality_expr(char **str);     /* equality ==, != */
130ULONG relational_expr(char **str);   /* relational <, >, <=, >= */
131ULONG shift_expr(char **str);        /* shifts <<, >> */
132ULONG add_expression(char **str);    /* addition/subtraction +, - */
133ULONG term(char **str);              /* multiplication/division *,%,/ */
134ULONG factor(char **str);            /* negation, logical not ~, ! */
135ULONG get_value(char **str);
136int   get_var(char *name, ULONG *val); /* external interfaces to vars */
137void  set_var(char *name, ULONG val);
138
139void  do_input(void);                /* reads stdin and calls parser */
140char *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 */
152typedef struct variable
153{
154  char  *name;
155  ULONG  value;
156  struct variable *next;
157}variable;
158
159variable dummy = { NULL, 0L, NULL };
160variable *vars=&dummy;
161
162variable *lookup_var(char *name);
163variable *add_var(char *name, ULONG value);
164char     *get_var_name(char **input_str);
165void      parse_args(int argc, char *argv[]);
166int (*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 */
173ULONG last_result = 0;
174
175
176static int
177special_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
192int
193main(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*/
216static void
217print_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
252void
253parse_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
279void
280do_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
303ULONG
304parse_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
330ULONG
331assignment_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
381ULONG
382do_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
445ULONG
446logical_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
469ULONG
470logical_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
492ULONG
493or_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
516ULONG
517xor_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
540ULONG
541and_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
563ULONG
564equality_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
593ULONG
594relational_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
642ULONG
643shift_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
673ULONG
674add_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
704ULONG
705term(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
759ULONG
760factor(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
831ULONG
832get_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*/
948static 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*/
963int (*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
973variable *
974lookup_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
986variable *
987add_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*/
1021void
1022set_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*/
1040int
1041get_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
1061char *
1062get_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
1102char *
1103skipwhite(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