diff --git a/bin/sh/arith.h b/bin/sh/arith.h index ba8134ecba50..45161c1ba00e 100644 --- a/bin/sh/arith.h +++ b/bin/sh/arith.h @@ -34,5 +34,6 @@ * $FreeBSD$ */ +int arith_assign(char *, arith_t); int arith(char *); int expcmd(int , char **); diff --git a/bin/sh/arith.y b/bin/sh/arith.y index 1ced62685d33..87a027c0ad83 100644 --- a/bin/sh/arith.y +++ b/bin/sh/arith.y @@ -1,58 +1,4 @@ -%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN - -%left ARITH_OR -%left ARITH_AND -%left ARITH_BOR -%left ARITH_BXOR -%left ARITH_BAND -%left ARITH_EQ ARITH_NE -%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE -%left ARITH_LSHIFT ARITH_RSHIFT -%left ARITH_ADD ARITH_SUB -%left ARITH_MUL ARITH_DIV ARITH_REM -%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT -%% - -exp: expr = { - return ($1); - } - ; - - -expr: ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; } - | expr ARITH_OR expr = { $$ = $1 ? $1 : $3 ? $3 : 0; } - | expr ARITH_AND expr = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } - | expr ARITH_BOR expr = { $$ = $1 | $3; } - | expr ARITH_BXOR expr = { $$ = $1 ^ $3; } - | expr ARITH_BAND expr = { $$ = $1 & $3; } - | expr ARITH_EQ expr = { $$ = $1 == $3; } - | expr ARITH_GT expr = { $$ = $1 > $3; } - | expr ARITH_GE expr = { $$ = $1 >= $3; } - | expr ARITH_LT expr = { $$ = $1 < $3; } - | expr ARITH_LE expr = { $$ = $1 <= $3; } - | expr ARITH_NE expr = { $$ = $1 != $3; } - | expr ARITH_LSHIFT expr = { $$ = $1 << $3; } - | expr ARITH_RSHIFT expr = { $$ = $1 >> $3; } - | expr ARITH_ADD expr = { $$ = $1 + $3; } - | expr ARITH_SUB expr = { $$ = $1 - $3; } - | expr ARITH_MUL expr = { $$ = $1 * $3; } - | expr ARITH_DIV expr = { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 / $3; - } - | expr ARITH_REM expr = { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 % $3; - } - | ARITH_NOT expr = { $$ = !($2); } - | ARITH_BNOT expr = { $$ = ~($2); } - | ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); } - | ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $2; } - | ARITH_NUM - ; -%% +%{ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -97,17 +43,207 @@ static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; #include __FBSDID("$FreeBSD$"); +#include #include "shell.h" +#include "var.h" +%} +%union { + arith_t l_value; + char* s_value; +} +%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN +%token ARITH_VAR + +%type expr +%right ARITH_ASSIGN +%right ARITH_ADDASSIGN ARITH_SUBASSIGN +%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN +%right ARITH_RSHASSIGN ARITH_LSHASSIGN +%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN +%left ARITH_OR +%left ARITH_AND +%left ARITH_BOR +%left ARITH_BXOR +%left ARITH_BAND +%left ARITH_EQ ARITH_NE +%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE +%left ARITH_LSHIFT ARITH_RSHIFT +%left ARITH_ADD ARITH_SUB +%left ARITH_MUL ARITH_DIV ARITH_REM +%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT +%% + +exp: expr = { + return ($1); + } + ; + +expr: ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; } + | expr ARITH_OR expr = { $$ = $1 ? $1 : $3 ? $3 : 0; } + | expr ARITH_AND expr = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } + | expr ARITH_BOR expr = { $$ = $1 | $3; } + | expr ARITH_BXOR expr = { $$ = $1 ^ $3; } + | expr ARITH_BAND expr = { $$ = $1 & $3; } + | expr ARITH_EQ expr = { $$ = $1 == $3; } + | expr ARITH_GT expr = { $$ = $1 > $3; } + | expr ARITH_GE expr = { $$ = $1 >= $3; } + | expr ARITH_LT expr = { $$ = $1 < $3; } + | expr ARITH_LE expr = { $$ = $1 <= $3; } + | expr ARITH_NE expr = { $$ = $1 != $3; } + | expr ARITH_LSHIFT expr = { $$ = $1 << $3; } + | expr ARITH_RSHIFT expr = { $$ = $1 >> $3; } + | expr ARITH_ADD expr = { $$ = $1 + $3; } + | expr ARITH_SUB expr = { $$ = $1 - $3; } + | expr ARITH_MUL expr = { $$ = $1 * $3; } + | expr ARITH_DIV expr = { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 / $3; + } + | expr ARITH_REM expr = { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 % $3; + } + | ARITH_NOT expr = { $$ = !($2); } + | ARITH_BNOT expr = { $$ = ~($2); } + | ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); } + | ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $2; } + | ARITH_NUM + | ARITH_VAR { + char *p; + arith_t arith_val; + char *str_val; + + if (lookupvar($1) == NULL) + setvarsafe($1, "0", 0); + str_val = lookupvar($1); + arith_val = strtoarith_t(str_val, &p, 0); + /* + * Conversion is successful only in case + * we've converted _all_ characters. + */ + if (*p != '\0') + yyerror("variable conversion error"); + $$ = arith_val; + } + | ARITH_VAR ARITH_ASSIGN expr { + if (arith_assign($1, $3) != 1) + yyerror("variable assignment error"); + $$ = $3; + } + | ARITH_VAR ARITH_ADDASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) + $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_SUBASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) - $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_MULASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) * $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_DIVASSIGN expr { + arith_t value; + + if ($3 == 0) + yyerror("division by zero"); + + value = atoarith_t(lookupvar($1)) / $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_REMASSIGN expr { + arith_t value; + + if ($3 == 0) + yyerror("division by zero"); + + value = atoarith_t(lookupvar($1)) % $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_RSHASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) >> $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_LSHASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) << $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_BANDASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) & $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_BXORASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) ^ $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + | ARITH_VAR ARITH_BORASSIGN expr { + arith_t value; + + value = atoarith_t(lookupvar($1)) | $3; + if (arith_assign($1, value) != 0) + yyerror("variable assignment error"); + $$ = value; + } + ; +%% #include "error.h" #include "output.h" #include "memalloc.h" +#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) + char *arith_buf, *arith_startbuf; extern void arith_lex_reset(); int yylex(void); int yyparse(void); +int +arith_assign(char *name, arith_t value) { + char *str; + int ret; + + str = (char *)ckmalloc(lstrlen(value)); + sprintf(str, ARITH_FORMAT_STR, value); + ret = setvarsafe(name, str, 0); + free(str); + return ret; +} + int arith(char *s) { diff --git a/bin/sh/arith_lex.l b/bin/sh/arith_lex.l index 6bbbfafd425d..cad1846cb0de 100644 --- a/bin/sh/arith_lex.l +++ b/bin/sh/arith_lex.l @@ -43,10 +43,12 @@ static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; #include __FBSDID("$FreeBSD$"); +#include "shell.h" #include "y.tab.h" #include "error.h" +#include "var.h" +#include "memalloc.h" -extern int yylval; extern char *arith_buf, *arith_startbuf; #undef YY_INPUT #define YY_INPUT(buf,result,max) \ @@ -56,7 +58,36 @@ extern char *arith_buf, *arith_startbuf; %% [ \t\n] { ; } -[0-9]+ { yylval = atol(yytext); return(ARITH_NUM); } + +0x[a-fA-F0-9]+ { + yylval.l_value = strtoarith_t(yytext, NULL, 16); + return(ARITH_NUM); + } + +0[0-7]+ { + yylval.l_value = strtoarith_t(yytext, NULL, 8); + return(ARITH_NUM); + } + +[0-9]+ { + yylval.l_value = strtoarith_t(yytext, NULL, 10); + return(ARITH_NUM); + } + + +[A-Za-z][A-Za-z0-9_]* { + /* + * If variable doesn't exist, we should initialize + * it to zero. + */ + char *temp; + if (lookupvar(yytext) == NULL) + setvarsafe(yytext, "0", 0); + temp = (char *)ckmalloc(strlen(yytext) + 1); + yylval.s_value = strcpy(temp, yytext); + + return(ARITH_VAR); + } "(" { return(ARITH_LPAREN); } ")" { return(ARITH_RPAREN); } "||" { return(ARITH_OR); } @@ -79,6 +110,17 @@ extern char *arith_buf, *arith_startbuf; "-" { return(ARITH_SUB); } "~" { return(ARITH_BNOT); } "!" { return(ARITH_NOT); } +"=" { return(ARITH_ASSIGN); } +"+=" { return(ARITH_ADDASSIGN); } +"-=" { return(ARITH_SUBASSIGN); } +"*=" { return(ARITH_MULASSIGN); } +"/=" { return(ARITH_DIVASSIGN); } +"%=" { return(ARITH_REMASSIGN); } +">>=" { return(ARITH_RSHASSIGN); } +"<<=" { return(ARITH_LSHASSIGN); } +"&=" { return(ARITH_BANDASSIGN); } +"^=" { return(ARITH_BXORASSIGN); } +"|=" { return(ARITH_BORASSIGN); } . { error("arith: syntax error: \"%s\"\n", arith_startbuf); } %% diff --git a/bin/sh/shell.h b/bin/sh/shell.h index 1de36f337ecb..eaf27552e1bf 100644 --- a/bin/sh/shell.h +++ b/bin/sh/shell.h @@ -51,6 +51,14 @@ #define JOBS 1 /* #define DEBUG 1 */ +/* + * Type of used arithmetics. SUSv3 requires us to have at least signed long. + */ +typedef long arith_t; +#define strtoarith_t(nptr, endptr, base) strtol(nptr, endptr, base) +#define atoarith_t(arg) strtol(arg, NULL, 0) +#define ARITH_FORMAT_STR "%ld" + typedef void *pointer; #define STATIC static #define MKINIT /* empty */