Bash-5.3 distribution sources and documentation

This commit is contained in:
Chet Ramey
2025-07-03 16:15:36 -04:00
parent 6794b5478f
commit b8c60bc9ca
896 changed files with 174241 additions and 103591 deletions
+183 -184
View File
@@ -1,6 +1,6 @@
/* expr.c -- arithmetic expression evaluation. */
/* Copyright (C) 1990-2021 Free Software Foundation, Inc.
/* Copyright (C) 1990-2025 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -26,8 +26,8 @@
order of decreasing precedence.
"id++", "id--" [post-increment and post-decrement]
"-", "+" [(unary operators)]
"++id", "--id" [pre-increment and pre-decrement]
"-", "+" [(unary operators)]
"!", "~"
"**" [(exponentiation)]
"*", "/", "%"
@@ -88,6 +88,9 @@
#include "subst.h"
#include "typemax.h" /* INTMAX_MAX, INTMAX_MIN */
#include "builtins/common.h" /* this_shell_builtin */
#include "builtins/builtext.h" /* let_builtin */
/* Because of the $((...)) construct, expressions may include newlines.
Here is a macro which accepts newlines, tabs and spaces as whitespace. */
#define cr_whitespace(c) (whitespace(c) || ((c) == '\n'))
@@ -155,7 +158,8 @@ struct lvalue
};
/* A structure defining a single expression context. */
typedef struct {
typedef struct
{
int curtok, lasttok;
char *expression, *tp, *lasttp;
intmax_t tokval;
@@ -181,48 +185,53 @@ static int already_expanded;
static struct lvalue curlval = {0, 0, 0, -1};
static struct lvalue lastlval = {0, 0, 0, -1};
static int _is_arithop PARAMS((int));
static void readtok PARAMS((void)); /* lexical analyzer */
static int is_arithop (int);
static int is_multiop (int);
static void readtok (void); /* lexical analyzer */
static void init_lvalue PARAMS((struct lvalue *));
static struct lvalue *alloc_lvalue PARAMS((void));
static void free_lvalue PARAMS((struct lvalue *));
static void init_lvalue (struct lvalue *);
static struct lvalue *alloc_lvalue (void);
static void free_lvalue (struct lvalue *);
static intmax_t expr_streval PARAMS((char *, int, struct lvalue *));
static intmax_t strlong PARAMS((char *));
static void evalerror PARAMS((const char *));
static intmax_t expr_streval (char *, int, struct lvalue *);
static intmax_t strlong (char *);
static void evalerror (const char *);
static void pushexp PARAMS((void));
static void popexp PARAMS((void));
static void expr_unwind PARAMS((void));
static void expr_bind_variable PARAMS((char *, char *));
#if defined (ARRAY_VARS)
static void expr_bind_array_element PARAMS((char *, arrayind_t, char *));
#if defined (ARRAYS)
static int expr_skipsubscript (char *, char *);
#endif
static intmax_t subexpr PARAMS((char *));
static void pushexp (void);
static void popexp (void);
static void expr_unwind (void);
static void expr_bind_variable (const char *, const char *);
#if defined (ARRAY_VARS)
static void expr_bind_array_element (const char *, arrayind_t, const char *);
#endif
static intmax_t expcomma PARAMS((void));
static intmax_t expassign PARAMS((void));
static intmax_t expcond PARAMS((void));
static intmax_t explor PARAMS((void));
static intmax_t expland PARAMS((void));
static intmax_t expbor PARAMS((void));
static intmax_t expbxor PARAMS((void));
static intmax_t expband PARAMS((void));
static intmax_t exp5 PARAMS((void));
static intmax_t exp4 PARAMS((void));
static intmax_t expshift PARAMS((void));
static intmax_t exp3 PARAMS((void));
static intmax_t expmuldiv PARAMS((void));
static intmax_t exppower PARAMS((void));
static intmax_t exp1 PARAMS((void));
static intmax_t exp0 PARAMS((void));
static intmax_t subexpr (const char *);
static intmax_t expcomma (void);
static intmax_t expassign (void);
static intmax_t expcond (void);
static intmax_t explor (void);
static intmax_t expland (void);
static intmax_t expbor (void);
static intmax_t expbxor (void);
static intmax_t expband (void);
static intmax_t expeq (void);
static intmax_t expcompare (void);
static intmax_t expshift (void);
static intmax_t expaddsub (void);
static intmax_t expmuldiv (void);
static intmax_t exppower (void);
static intmax_t expunary (void);
static intmax_t exp0 (void);
/* Global var which contains the stack of expression contexts. */
static EXPR_CONTEXT **expr_stack;
static int expr_depth; /* Location in the stack. */
static int expr_stack_size; /* Number of slots already allocated. */
static size_t expr_stack_size; /* Number of slots already allocated. */
#if defined (ARRAY_VARS)
extern const char * const bash_badsub_errmsg;
@@ -255,7 +264,7 @@ extern const char * const bash_badsub_errmsg;
/* Push and save away the contents of the globals describing the
current expression context. */
static void
pushexp ()
pushexp (void)
{
EXPR_CONTEXT *context;
@@ -279,7 +288,7 @@ pushexp ()
/* Pop the the contents of the expression context stack into the
globals describing the current expression context. */
static void
popexp ()
popexp (void)
{
EXPR_CONTEXT *context;
@@ -300,7 +309,7 @@ popexp ()
}
static void
expr_unwind ()
expr_unwind (void)
{
while (--expr_depth > 0)
{
@@ -319,8 +328,7 @@ expr_unwind ()
}
static void
expr_bind_variable (lhs, rhs)
char *lhs, *rhs;
expr_bind_variable (const char *lhs, const char *rhs)
{
SHELL_VAR *v;
int aflags;
@@ -329,13 +337,17 @@ expr_bind_variable (lhs, rhs)
return; /* XXX */
#if defined (ARRAY_VARS)
aflags = (assoc_expand_once && already_expanded) ? ASS_NOEXPAND : 0;
aflags = (array_expand_once && already_expanded) ? ASS_NOEXPAND : 0; /* XXX */
#if 0 /* TAG:bash-5.4 https://lists.gnu.org/archive/html/bug-bash/2024-12/msg00193.html */
if (this_shell_builtin == let_builtin && shell_compatibility_level > 51)
aflags |= ASS_NOEXPAND; /* we didn't quote subscripts */
#endif
aflags |= ASS_ALLOWALLSUB; /* allow assoc[@]=value */
#else
aflags = 0;
#endif
v = bind_int_variable (lhs, rhs, aflags);
if (v && (readonly_p (v) || noassign_p (v)))
if (v && ASSIGN_DISALLOWED (v, 0))
sh_longjmp (evalbuf, 1); /* variable assignment error */
stupidly_hack_special_variables (lhs);
}
@@ -344,31 +356,30 @@ expr_bind_variable (lhs, rhs)
/* This is similar to the logic in arrayfunc.c:valid_array_reference when
you pass VA_NOEXPAND. */
static int
expr_skipsubscript (vp, cp)
char *vp, *cp;
expr_skipsubscript (char *vp, char *cp)
{
int flags, isassoc;
int flags, isassoc, noexp;
SHELL_VAR *entry;
isassoc = 0;
isassoc = noexp = 0;
entry = 0;
if (assoc_expand_once & already_expanded)
/* We're not doing any evaluation here, we should suppress expansion when
skipping over the subscript */
noexp = already_expanded && (shell_compatibility_level > 51 || array_expand_once);
if (noexp)
{
*cp = '\0';
isassoc = legal_identifier (vp) && (entry = find_variable (vp)) && assoc_p (entry);
isassoc = valid_identifier (vp) && (entry = find_variable (vp)) && assoc_p (entry);
*cp = '['; /* ] */
}
flags = (isassoc && assoc_expand_once && already_expanded) ? VA_NOEXPAND : 0;
flags = (isassoc && noexp) ? VA_NOEXPAND : 0;
return (skipsubscript (cp, 0, flags));
}
/* Rewrite tok, which is of the form vname[expression], to vname[ind], where
IND is the already-calculated value of expression. */
static void
expr_bind_array_element (tok, ind, rhs)
char *tok;
arrayind_t ind;
char *rhs;
expr_bind_array_element (const char *tok, arrayind_t ind, const char *rhs)
{
char *lhs, *vname;
size_t llen;
@@ -403,10 +414,7 @@ expr_bind_array_element (tok, ind, rhs)
safe to let the loop terminate when expr_depth == 0, without freeing up
any of the expr_depth[0] stuff. */
intmax_t
evalexp (expr, flags, validp)
char *expr;
int flags;
int *validp;
evalexp (const char *expr, int flags, int *validp)
{
intmax_t val;
int c;
@@ -448,11 +456,10 @@ evalexp (expr, flags, validp)
}
static intmax_t
subexpr (expr)
char *expr;
subexpr (const char *expr)
{
intmax_t val;
char *p;
const char *p;
for (p = expr; p && *p && cr_whitespace (*p); p++)
;
@@ -474,9 +481,8 @@ subexpr (expr)
val = EXP_LOWEST ();
/*TAG:bash-5.3 make it clear that these are arithmetic syntax errors */
if (curtok != 0)
evalerror (_("syntax error in expression"));
evalerror (_("arithmetic syntax error in expression"));
FREE (tokstr);
FREE (expression);
@@ -487,7 +493,7 @@ subexpr (expr)
}
static intmax_t
expcomma ()
expcomma (void)
{
register intmax_t value;
@@ -502,7 +508,7 @@ expcomma ()
}
static intmax_t
expassign ()
expassign (void)
{
register intmax_t value;
char *lhs, *rhs;
@@ -529,7 +535,7 @@ expassign ()
}
if (tokstr == 0)
evalerror (_("syntax error in variable assignment"));
evalerror (_("arithmetic syntax error in variable assignment"));
/* XXX - watch out for pointer aliasing issues here */
lhs = savestring (tokstr);
@@ -621,7 +627,7 @@ expassign ()
/* Conditional expression (expr?expr:expr) */
static intmax_t
expcond ()
expcond (void)
{
intmax_t cval, val1, val2, rval;
int set_noeval;
@@ -669,7 +675,7 @@ expcond ()
/* Logical OR. */
static intmax_t
explor ()
explor (void)
{
register intmax_t val1, val2;
int set_noeval;
@@ -697,7 +703,7 @@ explor ()
/* Logical AND. */
static intmax_t
expland ()
expland (void)
{
register intmax_t val1, val2;
int set_noeval;
@@ -725,7 +731,7 @@ expland ()
/* Bitwise OR. */
static intmax_t
expbor ()
expbor (void)
{
register intmax_t val1, val2;
@@ -744,7 +750,7 @@ expbor ()
/* Bitwise XOR. */
static intmax_t
expbxor ()
expbxor (void)
{
register intmax_t val1, val2;
@@ -763,16 +769,16 @@ expbxor ()
/* Bitwise AND. */
static intmax_t
expband ()
expband (void)
{
register intmax_t val1, val2;
val1 = exp5 ();
val1 = expeq ();
while (curtok == BAND)
{
readtok ();
val2 = exp5 ();
val2 = expeq ();
val1 = val1 & val2;
lasttok = NUM;
}
@@ -781,18 +787,18 @@ expband ()
}
static intmax_t
exp5 ()
expeq (void)
{
register intmax_t val1, val2;
val1 = exp4 ();
val1 = expcompare ();
while ((curtok == EQEQ) || (curtok == NEQ))
{
int op = curtok;
readtok ();
val2 = exp4 ();
val2 = expcompare ();
if (op == EQEQ)
val1 = (val1 == val2);
else if (op == NEQ)
@@ -803,7 +809,7 @@ exp5 ()
}
static intmax_t
exp4 ()
expcompare (void)
{
register intmax_t val1, val2;
@@ -833,18 +839,18 @@ exp4 ()
/* Left and right shifts. */
static intmax_t
expshift ()
expshift (void)
{
register intmax_t val1, val2;
val1 = exp3 ();
val1 = expaddsub ();
while ((curtok == LSH) || (curtok == RSH))
{
int op = curtok;
readtok ();
val2 = exp3 ();
val2 = expaddsub ();
if (op == LSH)
val1 = val1 << val2;
@@ -857,7 +863,7 @@ expshift ()
}
static intmax_t
exp3 ()
expaddsub (void)
{
register intmax_t val1, val2;
@@ -880,7 +886,7 @@ exp3 ()
}
static intmax_t
expmuldiv ()
expmuldiv (void)
{
register intmax_t val1, val2;
#if defined (HAVE_IMAXDIV)
@@ -889,9 +895,7 @@ expmuldiv ()
val1 = exppower ();
while ((curtok == MUL) ||
(curtok == DIV) ||
(curtok == MOD))
while ((curtok == MUL) || (curtok == DIV) || (curtok == MOD))
{
int op = curtok;
char *stp, *sltp;
@@ -941,8 +945,7 @@ expmuldiv ()
}
static intmax_t
ipow (base, exp)
intmax_t base, exp;
ipow (intmax_t base, intmax_t exp)
{
intmax_t result;
@@ -958,52 +961,57 @@ ipow (base, exp)
}
static intmax_t
exppower ()
exppower (void)
{
register intmax_t val1, val2, c;
register intmax_t val1, val2;
val1 = exp1 ();
val1 = expunary ();
while (curtok == POWER)
{
readtok ();
val2 = exppower (); /* exponentiation is right-associative */
lasttok = NUM;
if (val2 == 0)
return (1);
if (val2 < 0)
evalerror (_("exponent less than 0"));
val1 = ipow (val1, val2);
if (noeval == 0)
{
if (val2 == 0)
return (1);
if (val2 < 0)
evalerror (_("exponent less than 0"));
val1 = ipow (val1, val2);
}
else
val1 = 1;
}
return (val1);
}
static intmax_t
exp1 ()
expunary (void)
{
register intmax_t val;
if (curtok == NOT)
{
readtok ();
val = !exp1 ();
val = !expunary ();
lasttok = NUM;
}
else if (curtok == BNOT)
{
readtok ();
val = ~exp1 ();
val = ~expunary ();
lasttok = NUM;
}
else if (curtok == MINUS)
{
readtok ();
val = - exp1 ();
val = - expunary ();
lasttok = NUM;
}
else if (curtok == PLUS)
{
readtok ();
val = exp1 ();
val = expunary ();
lasttok = NUM;
}
else
@@ -1013,13 +1021,14 @@ exp1 ()
}
static intmax_t
exp0 ()
exp0 (void)
{
register intmax_t val = 0, v2;
intmax_t val, v2;
char *vincdec;
int stok;
EXPR_CONTEXT ec;
val = 0;
/* XXX - might need additional logic here to decide whether or not
pre-increment or pre-decrement is legal at this point. */
if (curtok == PREINC || curtok == PREDEC)
@@ -1060,60 +1069,61 @@ exp0 ()
/* Skip over closing paren. */
readtok ();
}
else if ((curtok == NUM) || (curtok == STR))
else if (curtok == NUM)
{
val = tokval;
if (curtok == STR)
readtok ();
}
else if (curtok == STR)
{
val = tokval;
SAVETOK (&ec);
tokstr = (char *)NULL; /* keep it from being freed */
noeval = 1;
readtok ();
stok = curtok;
/* post-increment or post-decrement */
if (stok == POSTINC || stok == POSTDEC)
{
SAVETOK (&ec);
tokstr = (char *)NULL; /* keep it from being freed */
noeval = 1;
readtok ();
stok = curtok;
/* restore certain portions of EC */
tokstr = ec.tokstr;
noeval = ec.noeval;
curlval = ec.lval;
lasttok = STR; /* ec.curtok */
/* post-increment or post-decrement */
if (stok == POSTINC || stok == POSTDEC)
{
/* restore certain portions of EC */
tokstr = ec.tokstr;
noeval = ec.noeval;
curlval = ec.lval;
lasttok = STR; /* ec.curtok */
v2 = val + ((stok == POSTINC) ? 1 : -1);
vincdec = itos (v2);
if (noeval == 0)
{
v2 = val + ((stok == POSTINC) ? 1 : -1);
vincdec = itos (v2);
if (noeval == 0)
{
#if defined (ARRAY_VARS)
if (curlval.ind != -1)
expr_bind_array_element (curlval.tokstr, curlval.ind, vincdec);
else
if (curlval.ind != -1)
expr_bind_array_element (curlval.tokstr, curlval.ind, vincdec);
else
#endif
expr_bind_variable (tokstr, vincdec);
}
free (vincdec);
curtok = NUM; /* make sure x++=7 is flagged as an error */
}
else
{
/* XXX - watch out for pointer aliasing issues here */
if (stok == STR) /* free new tokstr before old one is restored */
FREE (tokstr);
RESTORETOK (&ec);
}
expr_bind_variable (tokstr, vincdec);
}
free (vincdec);
curtok = NUM; /* make sure x++=7 is flagged as an error */
}
else
{
/* XXX - watch out for pointer aliasing issues here */
if (stok == STR) /* free new tokstr before old one is restored */
FREE (tokstr);
RESTORETOK (&ec);
}
readtok ();
}
else
evalerror (_("syntax error: operand expected"));
evalerror (_("arithmetic syntax error: operand expected"));
return (val);
}
static void
init_lvalue (lv)
struct lvalue *lv;
init_lvalue (struct lvalue *lv)
{
lv->tokstr = 0;
lv->tokvar = 0;
@@ -1121,7 +1131,7 @@ init_lvalue (lv)
}
static struct lvalue *
alloc_lvalue ()
alloc_lvalue (void)
{
struct lvalue *lv;
@@ -1131,17 +1141,13 @@ alloc_lvalue ()
}
static void
free_lvalue (lv)
struct lvalue *lv;
free_lvalue (struct lvalue *lv)
{
free (lv); /* should be inlined */
}
static intmax_t
expr_streval (tok, e, lvalue)
char *tok;
int e;
struct lvalue *lvalue;
expr_streval (char *tok, int e, struct lvalue *lvalue)
{
SHELL_VAR *v;
char *value;
@@ -1162,7 +1168,11 @@ expr_streval (tok, e, lvalue)
initial_depth = expr_depth;
#if defined (ARRAY_VARS)
tflag = (assoc_expand_once && already_expanded) ? AV_NOEXPAND : 0; /* for a start */
tflag = (array_expand_once && already_expanded) ? AV_NOEXPAND : 0; /* for a start */
#if 0 /* TAG:bash-5.4 https://lists.gnu.org/archive/html/bug-bash/2024-12/msg00193.html */
if (this_shell_builtin == let_builtin && shell_compatibility_level > 51)
tflag |= AV_NOEXPAND; /* we didn't quote subscripts */
#endif
#endif
/* [[[[[ */
@@ -1245,9 +1255,8 @@ expr_streval (tok, e, lvalue)
return (tval);
}
static int
_is_multiop (c)
int c;
static inline int
is_multiop (int c)
{
switch (c)
{
@@ -1272,9 +1281,8 @@ _is_multiop (c)
}
}
static int
_is_arithop (c)
int c;
static inline int
is_arithop (int c)
{
switch (c)
{
@@ -1308,16 +1316,16 @@ _is_arithop (c)
Updates value of tp. May also set tokval (for number) or tokstr (for
string). */
static void
readtok ()
readtok (void)
{
register char *cp, *xp;
register unsigned char c, c1;
register int e;
struct lvalue lval;
char *cp, *xp;
unsigned char c, c1;
int e;
/* Skip leading whitespace. */
cp = tp;
c = e = 0;
c = 0;
e = 0;
while (cp && (c = *cp) && (cr_whitespace (c)))
cp++;
@@ -1356,7 +1364,7 @@ readtok ()
e = ']';
}
else
evalerror (bash_badsub_errmsg);
evalerror (_(bash_badsub_errmsg));
}
#endif /* ARRAY_VARS */
@@ -1456,11 +1464,10 @@ readtok ()
#endif
{
/* This catches something like --FOO++ */
/* TAG:bash-5.3 add gettext calls here or make this a separate function */
if (c == '-')
evalerror ("--: assignment requires lvalue");
evalerror (_("--: assignment requires lvalue"));
else
evalerror ("++: assignment requires lvalue");
evalerror (_("++: assignment requires lvalue"));
}
else if ((c == '-' || c == '+') && c1 == c)
{
@@ -1479,9 +1486,9 @@ readtok ()
preinc and predec. */
/* This catches something like --4++ */
if (c == '-')
evalerror ("--: assignment requires lvalue");
evalerror (_("--: assignment requires lvalue"));
else
evalerror ("++: assignment requires lvalue");
evalerror (_("++: assignment requires lvalue"));
}
#else
cp--; /* not preinc or predec, so unget the character */
@@ -1492,14 +1499,14 @@ readtok ()
assigntok = c; /* a OP= b */
c = OP_ASSIGN;
}
else if (_is_arithop (c) == 0)
else if (is_arithop (c) == 0)
{
cp--;
/* use curtok, since it hasn't been copied to lasttok yet */
if (curtok == 0 || _is_arithop (curtok) || _is_multiop (curtok))
evalerror (_("syntax error: operand expected"));
if (curtok == 0 || is_arithop (curtok) || is_multiop (curtok))
evalerror (_("arithmetic syntax error: operand expected"));
else
evalerror (_("syntax error: invalid arithmetic operator"));
evalerror (_("arithmetic syntax error: invalid arithmetic operator"));
}
else
cp--; /* `unget' the character */
@@ -1515,8 +1522,7 @@ readtok ()
}
static void
evalerror (msg)
const char *msg;
evalerror (const char *msg)
{
char *name, *t;
@@ -1543,8 +1549,7 @@ evalerror (msg)
#define VALID_NUMCHAR(c) (ISALNUM(c) || ((c) == '_') || ((c) == '@'))
static intmax_t
strlong (num)
char *num;
strlong (char *num)
{
register char *s;
register unsigned char c;
@@ -1632,16 +1637,13 @@ strlong (num)
#if defined (EXPR_TEST)
void *
xmalloc (n)
int n;
xmalloc (size_t n)
{
return (malloc (n));
}
void *
xrealloc (s, n)
char *s;
int n;
xrealloc (void *s, size_t n)
{
return (realloc (s, n));
}
@@ -1653,9 +1655,7 @@ char *get_string_value () { return 0; }
procenv_t top_level;
main (argc, argv)
int argc;
char **argv;
main (int argc, char **argv)
{
register int i;
intmax_t v;
@@ -1686,8 +1686,7 @@ builtin_error (format, arg1, arg2, arg3, arg4, arg5)
}
char *
itos (n)
intmax_t n;
itos (intmax_t n)
{
return ("42");
}