Implement missing shell arithmetic operators in $(()) expansion
and variable recognition. PR: standards/52972 Submitted by: Wartan Hachaturow <wart@tepkom.ru> Reviewed by: tjr (improved on original patch) Tested by: buildworld on CURRENT. MFC after: 6 weeks
This commit is contained in:
parent
9cd8ed99ee
commit
bc411549ba
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=119578
@ -34,5 +34,6 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
int arith_assign(char *, arith_t);
|
||||
int arith(char *);
|
||||
int expcmd(int , char **);
|
||||
|
246
bin/sh/arith.y
246
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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <limits.h>
|
||||
#include "shell.h"
|
||||
#include "var.h"
|
||||
%}
|
||||
%union {
|
||||
arith_t l_value;
|
||||
char* s_value;
|
||||
}
|
||||
%token <l_value> ARITH_NUM ARITH_LPAREN ARITH_RPAREN
|
||||
%token <s_value> ARITH_VAR
|
||||
|
||||
%type <l_value> 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)
|
||||
{
|
||||
|
@ -43,10 +43,12 @@ static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95";
|
||||
#include <sys/cdefs.h>
|
||||
__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); }
|
||||
%%
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user