]> git.ipfire.org Git - thirdparty/bash.git/blob - expr.c
0930789d6a5631c078bc58e7e1dc76ef00af56d2
[thirdparty/bash.git] / expr.c
1 /* expr.c -- arithmetic expression evaluation. */
2
3 /* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 1, or (at your option)
10 any later version.
11
12 Bash is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash; see the file COPYING. If not, write to the Free
19 Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 /*
22 All arithmetic is done as long integers with no checking for overflow
23 (though division by 0 is caught and flagged as an error).
24
25 The following operators are handled, grouped into a set of levels in
26 order of decreasing precedence.
27
28 "-", "+" [(unary operators)]
29 "!", "~"
30 "*", "/", "%"
31 "+", "-"
32 "<<", ">>"
33 "<=", ">=", "<", ">"
34 "==", "!="
35 "&"
36 "^"
37 "|"
38 "&&"
39 "||"
40 "expr ? expr : expr"
41 "=", "*=", "/=", "%=",
42 "+=", "-=", "<<=", ">>=",
43 "&=", "^=", "|="
44
45 (Note that most of these operators have special meaning to bash, and an
46 entire expression should be quoted, e.g. "a=$a+1" or "a=a+1" to ensure
47 that it is passed intact to the evaluator when using `let'. When using
48 the $[] or $(( )) forms, the text between the `[' and `]' or `((' and `))'
49 is treated as if in double quotes.)
50
51 Sub-expressions within parentheses have a precedence level greater than
52 all of the above levels and are evaluated first. Within a single prece-
53 dence group, evaluation is left-to-right, except for the arithmetic
54 assignment operator (`='), which is evaluated right-to-left (as in C).
55
56 The expression evaluator returns the value of the expression (assignment
57 statements have as a value what is returned by the RHS). The `let'
58 builtin, on the other hand, returns 0 if the last expression evaluates to
59 a non-zero, and 1 otherwise.
60
61 Implementation is a recursive-descent parser.
62
63 Chet Ramey
64 chet@ins.CWRU.Edu
65 */
66
67 #include "config.h"
68
69 #include <stdio.h>
70 #include "bashansi.h"
71 #if defined (HAVE_UNISTD_H)
72 # include <unistd.h>
73 #endif
74
75 #include "shell.h"
76
77 /* Because of the $((...)) construct, expressions may include newlines.
78 Here is a macro which accepts newlines, tabs and spaces as whitespace. */
79 #define cr_whitespace(c) (whitespace(c) || ((c) == '\n'))
80
81 /* Size be which the expression stack grows when neccessary. */
82 #define EXPR_STACK_GROW_SIZE 10
83
84 /* Maximum amount of recursion allowed. This prevents a non-integer
85 variable such as "num=num+2" from infinitely adding to itself when
86 "let num=num+2" is given. */
87 #define MAX_EXPR_RECURSION_LEVEL 1024
88
89 /* The Tokens. Singing "The Lion Sleeps Tonight". */
90
91 #define EQEQ 1 /* "==" */
92 #define NEQ 2 /* "!=" */
93 #define LEQ 3 /* "<=" */
94 #define GEQ 4 /* ">=" */
95 #define STR 5 /* string */
96 #define NUM 6 /* number */
97 #define LAND 7 /* "&&" Logical AND */
98 #define LOR 8 /* "||" Logical OR */
99 #define LSH 9 /* "<<" Left SHift */
100 #define RSH 10 /* ">>" Right SHift */
101 #define OP_ASSIGN 11 /* op= expassign as in Posix.2 */
102 #define COND 12
103 #define EQ '='
104 #define GT '>'
105 #define LT '<'
106 #define PLUS '+'
107 #define MINUS '-'
108 #define MUL '*'
109 #define DIV '/'
110 #define MOD '%'
111 #define NOT '!'
112 #define LPAR '('
113 #define RPAR ')'
114 #define BAND '&' /* Bitwise AND */
115 #define BOR '|' /* Bitwise OR. */
116 #define BXOR '^' /* Bitwise eXclusive OR. */
117 #define BNOT '~' /* Bitwise NOT; Two's complement. */
118 #define QUES '?'
119 #define COL ':'
120
121 static char *expression; /* The current expression */
122 static char *tp; /* token lexical position */
123 static char *lasttp; /* pointer to last token position */
124 static int curtok; /* the current token */
125 static int lasttok; /* the previous token */
126 static int assigntok; /* the OP in OP= */
127 static char *tokstr; /* current token string */
128 static int tokval; /* current token value */
129 static int noeval; /* set to 1 if no assignment to be done */
130 static procenv_t evalbuf;
131
132 static void readtok (); /* lexical analyzer */
133 static long subexpr (), expassign (), exp0 (), exp1 (), exp2 (), exp3 (),
134 exp4 (), exp5 (), expshift (), expland (), explor (),
135 expband (), expbor (), expbxor (), expcond ();
136 static long strlong ();
137 static void evalerror ();
138
139 /* A structure defining a single expression context. */
140 typedef struct {
141 int curtok, lasttok;
142 char *expression, *tp;
143 int tokval;
144 char *tokstr;
145 } EXPR_CONTEXT;
146
147 /* Global var which contains the stack of expression contexts. */
148 static EXPR_CONTEXT **expr_stack;
149 static int expr_depth; /* Location in the stack. */
150 static int expr_stack_size; /* Number of slots already allocated. */
151
152 extern char *this_command_name;
153
154 /* Push and save away the contents of the globals describing the
155 current expression context. */
156 static void
157 pushexp ()
158 {
159 EXPR_CONTEXT *context;
160
161 if (expr_depth >= MAX_EXPR_RECURSION_LEVEL)
162 evalerror ("expression recursion level exceeded");
163
164 if (expr_depth >= expr_stack_size)
165 {
166 expr_stack = (EXPR_CONTEXT **)
167 xrealloc (expr_stack, (expr_stack_size += EXPR_STACK_GROW_SIZE)
168 * sizeof (EXPR_CONTEXT *));
169 }
170
171 context = (EXPR_CONTEXT *)xmalloc (sizeof (EXPR_CONTEXT));
172
173 context->curtok = curtok;
174 context->lasttok = lasttok;
175 context->expression = expression;
176 context->tp = tp;
177 context->tokval = tokval;
178 context->tokstr = tokstr;
179 expr_stack[expr_depth++] = context;
180 }
181
182 /* Pop the the contents of the expression context stack into the
183 globals describing the current expression context. */
184 static void
185 popexp ()
186 {
187 EXPR_CONTEXT *context;
188
189 if (expr_depth == 0)
190 evalerror ("recursion stack underflow");
191
192 context = expr_stack[--expr_depth];
193 curtok = context->curtok;
194 lasttok = context->lasttok;
195 expression = context->expression;
196 tp = context->tp;
197 tokval = context->tokval;
198 tokstr = context->tokstr;
199 free (context);
200 }
201
202 /* Evaluate EXPR, and return the arithmetic result. If VALIDP is
203 non-null, a zero is stored into the location to which it points
204 if the expression is invalid, non-zero otherwise. If a non-zero
205 value is returned in *VALIDP, the return value of evalexp() may
206 be used.
207
208 The `while' loop after the longjmp is caught relies on the above
209 implementation of pushexp and popexp leaving in expr_stack[0] the
210 values that the variables had when the program started. That is,
211 the first things saved are the initial values of the variables that
212 were assigned at program startup or by the compiler. Therefore, it is
213 safe to let the loop terminate when expr_depth == 0, without freeing up
214 any of the expr_depth[0] stuff. */
215 long
216 evalexp (expr, validp)
217 char *expr;
218 int *validp;
219 {
220 long val;
221 #if 0
222 procenv_t old_evalbuf;
223 #endif
224
225 val = 0L;
226
227 #if 0
228 /* Save the value of evalbuf to protect it around possible recursive
229 calls to evalexp (). */
230 COPY_PROCENV (evalbuf, old_evalbuf);
231 #endif
232
233 if (setjmp (evalbuf))
234 {
235 FREE (tokstr);
236 FREE (expression);
237 tokstr = expression = (char *)NULL;
238
239 while (--expr_depth > 0)
240 {
241 if (expr_stack[expr_depth]->tokstr)
242 free (expr_stack[expr_depth]->tokstr);
243
244 if (expr_stack[expr_depth]->expression)
245 free (expr_stack[expr_depth]->expression);
246
247 free (expr_stack[expr_depth]);
248 }
249 free (expr_stack[expr_depth]); /* free the allocated EXPR_CONTEXT */
250
251 if (validp)
252 *validp = 0;
253 return (0L);
254 }
255
256 val = subexpr (expr);
257
258 #if 0
259 /* Restore the value of evalbuf so that any subsequent longjmp calls
260 will have a valid location to jump to. */
261 COPY_PROCENV (old_evalbuf, evalbuf);
262 #endif
263
264 if (validp)
265 *validp = 1;
266
267 return (val);
268 }
269
270 static long
271 subexpr (expr)
272 char *expr;
273 {
274 long val;
275 char *p;
276
277 for (p = expr; p && *p && cr_whitespace (*p); p++)
278 ;
279
280 if (p == NULL || *p == '\0')
281 return (0L);
282
283 pushexp ();
284 curtok = lasttok = 0;
285 expression = savestring (expr);
286 tp = expression;
287
288 tokstr = (char *)NULL;
289 tokval = 0L;
290
291 readtok ();
292
293 val = expassign ();
294
295 if (curtok != 0)
296 evalerror ("syntax error in expression");
297
298 FREE (tokstr);
299 FREE (expression);
300
301 popexp ();
302
303 return val;
304 }
305
306 /* Bind/create a shell variable with the name LHS to the RHS.
307 This creates or modifies a variable such that it is an integer.
308
309 This should really be in variables.c, but it is here so that all of the
310 expression evaluation stuff is localized. Since we don't want any
311 recursive evaluation from bind_variable() (possible without this code,
312 since bind_variable() calls the evaluator for variables with the integer
313 attribute set), we temporarily turn off the integer attribute for each
314 variable we set here, then turn it back on after binding as necessary. */
315
316 void
317 bind_int_variable (lhs, rhs)
318 char *lhs, *rhs;
319 {
320 register SHELL_VAR *v;
321 int isint = 0;
322
323 v = find_variable (lhs);
324 if (v)
325 {
326 isint = integer_p (v);
327 v->attributes &= ~att_integer;
328 }
329
330 v = bind_variable (lhs, rhs);
331 if (isint)
332 v->attributes |= att_integer;
333 }
334
335 static long
336 expassign ()
337 {
338 register long value;
339 char *lhs, *rhs;
340
341 value = expcond ();
342 if (curtok == EQ || curtok == OP_ASSIGN)
343 {
344 int special, op;
345 long lvalue;
346
347 special = curtok == OP_ASSIGN;
348
349 if (lasttok != STR)
350 evalerror ("attempted assignment to non-variable");
351
352 if (special)
353 {
354 op = assigntok; /* a OP= b */
355 lvalue = value;
356 }
357
358 lhs = savestring (tokstr);
359 readtok ();
360 value = expassign ();
361
362 if (special)
363 {
364 switch (op)
365 {
366 case MUL:
367 lvalue *= value;
368 break;
369 case DIV:
370 lvalue /= value;
371 break;
372 case MOD:
373 lvalue %= value;
374 break;
375 case PLUS:
376 lvalue += value;
377 break;
378 case MINUS:
379 lvalue -= value;
380 break;
381 case LSH:
382 lvalue <<= value;
383 break;
384 case RSH:
385 lvalue >>= value;
386 break;
387 case BAND:
388 lvalue &= value;
389 break;
390 case BOR:
391 lvalue |= value;
392 break;
393 default:
394 free (lhs);
395 evalerror ("bug: bad expassign token");
396 break;
397 }
398 value = lvalue;
399 }
400
401 rhs = itos (value);
402 if (noeval == 0)
403 bind_int_variable (lhs, rhs);
404 free (rhs);
405 free (lhs);
406 FREE (tokstr);
407 tokstr = (char *)NULL; /* For freeing on errors. */
408 }
409 return (value);
410 }
411
412 /* Conditional expression (expr?expr:expr) */
413 static long
414 expcond ()
415 {
416 long cval, val1, val2, rval;
417 int set_noeval;
418
419 set_noeval = 0;
420 rval = cval = explor ();
421 if (curtok == QUES) /* found conditional expr */
422 {
423 readtok ();
424 if (curtok == 0 || curtok == COL)
425 evalerror ("expression expected");
426 if (cval == 0)
427 {
428 set_noeval = 1;
429 noeval++;
430 }
431 #if 0
432 val1 = explor ();
433 #else
434 val1 = expassign ();
435 #endif
436 if (set_noeval)
437 noeval--;
438 if (curtok != COL)
439 evalerror ("`:' expected for conditional expression");
440 readtok ();
441 if (curtok == 0)
442 evalerror ("expression expected");
443 set_noeval = 0;
444 if (cval)
445 {
446 set_noeval = 1;
447 noeval++;
448 }
449 val2 = explor ();
450 if (set_noeval)
451 noeval--;
452 rval = cval ? val1 : val2;
453 lasttok = COND;
454 }
455 return rval;
456 }
457
458 /* Logical OR. */
459 static long
460 explor ()
461 {
462 register long val1, val2;
463 int set_noeval;
464
465 val1 = expland ();
466
467 while (curtok == LOR)
468 {
469 set_noeval = 0;
470 if (val1 != 0)
471 {
472 noeval++;
473 set_noeval = 1;
474 }
475 readtok ();
476 val2 = expland ();
477 if (set_noeval)
478 noeval--;
479 val1 = val1 || val2;
480 lasttok = LOR;
481 }
482
483 return (val1);
484 }
485
486 /* Logical AND. */
487 static long
488 expland ()
489 {
490 register long val1, val2;
491 int set_noeval;
492
493 val1 = expbor ();
494
495 while (curtok == LAND)
496 {
497 set_noeval = 0;
498 if (val1 == 0)
499 {
500 set_noeval = 1;
501 noeval++;
502 }
503 readtok ();
504 val2 = expbor ();
505 if (set_noeval)
506 noeval--;
507 val1 = val1 && val2;
508 lasttok = LAND;
509 }
510
511 return (val1);
512 }
513
514 /* Bitwise OR. */
515 static long
516 expbor ()
517 {
518 register long val1, val2;
519
520 val1 = expbxor ();
521
522 while (curtok == BOR)
523 {
524 readtok ();
525 val2 = expbxor ();
526 val1 = val1 | val2;
527 }
528
529 return (val1);
530 }
531
532 /* Bitwise XOR. */
533 static long
534 expbxor ()
535 {
536 register long val1, val2;
537
538 val1 = expband ();
539
540 while (curtok == BXOR)
541 {
542 readtok ();
543 val2 = expband ();
544 val1 = val1 ^ val2;
545 }
546
547 return (val1);
548 }
549
550 /* Bitwise AND. */
551 static long
552 expband ()
553 {
554 register long val1, val2;
555
556 val1 = exp5 ();
557
558 while (curtok == BAND)
559 {
560 readtok ();
561 val2 = exp5 ();
562 val1 = val1 & val2;
563 }
564
565 return (val1);
566 }
567
568 static long
569 exp5 ()
570 {
571 register long val1, val2;
572
573 val1 = exp4 ();
574
575 while ((curtok == EQEQ) || (curtok == NEQ))
576 {
577 int op = curtok;
578
579 readtok ();
580 val2 = exp4 ();
581 if (op == EQEQ)
582 val1 = (val1 == val2);
583 else if (op == NEQ)
584 val1 = (val1 != val2);
585 }
586 return (val1);
587 }
588
589 static long
590 exp4 ()
591 {
592 register long val1, val2;
593
594 val1 = expshift ();
595 while ((curtok == LEQ) ||
596 (curtok == GEQ) ||
597 (curtok == LT) ||
598 (curtok == GT))
599 {
600 int op = curtok;
601
602 readtok ();
603 val2 = expshift ();
604
605 if (op == LEQ)
606 val1 = val1 <= val2;
607 else if (op == GEQ)
608 val1 = val1 >= val2;
609 else if (op == LT)
610 val1 = val1 < val2;
611 else /* (op == GT) */
612 val1 = val1 > val2;
613 }
614 return (val1);
615 }
616
617 /* Left and right shifts. */
618 static long
619 expshift ()
620 {
621 register long val1, val2;
622
623 val1 = exp3 ();
624
625 while ((curtok == LSH) || (curtok == RSH))
626 {
627 int op = curtok;
628
629 readtok ();
630 val2 = exp3 ();
631
632 if (op == LSH)
633 val1 = val1 << val2;
634 else
635 val1 = val1 >> val2;
636 }
637
638 return (val1);
639 }
640
641 static long
642 exp3 ()
643 {
644 register long val1, val2;
645
646 val1 = exp2 ();
647
648 while ((curtok == PLUS) || (curtok == MINUS))
649 {
650 int op = curtok;
651
652 readtok ();
653 val2 = exp2 ();
654
655 if (op == PLUS)
656 val1 += val2;
657 else if (op == MINUS)
658 val1 -= val2;
659 }
660 return (val1);
661 }
662
663 static long
664 exp2 ()
665 {
666 register long val1, val2;
667
668 val1 = exp1 ();
669
670 while ((curtok == MUL) ||
671 (curtok == DIV) ||
672 (curtok == MOD))
673 {
674 int op = curtok;
675
676 readtok ();
677
678 val2 = exp1 ();
679
680 if (((op == DIV) || (op == MOD)) && (val2 == 0))
681 evalerror ("division by 0");
682
683 if (op == MUL)
684 val1 *= val2;
685 else if (op == DIV)
686 val1 /= val2;
687 else if (op == MOD)
688 val1 %= val2;
689 }
690 return (val1);
691 }
692
693 static long
694 exp1 ()
695 {
696 register long val;
697
698 if (curtok == NOT)
699 {
700 readtok ();
701 val = !exp1 ();
702 }
703 else if (curtok == BNOT)
704 {
705 readtok ();
706 val = ~exp1 ();
707 }
708 else
709 val = exp0 ();
710
711 return (val);
712 }
713
714 static long
715 exp0 ()
716 {
717 register long val = 0L;
718
719 if (curtok == MINUS)
720 {
721 readtok ();
722 val = - exp0 ();
723 }
724 else if (curtok == PLUS)
725 {
726 readtok ();
727 val = exp0 ();
728 }
729 else if (curtok == LPAR)
730 {
731 readtok ();
732 val = expassign ();
733
734 if (curtok != RPAR)
735 evalerror ("missing `)'");
736
737 /* Skip over closing paren. */
738 readtok ();
739 }
740 else if ((curtok == NUM) || (curtok == STR))
741 {
742 val = tokval;
743 readtok ();
744 }
745 else
746 evalerror ("syntax error: operand expected");
747
748 return (val);
749 }
750
751 /* Lexical analyzer/token reader for the expression evaluator. Reads the
752 next token and puts its value into curtok, while advancing past it.
753 Updates value of tp. May also set tokval (for number) or tokstr (for
754 string). */
755 static void
756 readtok ()
757 {
758 register char *cp;
759 register int c, c1, e;
760
761 /* Skip leading whitespace. */
762 cp = tp;
763 c = e = 0;
764 while (cp && (c = *cp) && (cr_whitespace (c)))
765 cp++;
766
767 if (c)
768 cp++;
769
770 lasttp = tp = cp - 1;
771
772 if (c == '\0')
773 {
774 lasttok = curtok;
775 curtok = 0;
776 tp = cp;
777 return;
778 }
779
780 if (legal_variable_starter (c))
781 {
782 /* Semi-bogus ksh compatibility feature -- variable names
783 not preceded with a dollar sign are shell variables. */
784 char *value;
785
786 while (legal_variable_char (c))
787 c = *cp++;
788
789 c = *--cp;
790
791 #if defined (ARRAY_VARS)
792 if (c == '[')
793 {
794 e = skipsubscript (cp, 0);
795 if (cp[e] == ']')
796 {
797 cp += e + 1;
798 c = *cp;
799 e = ']';
800 }
801 else
802 evalerror ("bad array subscript");
803 }
804 #endif /* ARRAY_VARS */
805
806 *cp = '\0';
807
808 FREE (tokstr);
809 tokstr = savestring (tp);
810
811 #if defined (ARRAY_VARS)
812 value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr);
813 #else
814 value = get_string_value (tokstr);
815 #endif
816
817 tokval = (value && *value) ? subexpr (value) : 0;
818
819 #if defined (ARRAY_VARS)
820 if (e == ']')
821 FREE (value); /* get_array_value returns newly-allocated memory */
822 #endif
823
824 *cp = c;
825 lasttok = curtok;
826 curtok = STR;
827 }
828 else if (digit(c))
829 {
830 while (digit (c) || isletter (c) || c == '#' || c == '@' || c == '_')
831 c = *cp++;
832
833 c = *--cp;
834 *cp = '\0';
835
836 tokval = strlong (tp);
837 *cp = c;
838 lasttok = curtok;
839 curtok = NUM;
840 }
841 else
842 {
843 c1 = *cp++;
844 if ((c == EQ) && (c1 == EQ))
845 c = EQEQ;
846 else if ((c == NOT) && (c1 == EQ))
847 c = NEQ;
848 else if ((c == GT) && (c1 == EQ))
849 c = GEQ;
850 else if ((c == LT) && (c1 == EQ))
851 c = LEQ;
852 else if ((c == LT) && (c1 == LT))
853 {
854 if (*cp == '=') /* a <<= b */
855 {
856 assigntok = LSH;
857 c = OP_ASSIGN;
858 cp++;
859 }
860 else
861 c = LSH;
862 }
863 else if ((c == GT) && (c1 == GT))
864 {
865 if (*cp == '=')
866 {
867 assigntok = RSH; /* a >>= b */
868 c = OP_ASSIGN;
869 cp++;
870 }
871 else
872 c = RSH;
873 }
874 else if ((c == BAND) && (c1 == BAND))
875 c = LAND;
876 else if ((c == BOR) && (c1 == BOR))
877 c = LOR;
878 else if (c1 == EQ && member(c, "*/%+-&^|"))
879 {
880 assigntok = c; /* a OP= b */
881 c = OP_ASSIGN;
882 }
883 else
884 cp--; /* `unget' the character */
885 lasttok = curtok;
886 curtok = c;
887 }
888 tp = cp;
889 }
890
891 static void
892 evalerror (msg)
893 char *msg;
894 {
895 char *name, *t;
896
897 name = this_command_name;
898 for (t = expression; whitespace (*t); t++)
899 ;
900 internal_error ("%s%s%s: %s (error token is \"%s\")",
901 name ? name : "", name ? ": " : "", t,
902 msg, (lasttp && *lasttp) ? lasttp : "");
903 longjmp (evalbuf, 1);
904 }
905
906 /* Convert a string to a long integer, with an arbitrary base.
907 0nnn -> base 8
908 0xnn -> base 16
909 Anything else: [base#]number (this is implemented to match ksh93)
910
911 Base may be >=2 and <=64. If base is <= 36, the numbers are drawn
912 from [0-9][a-zA-Z], and lowercase and uppercase letters may be used
913 interchangably. If base is > 36 and <= 64, the numbers are drawn
914 from [0-9][a-z][A-Z]_@ (a = 10, z = 35, A = 36, Z = 61, _ = 62, @ = 63 --
915 you get the picture). */
916
917 static long
918 strlong (num)
919 char *num;
920 {
921 register char *s;
922 register int c;
923 int base, foundbase;
924 long val = 0L;
925
926 s = num;
927 if (s == NULL || *s == '\0')
928 return 0L;
929
930 base = 10;
931 foundbase = 0;
932 if (*s == '0')
933 {
934 s++;
935
936 if (s == NULL || *s == '\0')
937 return 0L;
938
939 /* Base 16? */
940 if (*s == 'x' || *s == 'X')
941 {
942 base = 16;
943 s++;
944 }
945 else
946 base = 8;
947 foundbase++;
948 }
949
950 val = 0L;
951 for (c = *s++; c; c = *s++)
952 {
953 if (c == '#')
954 {
955 if (foundbase)
956 evalerror ("bad number");
957
958 base = (int)val;
959
960 /* Illegal base specifications raise an evaluation error. */
961 if (base < 2 || base > 64)
962 evalerror ("illegal arithmetic base");
963
964 val = 0L;
965 foundbase++;
966 }
967 else if (isletter(c) || digit(c) || (c == '_') || (c == '@'))
968 {
969 if (digit(c))
970 c = digit_value(c);
971 else if (c >= 'a' && c <= 'z')
972 c -= 'a' - 10;
973 else if (c >= 'A' && c <= 'Z')
974 c -= 'A' - ((base <= 36) ? 10 : 36);
975 else if (c == '_')
976 c = 62;
977 else if (c == '@')
978 c = 63;
979
980 if (c >= base)
981 evalerror ("value too great for base");
982
983 val = (val * base) + c;
984 }
985 else
986 break;
987 }
988 return (val);
989 }
990
991 #if defined (EXPR_TEST)
992 char *
993 xmalloc (n)
994 int n;
995 {
996 return (malloc (n));
997 }
998
999 char *
1000 xrealloc (s, n)
1001 char *s;
1002 int n;
1003 {
1004 return (realloc (s, n));
1005 }
1006
1007 SHELL_VAR *find_variable () { return 0;}
1008 SHELL_VAR *bind_variable () { return 0; }
1009
1010 char *get_string_value () { return 0; }
1011
1012 procenv_t top_level;
1013
1014 main (argc, argv)
1015 int argc;
1016 char **argv;
1017 {
1018 register int i;
1019 long v;
1020 int expok;
1021
1022 if (setjmp (top_level))
1023 exit (0);
1024
1025 for (i = 1; i < argc; i++)
1026 {
1027 v = evalexp (argv[i], &expok);
1028 if (expok == 0)
1029 fprintf (stderr, "%s: expression error\n", argv[i]);
1030 else
1031 printf ("'%s' -> %ld\n", argv[i], v);
1032 }
1033 exit (0);
1034 }
1035
1036 int
1037 builtin_error (format, arg1, arg2, arg3, arg4, arg5)
1038 char *format;
1039 {
1040 fprintf (stderr, "expr: ");
1041 fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
1042 fprintf (stderr, "\n");
1043 return 0;
1044 }
1045
1046 char *
1047 itos (n)
1048 int n;
1049 {
1050 return ("42");
1051 }
1052
1053 #endif /* EXPR_TEST */