string (after dequoting) is not null. For instance ${v= ''} should
not have the SAWQUOTEDNULL flag set because it is " " after
expansion and dequoting, even though we saw a quoted null there
+
+ 3/6
+ ---
+lib/sh/eaccess.c
+ - sh_eaccess: AIX needs the same kind of additional checks as FreeBSD
+ and Solaris when running as root and checking whether or not a file
+ is executable. Report and fix from REIX, Tony <tony.reix@atos.net>
+
+ 3/7
+ ---
+lib/glob/glob.h
+ - GX_RECURSE: new flag, indicates internal call to glob_filename
+
+lib/glob/glob.c
+ - glob_filename: add GX_RECURSE to recursive call to glob_filename
+ - glob_filename: dequote a directory name (in the absence of a
+ filename) only if this is not a recursive call to glob_filename
+ ((flags & GX_RECURSE) == 0). Fixes bug reported by Dr. Werner Fink
+ <werner@suse.de>
+
+ 3/8
+ ---
+parse.y
+ - handle_eof_input_unit: before calling exit_builtin, set
+ last_shell_builtin and this_shell_builtin appropriately, since the
+ exit builtin uses them to determine whether or not to exit
+ immediately. Fixes bug reported by Tom Levy <tomlevy93@gmail.com>
+
+lib/sh/shquote.c
+ - sh_double_quote,sh_mkdoublequoted,sh_backslash_quote_for_double_quotes:
+ make sure to handle multibyte characters that may contain characters
+ that need to be quoted in double quotes; adding a spurious double
+ quote may turn them into different characters or uncover characters
+ that are special in double quotes. Fixes bug reported by
+ Stephane Chazelas <stephane.chazelas@gmail.com>
+ - sh_backslash_quote: don't call COPY_CHAR_P if we're in a UTF-8
+ locale and the current character doesn't have its eighth bit set;
+ only check mb_cur_max and is_basic if we're not in a UTF-8 character
+ set
+
+ 3/9
+ ---
+subst.c
+ - read_comsub: if it looks like we're starting a multibyte character,
+ read a whole multibyte character from buf and add it all at once,
+ so we don't quote each byte in a multibyte character sequence. Fixes
+ bug uncovered by shquote changes from 3/8
+
.SM
.B "SHELL BUILTIN COMMANDS"
below).
+This can have unwanted side effects if escaped portions of the string
+appear within command substitution or contain characters special to
+word expansion.
.SH READLINE
This is the library that handles reading input when using an interactive
shell, unless the
parameter expansion, command substitution, arithmetic
expansion, and quote removal, subject to the value of the
@code{promptvars} shell option (@pxref{The Shopt Builtin}).
+This can have unwanted side effects if escaped portions of the string
+appear within command substitution or contain characters special to
+word expansion.
@node The Restricted Shell
@section The Restricted Shell
if (d[directory_len - 1] == '/')
d[directory_len - 1] = '\0';
- directories = glob_filename (d, dflags);
+ directories = glob_filename (d, dflags|GX_RECURSE);
if (free_dirname)
{
free (directory_name);
return (NULL);
}
- if (directory_len > 0 && hasglob == 2) /* need to dequote */
+ /* If we have a directory name with quoted characters, and we are
+ being called recursively to glob the directory portion of a pathname,
+ we need to dequote the directory name before returning it so the
+ caller can read the directory */
+ if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0)
{
dequote_pathname (directory_name);
directory_len = strlen (directory_name);
}
+
+ /* We could check whether or not the dequoted directory_name is a
+ directory and return it here, returning the original directory_name
+ if not, but we don't do that yet. I'm not sure it matters. */
+
/* Handle GX_MARKDIRS here. */
result[0] = (char *) malloc (directory_len + 1);
if (result[0] == NULL)
#define GX_NULLDIR 0x100 /* internal -- no directory preceding pattern */
#define GX_ADDCURDIR 0x200 /* internal -- add passed directory name */
#define GX_GLOBSTAR 0x400 /* turn on special handling of ** */
+#define GX_RECURSE 0x800 /* internal -- glob_filename called recursively */
extern int glob_pattern_p __P((const char *));
extern char **glob_vector __P((char *, char *, int));
# else /* HAVE_EACCESS */ /* FreeBSD */
ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
# endif /* HAVE_EACCESS */
-# if defined (__FreeBSD__) || defined (SOLARIS)
+# if defined (__FreeBSD__) || defined (SOLARIS) || defined (_AIX)
if (ret == 0 && current_user.euid == 0 && mode == X_OK)
return (sh_stataccess (path, mode));
-# endif /* __FreeBSD__ || SOLARIS */
+# endif /* __FreeBSD__ || SOLARIS || _AIX */
return ret;
#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
return access (path, mode|EFF_ONLY_OK);
/* shquote - functions to quote and dequote strings */
-/* Copyright (C) 1999-2015 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2019 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
const char *string;
{
register unsigned char c;
+ int mb_cur_max;
char *result, *r;
- const char *s;
+ size_t slen;
+ const char *s, *send;
+ DECLARE_MBSTATE;
+
+ slen = strlen (string);
+ send = string + slen;
+ mb_cur_max = MB_CUR_MAX;
result = (char *)xmalloc (3 + (2 * strlen (string)));
r = result;
/* Backslash-newline disappears within double quotes, so don't add one. */
if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
*r++ = '\\';
-#if 0
- /* Assume that the string will not be further expanded. */
- else if (c == CTLESC || c == CTLNUL)
- *r++ = CTLESC; /* could be '\\'? */
+
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
+ {
+ COPY_CHAR_P (r, s, send);
+ s--; /* compensate for auto-increment in loop above */
+ continue;
+ }
#endif
+ /* Assume that the string will not be further expanded, so no need to
+ add CTLESC to protect CTLESC or CTLNUL. */
*r++ = c;
}
int slen, flags;
{
char *r, *ret;
- int rlen;
+ const char *send;
+ int rlen, mb_cur_max;
+ DECLARE_MBSTATE;
+ send = s + slen;
+ mb_cur_max = flags ? MB_CUR_MAX : 1;
rlen = (flags == 0) ? slen + 3 : (2 * slen) + 1;
ret = r = (char *)xmalloc (rlen);
-
+
*r++ = '"';
while (*s)
{
if (flags && *s == '"')
*r++ = '\\';
+
+#if defined (HANDLE_MULTIBYTE)
+ if (flags && ((locale_utf8locale && (*s & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (*s) == 0)))
+ {
+ COPY_CHAR_P (r, s, send);
+ continue;
+ }
+#endif
*r++ = *s++;
}
*r++ = '"';
*r++ = c;
continue;
}
- if (mb_cur_max > 1 && is_basic (c) == 0)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
{
COPY_CHAR_P (r, s, send);
s--; /* compensate for auto-increment in loop above */
char *string;
{
unsigned char c;
- char *result, *r, *s;
-
- result = (char *)xmalloc (2 * strlen (string) + 1);
+ char *result, *r, *s, *send;
+ size_t slen;
+ int mb_cur_max;
+ DECLARE_MBSTATE;
+
+ slen = strlen (string);
+ send = string + slen;
+ mb_cur_max = MB_CUR_MAX;
+ result = (char *)xmalloc (2 * slen + 1);
for (r = result, s = string; s && (c = *s); s++)
{
- if (sh_syntaxtab[c] & CBSDQUOTE)
+ /* Backslash-newline disappears within double quotes, so don't add one. */
+ if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
*r++ = '\\';
- /* I should probably add flags for these to sh_syntaxtab[] */
+ /* I should probably use the CSPECL flag for these in sh_syntaxtab[] */
else if (c == CTLESC || c == CTLNUL)
*r++ = CTLESC; /* could be '\\'? */
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
+ {
+ COPY_CHAR_P (r, s, send);
+ s--; /* compensate for auto-increment in loop above */
+ continue;
+ }
+#endif
+
*r++ = c;
}
/* In this case EOF should exit the shell. Do it now. */
reset_parser ();
+
+ last_shell_builtin = this_shell_builtin;
+ this_shell_builtin = exit_builtin;
exit_builtin ((WORD_LIST *)NULL);
}
else
int fd, quoted, flags;
int *rflag;
{
- char *istring, buf[128], *bufp;
+ char *istring, buf[512], *bufp;
int istring_index, c, tflag, skip_ctlesc, skip_ctlnul;
+ int mb_cur_max;
size_t istring_size;
ssize_t bufn;
int nullbyte;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ wchar_t wc;
+ size_t mblen;
+ int i;
+#endif
istring = (char *)NULL;
istring_index = istring_size = bufn = tflag = 0;
skip_ctlesc = ifs_cmap[CTLESC];
skip_ctlnul = ifs_cmap[CTLNUL];
+ mb_cur_max = MB_CUR_MAX;
nullbyte = 0;
/* Read the output of the command through the pipe. This may need to be
}
/* Add the character to ISTRING, possibly after resizing it. */
- RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE);
+ RESIZE_MALLOCED_BUFFER (istring, istring_index, mb_cur_max+1, istring_size, DEFAULT_ARRAY_SIZE);
/* This is essentially quote_string inline */
if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) /* || c == CTLESC || c == CTLNUL */)
else if ((skip_ctlnul == 0 && c == CTLNUL) || (c == ' ' && (ifs_value && *ifs_value == 0)))
istring[istring_index++] = CTLESC;
+#if defined (HANDLE_MULTIBYTE)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && mb_cur_max > 1 && (unsigned char)c > 127))
+ {
+ /* read a multibyte character from buf */
+ /* punt on the hard case for now */
+ memset (&ps, '\0', sizeof (mbstate_t));
+ mblen = mbrtowc (&wc, bufp-1, bufn+1, &ps);
+ if (MB_INVALIDCH (mblen) || mblen == 0 || mblen == 1)
+ istring[istring_index++] = c;
+ else
+ {
+ istring[istring_index++] = c;
+ for (i = 0; i < mblen-1; i++)
+ istring[istring_index++] = *bufp++;
+ bufn -= mblen - 1;
+ }
+ continue;
+ }
+#endif
+
istring[istring_index++] = c;
#if 0
a special case; it's the only case where a quoted string
can expand into more than one word. It's going to come back
from the above call to expand_word_internal as a list with
- a single word, in which all characters are quoted and
- separated by blanks. What we want to do is to turn it back
- into a list for the next piece of code. */
+ multiple words. */
if (list)
dequote_list (list);
-BUILD_DIR=/usr/local/build/chet/bash/bash-current
+BUILD_DIR=/usr/local/build/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
aa
<define\/\
/>
-/tmp/a/b/c /tmp/a/b/c /tmp/a/b/c
-/tmp/a/b/c /tmp/a/b/c /tmp/a/b/c
-/tmp/a/b/c
-/tmp/a/b/c
-/tmp\/a/b/c
-/tm[p]\/a/b/c
+./tmp/a/b/c ./tmp/a/b/c ./tmp/a/b/c
+./tmp/a/b/c ./tmp/a/b/c ./tmp/a/b/c
+./tmp/a/b/c
+./tmp/a/b/c
+./tmp\/a/b/c
+./tm[p]\/a/b/c
+./tmp/a/b/c
+argv[1] = <./tmp/a/*>
+argv[1] = <./tmp/a/*>
+argv[1] = <./tmp/a/b/c>
+argv[1] = <./tmp/a/*>
+argv[1] = <./tmp/a/b/c>
+argv[1] = <./t\mp/a/*>
+argv[1] = <./tmp/a/b/c>
+argv[1] = <./tmp/a/>
+argv[1] = <./tmp/a/b/>
+argv[1] = <./t\mp/a/>
+argv[1] = <./t\mp/a/b/>
+argv[1] = <./tmp/a/*>
+argv[1] = <./tmp/a/b/c>
+argv[1] = <./tmp/a>
+argv[1] = <./tmp/a/b*>
+argv[1] = <./tmp/a>
+argv[1] = <./tmp/a/b*>
+argv[1] = <./tmp/>
argv[1] = <a>
argv[2] = <abc>
argv[3] = <abd>
-mkdir -m700 /tmp/a /tmp/a/b
-touch /tmp/a/b/c
+ORIGD=$PWD
+: ${TMPDIR:=/var/tmp}
-echo /tmp/a/b/* "/tmp/a/"b/* "/tmp/a/b"/*
+SD=$TMPDIR/scratch-$$
+[ -d $SD ] || mkdir $SD
+cd $SD
+mkdir tmp
-chmod -r /tmp/a
-echo /tmp/a/b/* "/tmp/a/"b/* "/tmp/a/b"/*
-echo "/tmp/a/b"/*
+D=./tmp/a
+D1='./t\mp/a'
+
+mkdir -m700 ./tmp/a ./tmp/a/b
+touch ./tmp/a/b/c
+
+echo ./tmp/a/b/* "./tmp/a/"b/* "./tmp/a/b"/*
+
+chmod -r ./tmp/a
+echo ./tmp/a/b/* "./tmp/a/"b/* "./tmp/a/b"/*
+echo "./tmp/a/b"/*
bs=\\
-echo /tmp${bs}/a/b/*
-echo /tmp${bs}/a/b/c
-echo /tm[p]${bs}/a/b/c
+echo ./tmp${bs}/a/b/*
+echo ./tmp${bs}/a/b/c
+echo ./tm[p]${bs}/a/b/c
+echo ./t${bs}mp/a/b/*
+
+recho "./tmp/a"/*
+recho "$D"/*
+recho "$D"/b/*
+
+recho $D/*
+recho $D/b/*
+recho $D1/*
+recho $D1/b/*
+recho $D/
+recho $D/b/
+recho $D1/
+recho $D1/b/
+
+recho ./t\mp/a/*
+recho ./t\mp/a/b/*
+
+recho ./tmp/a*
+recho ./tmp/a/b*
+recho ./t\mp/a*
+recho ./t\mp/a/b*
+
+recho ./t\mp/
+
+chmod +r ./tmp/a
+rm -rf ./tmp/a
-chmod +r /tmp/a
-rm -rf /tmp/a
+cd $ORIGD
+rm -rf $SD