Imported from ../bash-2.0.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         "*", "/", "%"
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     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   context = (EXPR_CONTEXT *)xmalloc (sizeof (EXPR_CONTEXT));
162
163   if (expr_depth >= MAX_EXPR_RECURSION_LEVEL)
164     evalerror ("expression recursion level exceeded");
165
166   if (expr_depth >= expr_stack_size)
167     {
168       expr_stack = (EXPR_CONTEXT **)
169         xrealloc (expr_stack, (expr_stack_size += EXPR_STACK_GROW_SIZE)
170                   * sizeof (EXPR_CONTEXT *));
171     }
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.
203
204    The `while' loop after the longjmp is caught relies on the above
205    implementation of pushexp and popexp leaving in expr_stack[0] the
206    values that the variables had when the program started.  That is,
207    the first things saved are the initial values of the variables that
208    were assigned at program startup or by the compiler.  Therefore, it is
209    safe to let the loop terminate when expr_depth == 0, without freeing up
210    any of the expr_depth[0] stuff. */
211 long
212 evalexp (expr)
213      char *expr;
214 {
215   long val = 0L;
216   procenv_t old_evalbuf;
217   char *p;
218
219   for (p = expr; p && *p && cr_whitespace (*p); p++)
220     ;
221
222   if (p == NULL || *p == '\0')
223     return (0);
224
225   /* Save the value of evalbuf to protect it around possible recursive
226      calls to evalexp (). */
227   COPY_PROCENV (evalbuf, old_evalbuf);
228
229   if (setjmp (evalbuf))
230     {
231       if (tokstr)               /* Clean up local allocation. */
232         free (tokstr);
233
234       if (expression)
235         free (expression);
236
237       while (--expr_depth)
238         {
239           if (expr_stack[expr_depth]->tokstr)
240             free (expr_stack[expr_depth]->tokstr);
241
242           if (expr_stack[expr_depth]->expression)
243             free (expr_stack[expr_depth]->expression);
244         }
245       jump_to_top_level (DISCARD);
246     }
247
248   pushexp ();
249   curtok = lasttok = 0;
250   expression = savestring (expr);
251   tp = expression;
252
253   tokstr = (char *)NULL;
254   tokval = 0l;
255
256   readtok ();
257
258   val = expassign ();
259
260   if (curtok != 0)
261     evalerror ("syntax error in expression");
262
263   if (tokstr)
264     free (tokstr);
265   if (expression)
266     free (expression);
267
268   popexp ();
269
270   /* Restore the value of evalbuf so that any subsequent longjmp calls
271      will have a valid location to jump to. */
272   COPY_PROCENV (old_evalbuf, evalbuf);
273
274   return (val);
275 }
276
277 /* Bind/create a shell variable with the name LHS to the RHS.
278    This creates or modifies a variable such that it is an integer.
279
280    This should really be in variables.c, but it is here so that all of the
281    expression evaluation stuff is localized.  Since we don't want any
282    recursive evaluation from bind_variable() (possible without this code,
283    since bind_variable() calls the evaluator for variables with the integer
284    attribute set), we temporarily turn off the integer attribute for each
285    variable we set here, then turn it back on after binding as necessary. */
286
287 void
288 bind_int_variable (lhs, rhs)
289      char *lhs, *rhs;
290 {
291   register SHELL_VAR *v;
292   int isint = 0;
293
294   v = find_variable (lhs);
295   if (v)
296     {
297       isint = integer_p (v);
298       v->attributes &= ~att_integer;
299     }
300
301   v = bind_variable (lhs, rhs);
302   if (isint)
303     v->attributes |= att_integer;
304 }
305
306 static long
307 expassign ()
308 {
309   register long value;
310   char *lhs, *rhs;
311
312   value = expcond ();
313   if (curtok == EQ || curtok == OP_ASSIGN)
314     {
315       int special, op;
316       long lvalue;
317
318       special = curtok == OP_ASSIGN;
319
320       if (lasttok != STR)
321         evalerror ("attempted assignment to non-variable");
322
323       if (special)
324         {
325           op = assigntok;               /* a OP= b */
326           lvalue = value;
327         }
328
329       lhs = savestring (tokstr);
330       readtok ();
331       value = expassign ();
332
333       if (special)
334         {
335           switch (op)
336             {
337             case MUL:
338               lvalue *= value;
339               break;
340             case DIV:
341               lvalue /= value;
342               break;
343             case MOD:
344               lvalue %= value;
345               break;
346             case PLUS:
347               lvalue += value;
348               break;
349             case MINUS:
350               lvalue -= value;
351               break;
352             case LSH:
353               lvalue <<= value;
354               break;
355             case RSH:
356               lvalue >>= value;
357               break;
358             case BAND:
359               lvalue &= value;
360               break;
361             case BOR:
362               lvalue |= value;
363               break;
364             default:
365               evalerror ("bug: bad expassign token");
366               break;
367             }
368           value = lvalue;
369         }
370
371       rhs = itos (value);
372       if (noeval == 0)
373         bind_int_variable (lhs, rhs);
374       free (rhs);
375       free (lhs);
376       free (tokstr);
377       tokstr = (char *)NULL;            /* For freeing on errors. */
378     }
379   return (value);
380 }
381
382 /* Conditional expression (expr?expr:expr) */
383 static long
384 expcond ()
385 {
386   long cval, val1, val2, rval;
387   rval = cval = explor ();
388   if (curtok == QUES)           /* found conditional expr */
389     {
390       readtok ();
391       if (curtok == 0 || curtok == COL)
392         evalerror ("expression expected");
393       if (cval == 0)
394         noeval++;
395 #if 0
396       val1 = explor ();
397 #else
398       val1 = expassign ();
399 #endif
400       if (cval == 0)
401         noeval--;
402       if (curtok != COL)
403         evalerror ("`:' expected for conditional expression");
404       readtok ();
405       if (curtok == 0)
406         evalerror ("expression expected");
407       if (cval)
408         noeval++;
409       val2 = explor ();
410       if (cval)
411         noeval--;
412       rval = cval ? val1 : val2;
413       lasttok = COND;
414     }
415   return rval;
416 }
417
418 /* Logical OR. */
419 static long
420 explor ()
421 {
422   register long val1, val2;
423
424   val1 = expland ();
425
426   while (curtok == LOR)
427     {
428       readtok ();
429       if (val1 != 0)
430         noeval++;
431       val2 = expland ();
432       if (val1 != 0)
433         noeval--;
434       val1 = val1 || val2;
435     }
436
437   return (val1);
438 }
439
440 /* Logical AND. */
441 static long
442 expland ()
443 {
444   register long val1, val2;
445
446   val1 = expbor ();
447
448   while (curtok == LAND)
449     {
450       readtok ();
451       if (val1 == 0)
452         noeval++;
453       val2 = expbor ();
454       if (val1 == 0)
455         noeval--;
456       val1 = val1 && val2;
457     }
458
459   return (val1);
460 }
461
462 /* Bitwise OR. */
463 static long
464 expbor ()
465 {
466   register long val1, val2;
467
468   val1 = expbxor ();
469
470   while (curtok == BOR)
471     {
472       readtok ();
473       val2 = expbxor ();
474       val1 = val1 | val2;
475     }
476
477   return (val1);
478 }
479
480 /* Bitwise XOR. */
481 static long
482 expbxor ()
483 {
484   register long val1, val2;
485
486   val1 = expband ();
487
488   while (curtok == BXOR)
489     {
490       readtok ();
491       val2 = expband ();
492       val1 = val1 ^ val2;
493     }
494
495   return (val1);
496 }
497
498 /* Bitwise AND. */
499 static long
500 expband ()
501 {
502   register long val1, val2;
503
504   val1 = exp5 ();
505
506   while (curtok == BAND)
507     {
508       readtok ();
509       val2 = exp5 ();
510       val1 = val1 & val2;
511     }
512
513   return (val1);
514 }
515
516 static long
517 exp5 ()
518 {
519   register long val1, val2;
520
521   val1 = exp4 ();
522
523   while ((curtok == EQEQ) || (curtok == NEQ))
524     {
525       int op = curtok;
526
527       readtok ();
528       val2 = exp4 ();
529       if (op == EQEQ)
530         val1 = (val1 == val2);
531       else if (op == NEQ)
532         val1 = (val1 != val2);
533     }
534   return (val1);
535 }
536
537 static long
538 exp4 ()
539 {
540   register long val1, val2;
541
542   val1 = expshift ();
543   while ((curtok == LEQ) ||
544          (curtok == GEQ) ||
545          (curtok == LT) ||
546          (curtok == GT))
547     {
548       int op = curtok;
549
550       readtok ();
551       val2 = expshift ();
552
553       if (op == LEQ)
554         val1 = val1 <= val2;
555       else if (op == GEQ)
556         val1 = val1 >= val2;
557       else if (op == LT)
558         val1 = val1 < val2;
559       else if (op == GT)
560         val1 = val1 > val2;
561     }
562   return (val1);
563 }
564
565 /* Left and right shifts. */
566 static long
567 expshift ()
568 {
569   register long val1, val2;
570
571   val1 = exp3 ();
572
573   while ((curtok == LSH) || (curtok == RSH))
574     {
575       int op = curtok;
576
577       readtok ();
578       val2 = exp3 ();
579
580       if (op == LSH)
581         val1 = val1 << val2;
582       else
583         val1 = val1 >> val2;
584     }
585
586   return (val1);
587 }
588
589 static long
590 exp3 ()
591 {
592   register long val1, val2;
593
594   val1 = exp2 ();
595
596   while ((curtok == PLUS) || (curtok == MINUS))
597     {
598       int op = curtok;
599
600       readtok ();
601       val2 = exp2 ();
602
603       if (op == PLUS)
604         val1 += val2;
605       else if (op == MINUS)
606         val1 -= val2;
607     }
608   return (val1);
609 }
610
611 static long
612 exp2 ()
613 {
614   register long val1, val2;
615
616   val1 = exp1 ();
617
618   while ((curtok == MUL) ||
619          (curtok == DIV) ||
620          (curtok == MOD))
621     {
622       int op = curtok;
623
624       readtok ();
625
626       val2 = exp1 ();
627
628       if (((op == DIV) || (op == MOD)) && (val2 == 0))
629         evalerror ("division by 0");
630
631       if (op == MUL)
632         val1 *= val2;
633       else if (op == DIV)
634         val1 /= val2;
635       else if (op == MOD)
636         val1 %= val2;
637     }
638   return (val1);
639 }
640
641 static long
642 exp1 ()
643 {
644   register long val;
645
646   if (curtok == NOT)
647     {
648       readtok ();
649       val = !exp1 ();
650     }
651   else if (curtok == BNOT)
652     {
653       readtok ();
654       val = ~exp1 ();
655     }
656   else
657     val = exp0 ();
658
659   return (val);
660 }
661
662 static long
663 exp0 ()
664 {
665   register long val = 0L;
666
667   if (curtok == MINUS)
668     {
669       readtok ();
670       val = - exp0 ();
671     }
672   else if (curtok == PLUS)
673     {
674       readtok ();
675       val = exp0 ();
676     }
677   else if (curtok == LPAR)
678     {
679       readtok ();
680       val = expassign ();
681
682       if (curtok != RPAR)
683         evalerror ("missing `)'");
684
685       /* Skip over closing paren. */
686       readtok ();
687     }
688   else if ((curtok == NUM) || (curtok == STR))
689     {
690       val = tokval;
691       readtok ();
692     }
693   else
694     evalerror ("syntax error: operand expected");
695
696   return (val);
697 }
698
699 /* Lexical analyzer/token reader for the expression evaluator.  Reads the
700    next token and puts its value into curtok, while advancing past it.
701    Updates value of tp.  May also set tokval (for number) or tokstr (for
702    string). */
703 static void
704 readtok ()
705 {
706   register char *cp;
707   register int c, c1, e;
708
709   /* Skip leading whitespace. */
710   cp = tp;
711   c = e = 0;
712   while (cp && (c = *cp) && (cr_whitespace (c)))
713     cp++;
714
715   if (c)
716     cp++;
717
718   lasttp = tp = cp - 1;
719
720   if (c == '\0')
721     {
722       lasttok = curtok;
723       curtok = 0;
724       tp = cp;
725       return;
726     }
727
728   if (legal_variable_starter (c))
729     {
730       /* Semi-bogus ksh compatibility feature -- variable names
731          not preceded with a dollar sign are shell variables. */
732       char *value;
733
734       while (legal_variable_char (c))
735         c = *cp++;
736
737       c = *--cp;
738
739 #if defined (ARRAY_VARS)
740       if (c == '[')
741         {
742           e = skipsubscript (cp, 0);
743           if (cp[e] == ']')
744             {
745               cp += e + 1;
746               c = *cp;
747               e = ']';
748             }
749           else
750             evalerror ("bad array subscript");
751         }
752 #endif /* ARRAY_VARS */
753
754       *cp = '\0';
755
756       FREE (tokstr);
757       tokstr = savestring (tp);
758
759 #if defined (ARRAY_VARS)
760       value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr);
761 #else
762       value = get_string_value (tokstr);
763 #endif
764
765       tokval = (value && *value) ? evalexp (value) : 0;
766
767       *cp = c;
768       lasttok = curtok;
769       curtok = STR;
770     }
771   else if (digit(c))
772     {
773       while (digit (c) || isletter (c) || c == '#' || c == '@' || c == '_')
774         c = *cp++;
775
776       c = *--cp;
777       *cp = '\0';
778
779       tokval = strlong (tp);
780       *cp = c;
781       lasttok = curtok;
782       curtok = NUM;
783     }
784   else
785     {
786       c1 = *cp++;
787       if ((c == EQ) && (c1 == EQ))
788         c = EQEQ;
789       else if ((c == NOT) && (c1 == EQ))
790         c = NEQ;
791       else if ((c == GT) && (c1 == EQ))
792         c = GEQ;
793       else if ((c == LT) && (c1 == EQ))
794         c = LEQ;
795       else if ((c == LT) && (c1 == LT))
796         {
797           if (*cp == '=')       /* a <<= b */
798             {
799               assigntok = LSH;
800               c = OP_ASSIGN;
801               cp++;
802             }
803           else
804             c = LSH;
805         }
806       else if ((c == GT) && (c1 == GT))
807         {
808           if (*cp == '=')
809             {
810               assigntok = RSH;  /* a >>= b */
811               c = OP_ASSIGN;
812               cp++;
813             }
814           else
815             c = RSH;
816         }
817       else if ((c == BAND) && (c1 == BAND))
818         c = LAND;
819       else if ((c == BOR) && (c1 == BOR))
820         c = LOR;
821       else if (c1 == EQ && member(c, "*/%+-&^|"))
822         {
823           assigntok = c;        /* a OP= b */
824           c = OP_ASSIGN;
825         }
826       else
827         cp--;                   /* `unget' the character */
828       lasttok = curtok;
829       curtok = c;
830     }
831   tp = cp;
832 }
833
834 static void
835 evalerror (msg)
836      char *msg;
837 {
838   char *name, *t;
839
840   name = this_command_name;
841   for (t = expression; whitespace (*t); t++)
842     ;
843   internal_error ("%s%s%s: %s (error token is \"%s\")",
844                    name ? name : "", name ? ": " : "", t,
845                    msg, (lasttp && *lasttp) ? lasttp : "");
846   longjmp (evalbuf, 1);
847 }
848
849 /* Convert a string to a long integer, with an arbitrary base.
850    0nnn -> base 8
851    0xnn -> base 16
852    Anything else: [base#]number (this is implemented to match ksh93)
853
854    Base may be >=2 and <=64.  If base is <= 36, the numbers are drawn
855    from [0-9][a-zA-Z], and lowercase and uppercase letters may be used
856    interchangably.  If base is > 36 and <= 64, the numbers are drawn
857    from [0-9][a-z][A-Z]_@ (a = 10, z = 35, A = 36, Z = 61, _ = 62, @ = 63 --
858    you get the picture). */
859
860 static long
861 strlong (num)
862      char *num;
863 {
864   register char *s;
865   register int c;
866   int base, foundbase;
867   long val = 0L;
868
869   s = num;
870   if (s == NULL || *s == '\0')
871     return 0L;
872
873   base = 10;
874   foundbase = 0;
875   if (*s == '0')
876     {
877       s++;
878
879       if (s == NULL || *s == '\0')
880         return 0L;
881
882        /* Base 16? */
883       if (*s == 'x' || *s == 'X')
884         {
885           base = 16;
886           s++;
887         }
888       else
889         base = 8;
890       foundbase++;
891     }
892
893   val = 0L;
894   for (c = *s++; c; c = *s++)
895     {
896       if (c == '#')
897         {
898           if (foundbase)
899             evalerror ("bad number");
900
901           base = (int)val;
902
903           /* Illegal base specifications raise an evaluation error. */
904           if (base < 2 || base > 64)
905             evalerror ("illegal arithmetic base");
906
907           val = 0L;
908           foundbase++;
909         }
910       else if (isletter(c) || digit(c) || (c == '_') || (c == '@'))
911         {
912           if (digit(c))
913             c = digit_value(c);
914           else if (c >= 'a' && c <= 'z')
915             c -= 'a' - 10;
916           else if (c >= 'A' && c <= 'Z')
917             c -= 'A' - ((base <= 36) ? 10 : 36);
918           else if (c == '_')
919             c = 62;
920           else if (c == '@')
921             c = 63;
922
923           if (c >= base)
924             evalerror ("value too great for base");
925
926           val = (val * base) + c;
927         }
928       else
929         break;
930     }
931   return (val);
932 }
933
934 #if defined (EXPR_TEST)
935 char *
936 xmalloc (n)
937      int n;
938 {
939   return (malloc (n));
940 }
941
942 char *
943 xrealloc (s, n)
944      char *s;
945      int n;
946 {
947   return (realloc (s, n));
948 }
949
950 SHELL_VAR *find_variable () { return 0;}
951 SHELL_VAR *bind_variable () { return 0; }
952
953 char *get_string_value () { return 0; }
954
955 procenv_t top_level;
956
957 main (argc, argv)
958      int argc;
959      char **argv;
960 {
961   register int i;
962   long v;
963
964   if (setjmp (top_level))
965     exit (0);
966
967   for (i = 1; i < argc; i++)
968     {
969       v = evalexp (argv[i]);
970       printf ("'%s' -> %ld\n", argv[i], v);
971     }
972   exit (0);
973 }
974
975 int
976 builtin_error (format, arg1, arg2, arg3, arg4, arg5)
977      char *format;
978 {
979   fprintf (stderr, "expr: ");
980   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
981   fprintf (stderr, "\n");
982   return 0;
983 }
984
985 char *
986 itos (n)
987      int n;
988 {
989   return ("42");
990 }
991
992 #endif /* EXPR_TEST */