Imported from ../bash-2.02.tar.gz.
[platform/upstream/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 */