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