overflow, since we don't call printf
- printstr,printwidestr: set field width and precision on integer
overflow the way we do in printf_builtin
- From a report by Paul Eggert <eggert@cs.ucla.edu>
+ From a report and patch by Paul Eggert <eggert@cs.ucla.edu>
bashtypes.h
- PRIdMAX: move redefinition here after including inttypes.h
builtins/printf.def,examples/loadables/seq.c,examples/loadables/getconf.h
- remove PRIdMAX redefinitions, since it's now fixed in bashtypes.h
- From a report by Paul Eggert <eggert@cs.ucla.edu>
+ From a report and patch by Paul Eggert <eggert@cs.ucla.edu>
3/15
----
sprintf family has trouble when width > INT_MAX
- expand_seqterm: use size_t instead of int for length and width
variables
- From a report by Paul Eggert <eggert@cs.ucla.edu>
+ From a report and patch by Paul Eggert <eggert@cs.ucla.edu>
+
+ 3/18
+ ----
+builtins/printf.def
+ - printstr,bexpand: changed size/length arguments to be size_t; changed
+ callers
+
+subst.c
+ - string_var_assignment: if the variable is a local variable at the
+ current scope, output a declare command even if there are no
+ attributes or value. Still tagged as tentative
+
+ 3/23
+ ----
+test.c
+ - posixtest: takes an argument, the number of test arguments to parse
+ - two_arguments,three_arguments: use advance() instead of accessing
+ and manipulating pos directly
+ - posixtest,three_arguments,two_arguments: don't set pos = argc
+ directly, rely on functions to advance pos appropriately
+ Picked up from coreutils test
+ - term: if we see a left paren, scan forward for a closing right paren
+ and use posixtest() if there are 1-4 arguments between the parens to
+ avoid ambiguous behavior for expressions like true -a \( ! -a \),
+ which a user would expect to return false instead of an error
+ message
+ Picked up from coreutils test
static inline void printf_erange (char *);
static inline void report_erange (char *, char *);
-static int printstr (char *, char *, int, int, int);
+static int printstr (char *, char *, size_t, int, int);
static int tescape (char *, char *, int *, int *);
-static char *bexpand (char *, int, int *, int *);
+static char *bexpand (char *, size_t, int *, size_t *);
static char *vbadd (char *, int);
static int vbprintf (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
static char *mklong (char *, char *, size_t);
case 'b': /* expand escapes in argument */
{
char *p, *xp;
- int rlen, r;
+ size_t rlen;
+ int r;
p = getstr ();
- ch = rlen = r = 0;
+ ch = r = 0;
+ rlen = 0;
xp = bexpand (p, strlen (p), &ch, &rlen);
if (xp)
Returns -1 on detectable write error, 0 otherwise. */
static int
-printstr (char *fmt, char *string, int len, int fieldwidth, int precision)
+printstr (char *fmt, char *string, size_t len, int fieldwidth, int precision)
{
#if 0
char *s;
}
static char *
-bexpand (char *string, int len, int *sawc, int *lenp)
+bexpand (char *string, size_t len, int *sawc, size_t *lenp)
{
int temp, c;
char *ret, *r, *s, *send;
val = (v && (invisible_p (v) || var_isset (v) == 0)) ? (char *)NULL : sh_quote_reusable (s, 0);
i = var_attribute_string (v, 0, flags);
+#if 0 /*TAG: notyet */
+ if (v && local_p (v) == 0 && variable_context > 0)
+ flags[i++] = 'g';
+#endif
/* i = strlen (flags) */
- if (i == 0 && val == 0)
+ if (i == 0 && val == 0 && (local_p (v) == 0 || v->context != variable_context))
return (char *)NULL;
ret = (char *)xmalloc (i + STRLEN (val) + strlen (v->name) + 16 + MAX_ATTRIBUTES);
+
if (i > 0 && val == 0)
- sprintf (ret, "declare -%s %s", flags, v->name);
+ sprintf (ret, "declare -%s %s", flags, v->name); /* just attributes, unset */
else if (i > 0)
- sprintf (ret, "declare -%s %s=%s", flags, v->name, val);
+ sprintf (ret, "declare -%s %s=%s", flags, v->name, val); /* attributes, set */
+#if 1 /*TAG: tentative */
+ else if (i == 0 && val && local_p (v) && variable_context == v->context)
+ sprintf (ret, "declare %s=%s", v->name, val); /* set local variable at current scope */
+ else if (i == 0 && val == 0 && local_p (v) && variable_context == v->context)
+ sprintf (ret, "declare %s", v->name); /* unset local variable at current scope, unset */
+#endif
else
sprintf (ret, "%s=%s", v->name, val);
free (val);
#define ISPRIMARY(s, c) (s[0] == '-' && s[1] == c && s[2] == '\0')
#define ANDOR(s) (s[0] == '-' && (s[1] == 'a' || s[1] == 'o') && s[2] == 0)
+/* single-character tokens like `!', `(', and `)' */
+#define ISTOKEN(s, c) (s[0] == (c) && s[1] == '\0')
+
#if !defined (R_OK)
#define R_OK 4
#define W_OK 2
static int binary_operator (void);
static int two_arguments (void);
static int three_arguments (void);
-static int posixtest (void);
+static int posixtest (int);
static int expr (void);
static int term (void);
/* A paren-bracketed argument. */
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
{
+ int nargs;
+
advance (1);
- value = expr ();
+ /* Steal an idea from coreutils and scan forward to check where the right
+ paren appears to prevent some ambiguity. If we find a valid sub-
+ expression that has 1-4 arguments, call posixtest on it ( */
+ for (nargs = 1; pos + nargs < argc && ISTOKEN (argv[pos+nargs], ')') == 0; nargs++)
+ ;
+ /* only do this if we have a valid parenthesized expression */
+ value = (pos + nargs < argc && nargs <= 4) ? posixtest (nargs) : expr ();
if (argv[pos] == 0) /* ( */
test_syntax_error (_("`)' expected"), (char *)NULL);
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
two_arguments (void)
{
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
- return (argv[pos + 1][0] == '\0');
+ {
+ advance (0);
+ return (argv[pos++][0] == '\0');
+ }
else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
int value;
if (test_binop (argv[pos+1]))
- {
- value = binary_operator ();
- pos = argc;
- }
+ value = binary_operator ();
else if (ANDOR (argv[pos+1]))
{
if (argv[pos+1][1] == 'a')
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
else
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
- pos = argc;
+ pos += 3;
}
else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = !two_arguments ();
- pos = argc;
}
- else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
+ else if (ISTOKEN (argv[pos], '(') && ISTOKEN (argv[pos+2], ')'))
{
- value = ONE_ARG_TEST(argv[pos+1]);
- pos = argc;
+ advance (0);
+ value = ONE_ARG_TEST(argv[pos]);
+ pos += 2;
}
else
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
-posixtest (void)
+posixtest (int nargs)
{
int value;
- switch (argc - 1) /* one extra passed in */
+ switch (nargs)
{
case 0:
value = FALSE;
- pos = argc;
break;
case 1:
value = ONE_ARG_TEST(argv[1]);
- pos = argc;
+ advance (0);
break;
case 2:
value = two_arguments ();
- pos = argc;
break;
case 3:
value = !three_arguments ();
break;
}
- else if (argv[pos][0] == '(' && argv[pos][1] == '\0' && argv[argc-1][0] == ')' && argv[argc-1][1] == '\0')
+ else if (ISTOKEN (argv[pos], '(') && ISTOKEN (argv[pos+3], ')'))
{
advance (1);
value = two_arguments ();
- pos = argc;
+ advance (0);
break;
}
/* FALLTHROUGH */
test_exit (SHELL_BOOLEAN (FALSE));
noeval = 0;
- value = posixtest ();
+ value = posixtest (argc - 1);
if (pos != argc)
{
2
./test.tests: line 26: test: : integer expected
2
+1
+0
+0
+./test.tests: line 26: test: `)' expected
+2
+./test.tests: line 26: test: (: unary operator expected
+2
+./test.tests: line 26: test: `)' expected
+2
t -p /dev/fd/6
1
t -p /dev/fd/6
# fixed in bash-4.0-beta
t -t ' '
+# ambiguous parenthesized test expressions with more than 4 arguments
+
+t true -a \( ! -a \)
+t true -a \( -n foo \)
+t true -a \( foo \)
+
+t 1 = 1 -a \( \)
+t \( \)
+
+t 1 = 1 -a \( \) -a -n ""
+
${THIS_SH} ./test1.sub