commit bash-20041027 snapshot

This commit is contained in:
Chet Ramey
2011-12-03 13:38:37 -05:00
parent c327176389
commit d90269dd00
54 changed files with 10370 additions and 41 deletions
+36
View File
@@ -10431,3 +10431,39 @@ lib/readline/text.c
multibyte locale) using above utility functions
- fix rl_backward_word to work with multibyte characters (or in a
multibyte locale) using above utility functions
10/26
-----
parse.y
- fix parse_matched_pair so that it doesn't swallow \<newline> when
parsing a $'...' construct (call shell_getc with different arg)
10/28
-----
lib/glob/glob.c
- after some (compiled-in) threshold, glob_vector will stop using
alloca to allocate `struct globval's and will switch to using
malloc, with appropriate cleanup before returning
subst.c
- don't expand tildes after `=' in expand_word_internal, even if the
W_TILDEEXP flag is set, unless it's the first tilde in a word
marked W_ASSIGNMENT
10/31
-----
lib/readline/text.c
- make sure rl_point doesn't go below 0 in rl_delete_horizontal_space
(from SUSE, but not sent in)
shell.c
- make sure shell_is_restricted skips over a single leading `-' in
the shell name (from SUSE, but not sent in)
lib/readline/display.c
- disable `fast redisplay' at the end of the line if in a locale that
supports multibyte characters (from SUSE, but not sent in)
lib/readline/histexpand.c
- fix a problem with finding the delimiter of a `?' substring when
compiled for multibyte characters (from SUSE, but not sent in)
+37
View File
@@ -10422,7 +10422,44 @@ lib/readline/mbutil.c
lib/readline/rlmbutil.h
- extern defines for _rl_walphabetic and _rl_char_value for when
multibyte chars are not being used
- new wrapper definitions for _rl_find_next_mbchar (MB_NEXTCHAR) and
_rl_find_prev_mbchar (MB_PREVCHAR) that try to avoid unneeded
function calls
lib/readline/text.c
- fix rl_foward_word to work with multibyte characters (or in a
multibyte locale) using above utility functions
- fix rl_backward_word to work with multibyte characters (or in a
multibyte locale) using above utility functions
10/26
-----
parse.y
- fix parse_matched_pair so that it doesn't swallow \<newline> when
parsing a $'...' construct (call shell_getc with different arg)
10/28
-----
lib/glob/glob.c
- after some (compiled-in) threshold, glob_vector will stop using
alloca to allocate `struct globval's and will switch to using
malloc, with appropriate cleanup before returning
subst.c
- don't expand tildes after `=' in expand_word_internal, even if the
W_TILDEEXP flag is set, unless it's the first tilde in a word
marked W_ASSIGNMENT
10/31
-----
lib/readline/text.c
- make sure rl_point doesn't go below 0 in rl_delete_horizontal_space
(from SUSE, but not sent in)
shell.c
- make sure shell_is_restricted skips over a single leading `-' in
the shell name (from SUSE, but not sent in)
lib/readline/display.c
- disable `fast redisplay' at the end of the line if in a locale that
supports multibyte characters (from SUSE, but not sent in)
+5
View File
@@ -683,6 +683,8 @@ tests/arith1.sub f
tests/arith2.sub f
tests/array.tests f
tests/array.right f
tests/array1.sub f
tests/array2.sub f
tests/array-at-star f
tests/array2.right f
tests/braces.tests f
@@ -764,6 +766,8 @@ tests/ifs.right f
tests/input-line.sh f
tests/input-line.sub f
tests/input.right f
tests/intl.tests f
tests/intl.right f
tests/invert.tests f
tests/invert.right f
tests/jobs.tests f
@@ -852,6 +856,7 @@ tests/run-histexpand f
tests/run-history f
tests/run-ifs f
tests/run-input-test f
tests/run-intl f
tests/run-invert f
tests/run-jobs f
tests/run-more-exp f
+11 -1
View File
@@ -408,6 +408,7 @@ lib/sh/strftime.c f
lib/sh/strindex.c f
lib/sh/stringlist.c f
lib/sh/stringvec.c f
lib/sh/strnlen.c f
lib/sh/strpbrk.c f
lib/sh/strstr.c f
lib/sh/strtod.c f
@@ -730,6 +731,8 @@ tests/extglob.tests f
tests/extglob.right f
tests/extglob2.tests f
tests/extglob2.right f
tests/extglob3.tests f
tests/extglob3.right f
tests/func.tests f
tests/func.right f
tests/func1.sub f
@@ -761,6 +764,8 @@ tests/ifs.right f
tests/input-line.sh f
tests/input-line.sub f
tests/input.right f
tests/intl.tests f
tests/intl.right f
tests/invert.tests f
tests/invert.right f
tests/jobs.tests f
@@ -839,6 +844,7 @@ tests/run-execscript f
tests/run-exp-tests f
tests/run-extglob f
tests/run-extglob2 f
tests/run-extglob3 f
tests/run-func f
tests/run-getopts f
tests/run-glob-test f
@@ -848,6 +854,7 @@ tests/run-histexpand f
tests/run-history f
tests/run-ifs f
tests/run-input-test f
tests/run-intl f
tests/run-invert f
tests/run-jobs f
tests/run-more-exp f
@@ -872,6 +879,7 @@ tests/run-shopt f
tests/run-strip f
tests/run-test f
tests/run-tilde f
tests/run-tilde2 f
tests/run-trap f
tests/run-type f
tests/run-varenv f
@@ -885,8 +893,10 @@ tests/strip.tests f
tests/strip.right f
tests/test.tests f
tests/test.right f
tests/tilde-tests f
tests/tilde.tests f
tests/tilde.right f
tests/tilde2.tests f
tests/tilde2.right f
tests/trap.tests f
tests/trap.right f
tests/trap1.sub f 755
+5 -5
View File
@@ -6,12 +6,12 @@
.\" Case Western Reserve University
.\" chet@po.CWRU.Edu
.\"
.\" Last Change: Sat Oct 2 18:05:57 EDT 2004
.\" Last Change: Sat Oct 30 22:24:07 EDT 2004
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
.TH BASH 1 "2004 Oct 2" "GNU Bash-3.1-devel"
.TH BASH 1 "2004 Oct 30" "GNU Bash-3.1-devel"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
@@ -8541,9 +8541,9 @@ subsequently reset. The exit status is true unless a
.I name
is readonly.
.TP
\fBwait\fP [\fIn\fP]
Wait for the specified process and return its termination
status.
\fBwait\fP [\fIn ...\fP]
Wait for each specified process and return its termination status.
Each
.I n
may be a process
ID or a job specification; if a job spec is given, all processes
+5
View File
@@ -4516,6 +4516,11 @@ If set to \fBnone\fP, readline never rings the bell. If set to
\fBvisible\fP, readline uses a visible bell if one is available.
If set to \fBaudible\fP, readline attempts to ring the terminal's bell.
.TP
.B bind\-tty\-special\-chars (On)
If set to \fBOn\fP, readline attempts to bind the control characters
treated specially by the kernel's terminal driver to their readline
equivalents.
.TP
.B comment\-begin (``#'')
The string that is inserted when the readline
.B insert\-comment
+4 -4
View File
@@ -6252,11 +6252,11 @@ or non-zero if an error occurs or an invalid option is encountered.
@item wait
@btindex wait
@example
wait [@var{jobspec} or @var{pid}]
wait [@var{jobspec} or @var{pid} ...]
@end example
Wait until the child process specified by process @sc{id} @var{pid} or job
specification @var{jobspec} exits and return the exit status of the last
command waited for.
Wait until the child process specified by each process @sc{id} @var{pid}
or job specification @var{jobspec} exits and return the exit status of the
last command waited for.
If a job spec is given, all processes in the job are waited for.
If no arguments are given, all currently active child processes are
waited for, and the return status is zero.
+4
View File
@@ -2017,6 +2017,10 @@ connection to the corresponding socket.
A failure to open or create a file causes the redirection to fail.
Redirections using file descriptors greater than 9 should be used with
care, as they may conflict with file descriptors the shell uses
internally.
@subsection Redirecting Input
Redirection of input causes the file whose name results from
the expansion of @var{word}
+2 -2
View File
@@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc.
@set EDITION 3.1-devel
@set VERSION 3.1-devel
@set UPDATED 20 October 2004
@set UPDATED 30 October 2004
@set UPDATED-MONTH October 2004
@set LASTCHANGE Wed Oct 20 09:54:44 EDT 2004
@set LASTCHANGE Sat Oct 30 22:24:26 EDT 2004
+2 -2
View File
@@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc.
@set EDITION 3.1-devel
@set VERSION 3.1-devel
@set UPDATED 9 October 2004
@set UPDATED 20 October 2004
@set UPDATED-MONTH October 2004
@set LASTCHANGE Sat Oct 9 18:40:05 EDT 2004
@set LASTCHANGE Wed Oct 20 09:54:44 EDT 2004
+49 -4
View File
@@ -66,6 +66,12 @@
# define FREE(x) if (x) free (x)
#endif
/* Don't try to alloca() more than this much memory for `struct globval'
in glob_vector() */
#ifndef ALLOCA_MAX
# define ALLOCA_MAX 100000
#endif
extern void throw_to_top_level __P((void));
extern int test_eaccess __P((char *, int));
@@ -347,10 +353,14 @@ glob_vector (pat, dir, flags)
register char **name_vector;
register unsigned int i;
int mflags; /* Flags passed to strmatch (). */
int nalloca;
struct globval *firstmalloc, *tmplink;
lastlink = 0;
count = lose = skip = 0;
firstmalloc = 0;
/* If PAT is empty, skip the loop, but return one (empty) filename. */
if (pat == 0 || *pat == '\0')
{
@@ -488,8 +498,18 @@ glob_vector (pat, dir, flags)
if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
{
if (nalloca < ALLOCA_MAX)
{
nextlink = (struct globval *) alloca (sizeof (struct globval));
nalloca += sizeof (struct globval);
}
else
{
nextlink = (struct globval *) malloc (sizeof (struct globval));
if (firstmalloc == 0)
firstmalloc = nextlink;
}
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
nextlink = (struct globval *) alloca (sizeof (struct globval));
if (nextlink == 0 || nextname == 0)
{
lose = 1;
@@ -515,11 +535,20 @@ glob_vector (pat, dir, flags)
/* Have we run out of memory? */
if (lose)
{
tmplink = 0;
/* Here free the strings we have got. */
while (lastlink)
{
if (firstmalloc)
{
if (lastlink == firstmalloc)
firstmalloc = 0;
tmplink = lastlink;
}
free (lastlink->name);
lastlink = lastlink->next;
FREE (tmplink);
}
QUIT;
@@ -528,13 +557,29 @@ glob_vector (pat, dir, flags)
}
/* Copy the name pointers from the linked list into the vector. */
for (i = 0; i < count; ++i)
for (tmplink = lastlink, i = 0; i < count; ++i)
{
name_vector[i] = lastlink->name;
lastlink = lastlink->next;
name_vector[i] = tmplink->name;
tmplink = tmplink->next;
}
name_vector[count] = NULL;
/* If we allocated some of the struct globvals, free them now. */
if (firstmalloc)
{
tmplink = 0;
while (lastlink)
{
tmplink = lastlink;
if (lastlink == firstmalloc)
lastlink = firstmalloc = 0;
else
lastlink = lastlink->next;
free (tmplink);
}
}
return (name_vector);
}
+888
View File
@@ -0,0 +1,888 @@
/* glob.c -- file-name wildcard pattern matching for Bash.
Copyright (C) 1985-2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
/* To whomever it may concern: I have never seen the code which most
Unix programs use to perform this function. I wrote this from scratch
based on specifications for the pattern matching. --RMS. */
#include <config.h>
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
#pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */
#include "bashtypes.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include "posixdir.h"
#include "posixstat.h"
#include "shmbutil.h"
#include "xmalloc.h"
#include "filecntl.h"
#if !defined (F_OK)
# define F_OK 0
#endif
#include "stdc.h"
#include "memalloc.h"
#include "quit.h"
#include "glob.h"
#include "strmatch.h"
#if !defined (HAVE_BCOPY) && !defined (bcopy)
# define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
#endif /* !HAVE_BCOPY && !bcopy */
#if !defined (NULL)
# if defined (__STDC__)
# define NULL ((void *) 0)
# else
# define NULL 0x0
# endif /* __STDC__ */
#endif /* !NULL */
#if !defined (FREE)
# define FREE(x) if (x) free (x)
#endif
/* Don't try to alloca() more than this much memory for `struct globval'
in glob_vector() */
#ifndef ALLOCA_MAX
# define ALLOCA_MAX 128 /* Testing, was 100000 */
#endif
extern void throw_to_top_level __P((void));
extern int test_eaccess __P((char *, int));
extern int extended_glob;
/* Global variable which controls whether or not * matches .*.
Non-zero means don't match .*. */
int noglob_dot_filenames = 1;
/* Global variable which controls whether or not filename globbing
is done without regard to case. */
int glob_ignore_case = 0;
/* Global variable to return to signify an error in globbing. */
char *glob_error_return;
/* Some forward declarations. */
static int skipname __P((char *, char *));
#if HANDLE_MULTIBYTE
static int mbskipname __P((char *, char *));
#endif
#if HANDLE_MULTIBYTE
static void udequote_pathname __P((char *));
static void wdequote_pathname __P((char *));
#else
# define dequote_pathname udequote_pathname
#endif
static void dequote_pathname __P((char *));
static int glob_testdir __P((char *));
static char **glob_dir_to_array __P((char *, char **, int));
/* Compile `glob_loop.c' for single-byte characters. */
#define CHAR unsigned char
#define INT int
#define L(CS) CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_pattern_p
#include "glob_loop.c"
/* Compile `glob_loop.c' again for multibyte characters. */
#if HANDLE_MULTIBYTE
#define CHAR wchar_t
#define INT wint_t
#define L(CS) L##CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_wpattern_p
#include "glob_loop.c"
#endif /* HANDLE_MULTIBYTE */
/* And now a function that calls either the single-byte or multibyte version
of internal_glob_pattern_p. */
int
glob_pattern_p (pattern)
const char *pattern;
{
#if HANDLE_MULTIBYTE
size_t n;
wchar_t *wpattern;
int r;
if (MB_CUR_MAX == 1)
return (internal_glob_pattern_p (pattern));
/* Convert strings to wide chars, and call the multibyte version. */
n = xdupmbstowcs (&wpattern, NULL, pattern);
if (n == (size_t)-1)
/* Oops. Invalid multibyte sequence. Try it as single-byte sequence. */
return (internal_glob_pattern_p (pattern));
r = internal_glob_wpattern_p (wpattern);
free (wpattern);
return r;
#else
return (internal_glob_pattern_p (pattern));
#endif
}
/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned
with matching leading `.'. */
static int
skipname (pat, dname)
char *pat;
char *dname;
{
/* If a leading dot need not be explicitly matched, and the pattern
doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.') &&
(dname[0] == '.' &&
(dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
return 1;
/* If a dot must be explicity matched, check to see if they do. */
else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.'))
return 1;
return 0;
}
#if HANDLE_MULTIBYTE
/* Return 1 if DNAME should be skipped according to PAT. Handles multibyte
characters in PAT and DNAME. Mostly concerned with matching leading `.'. */
static int
mbskipname (pat, dname)
char *pat, *dname;
{
int ret;
wchar_t *pat_wc, *dn_wc;
size_t pat_n, dn_n, n;
pat_n = xdupmbstowcs (&pat_wc, NULL, pat);
dn_n = xdupmbstowcs (&dn_wc, NULL, dname);
ret = 0;
if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
{
/* If a leading dot need not be explicitly matched, and the
pattern doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.') &&
(dn_wc[0] == L'.' &&
(dn_wc[1] == L'\0' || (dn_wc[1] == L'.' && dn_wc[2] == L'\0'))))
ret = 1;
/* If a leading dot must be explicity matched, check to see if the
pattern and dirname both have one. */
else if (noglob_dot_filenames && dn_wc[0] == L'.' &&
pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.'))
ret = 1;
}
FREE (pat_wc);
FREE (dn_wc);
return ret;
}
#endif /* HANDLE_MULTIBYTE */
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
udequote_pathname (pathname)
char *pathname;
{
register int i, j;
for (i = j = 0; pathname && pathname[i]; )
{
if (pathname[i] == '\\')
i++;
pathname[j++] = pathname[i++];
if (pathname[i - 1] == 0)
break;
}
pathname[j] = '\0';
}
#if HANDLE_MULTIBYTE
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
wdequote_pathname (pathname)
char *pathname;
{
mbstate_t ps;
size_t len, n;
wchar_t *wpathname;
int i, j;
wchar_t *orig_wpathname;
len = strlen (pathname);
/* Convert the strings into wide characters. */
n = xdupmbstowcs (&wpathname, NULL, pathname);
if (n == (size_t) -1)
/* Something wrong. */
return;
orig_wpathname = wpathname;
for (i = j = 0; wpathname && wpathname[i]; )
{
if (wpathname[i] == L'\\')
i++;
wpathname[j++] = wpathname[i++];
if (wpathname[i - 1] == L'\0')
break;
}
wpathname[j] = L'\0';
/* Convert the wide character string into unibyte character set. */
memset (&ps, '\0', sizeof(mbstate_t));
n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
pathname[len] = '\0';
/* Can't just free wpathname here; wcsrtombs changes it in many cases. */
free (orig_wpathname);
}
static void
dequote_pathname (pathname)
char *pathname;
{
if (MB_CUR_MAX > 1)
wdequote_pathname (pathname);
else
udequote_pathname (pathname);
}
#endif /* HANDLE_MULTIBYTE */
/* Test whether NAME exists. */
#if defined (HAVE_LSTAT)
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
# if !defined (AFS)
# define GLOB_TESTNAME(name) (test_eaccess (nextname, F_OK))
# else /* AFS */
# define GLOB_TESTNAME(name) (access (nextname, F_OK))
# endif /* AFS */
#endif /* !HAVE_LSTAT */
/* Return 0 if DIR is a directory, -1 otherwise. */
static int
glob_testdir (dir)
char *dir;
{
struct stat finfo;
if (stat (dir, &finfo) < 0)
return (-1);
if (S_ISDIR (finfo.st_mode) == 0)
return (-1);
return (0);
}
/* Return a vector of names of files in directory DIR
whose names match glob pattern PAT.
The names are not in any particular order.
Wildcards at the beginning of PAT do not match an initial period.
The vector is terminated by an element that is a null pointer.
To free the space allocated, first free the vector's elements,
then free the vector.
Return 0 if cannot get enough memory to hold the pointer
and the names.
Return -1 if cannot access directory DIR.
Look in errno for more information. */
char **
glob_vector (pat, dir, flags)
char *pat;
char *dir;
int flags;
{
struct globval
{
struct globval *next;
char *name;
};
DIR *d;
register struct dirent *dp;
struct globval *lastlink;
register struct globval *nextlink;
register char *nextname, *npat;
unsigned int count;
int lose, skip;
register char **name_vector;
register unsigned int i;
int mflags; /* Flags passed to strmatch (). */
int nalloca;
struct globval *firstmalloc, *tmplink;
lastlink = 0;
count = lose = skip = 0;
firstmalloc = 0;
/* If PAT is empty, skip the loop, but return one (empty) filename. */
if (pat == 0 || *pat == '\0')
{
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
nextlink = (struct globval *)alloca (sizeof (struct globval));
if (nextlink == NULL)
return ((char **) NULL);
nextlink->next = (struct globval *)0;
nextname = (char *) malloc (1);
if (nextname == 0)
lose = 1;
else
{
lastlink = nextlink;
nextlink->name = nextname;
nextname[0] = '\0';
count = 1;
}
skip = 1;
}
/* If the filename pattern (PAT) does not contain any globbing characters,
we can dispense with reading the directory, and just see if there is
a filename `DIR/PAT'. If there is, and we can access it, just make the
vector to return and bail immediately. */
if (skip == 0 && glob_pattern_p (pat) == 0)
{
int dirlen;
struct stat finfo;
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
dirlen = strlen (dir);
nextname = (char *)malloc (dirlen + strlen (pat) + 2);
npat = (char *)malloc (strlen (pat) + 1);
if (nextname == 0 || npat == 0)
lose = 1;
else
{
strcpy (npat, pat);
dequote_pathname (npat);
strcpy (nextname, dir);
nextname[dirlen++] = '/';
strcpy (nextname + dirlen, npat);
if (GLOB_TESTNAME (nextname) >= 0)
{
free (nextname);
nextlink = (struct globval *)alloca (sizeof (struct globval));
if (nextlink)
{
nextlink->next = (struct globval *)0;
lastlink = nextlink;
nextlink->name = npat;
count = 1;
}
else
lose = 1;
}
else
{
free (nextname);
free (npat);
}
}
skip = 1;
}
if (skip == 0)
{
/* Open the directory, punting immediately if we cannot. If opendir
is not robust (i.e., it opens non-directories successfully), test
that DIR is a directory and punt if it's not. */
#if defined (OPENDIR_NOT_ROBUST)
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
#endif
d = opendir (dir);
if (d == NULL)
return ((char **) &glob_error_return);
/* Compute the flags that will be passed to strmatch(). We don't
need to do this every time through the loop. */
mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
#ifdef FNM_CASEFOLD
if (glob_ignore_case)
mflags |= FNM_CASEFOLD;
#endif
if (extended_glob)
mflags |= FNM_EXTMATCH;
/* Scan the directory, finding all names that match.
For each name that matches, allocate a struct globval
on the stack and store the name in it.
Chain those structs together; lastlink is the front of the chain. */
while (1)
{
/* Make globbing interruptible in the shell. */
if (interrupt_state)
{
lose = 1;
break;
}
dp = readdir (d);
if (dp == NULL)
break;
/* If this directory entry is not to be used, try again. */
if (REAL_DIR_ENTRY (dp) == 0)
continue;
#if 0
if (dp->d_name == 0 || *dp->d_name == 0)
continue;
#endif
#if HANDLE_MULTIBYTE
if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name))
continue;
else
#endif
if (skipname (pat, dp->d_name))
continue;
if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
{
if (nalloca < ALLOCA_MAX)
{
nextlink = (struct globval *) alloca (sizeof (struct globval));
nalloca += sizeof (struct globval);
}
else
{
nextlink = (struct globval *) malloc (sizeof (struct globval));
if (firstmalloc == 0)
firstmalloc = nextlink;
}
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
if (nextlink == 0 || nextname == 0)
{
lose = 1;
break;
}
nextlink->next = lastlink;
lastlink = nextlink;
nextlink->name = nextname;
bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
++count;
}
}
(void) closedir (d);
}
if (lose == 0)
{
name_vector = (char **) malloc ((count + 1) * sizeof (char *));
lose |= name_vector == NULL;
}
/* Have we run out of memory? */
if (lose)
{
tmplink = 0;
/* Here free the strings we have got. */
while (lastlink)
{
if (firstmalloc)
{
if (lastlink == firstmalloc)
firstmalloc = 0;
tmplink = lastlink;
}
free (lastlink->name);
lastlink = lastlink->next;
FREE (tmplink);
}
QUIT;
return ((char **)NULL);
}
tmplink = lastlink; /* save in case we need to free */
/* Copy the name pointers from the linked list into the vector. */
for (i = 0; i < count; ++i)
{
name_vector[i] = lastlink->name;
lastlink = lastlink->next;
}
lastlink = tmplink; /* restore in case we need to free */
name_vector[count] = NULL;
/* If we allocated some of the struct globvals, free them now. */
if (firstmalloc)
{
tmplink = 0;
while (lastlink)
{
tmplink = lastlink;
if (lastlink == firstmalloc)
lastlink = firstmalloc = 0;
else
lastlink = lastlink->next;
free (tmplink);
}
}
return (name_vector);
}
/* Return a new array which is the concatenation of each string in ARRAY
to DIR. This function expects you to pass in an allocated ARRAY, and
it takes care of free()ing that array. Thus, you might think of this
function as side-effecting ARRAY. This should handle GX_MARKDIRS. */
static char **
glob_dir_to_array (dir, array, flags)
char *dir, **array;
int flags;
{
register unsigned int i, l;
int add_slash;
char **result, *new;
struct stat sb;
l = strlen (dir);
if (l == 0)
{
if (flags & GX_MARKDIRS)
for (i = 0; array[i]; i++)
{
if ((stat (array[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
l = strlen (array[i]);
new = (char *)realloc (array[i], l + 2);
if (new == 0)
return NULL;
new[l] = '/';
new[l+1] = '\0';
array[i] = new;
}
}
return (array);
}
add_slash = dir[l - 1] != '/';
i = 0;
while (array[i] != NULL)
++i;
result = (char **) malloc ((i + 1) * sizeof (char *));
if (result == NULL)
return (NULL);
for (i = 0; array[i] != NULL; i++)
{
/* 3 == 1 for NUL, 1 for slash at end of DIR, 1 for GX_MARKDIRS */
result[i] = (char *) malloc (l + strlen (array[i]) + 3);
if (result[i] == NULL)
return (NULL);
strcpy (result[i], dir);
if (add_slash)
result[i][l] = '/';
strcpy (result[i] + l + add_slash, array[i]);
if (flags & GX_MARKDIRS)
{
if ((stat (result[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
size_t rlen;
rlen = strlen (result[i]);
result[i][rlen] = '/';
result[i][rlen+1] = '\0';
}
}
}
result[i] = NULL;
/* Free the input array. */
for (i = 0; array[i] != NULL; i++)
free (array[i]);
free ((char *) array);
return (result);
}
/* Do globbing on PATHNAME. Return an array of pathnames that match,
marking the end of the array with a null-pointer as an element.
If no pathnames match, then the array is empty (first element is null).
If there isn't enough memory, then return NULL.
If a file system error occurs, return -1; `errno' has the error code. */
char **
glob_filename (pathname, flags)
char *pathname;
int flags;
{
char **result;
unsigned int result_size;
char *directory_name, *filename;
unsigned int directory_len;
int free_dirname; /* flag */
result = (char **) malloc (sizeof (char *));
result_size = 1;
if (result == NULL)
return (NULL);
result[0] = NULL;
directory_name = NULL;
/* Find the filename. */
filename = strrchr (pathname, '/');
if (filename == NULL)
{
filename = pathname;
directory_name = "";
directory_len = 0;
free_dirname = 0;
}
else
{
directory_len = (filename - pathname) + 1;
directory_name = (char *) malloc (directory_len + 1);
if (directory_name == 0) /* allocation failed? */
return (NULL);
bcopy (pathname, directory_name, directory_len);
directory_name[directory_len] = '\0';
++filename;
free_dirname = 1;
}
/* If directory_name contains globbing characters, then we
have to expand the previous levels. Just recurse. */
if (glob_pattern_p (directory_name))
{
char **directories;
register unsigned int i;
if (directory_name[directory_len - 1] == '/')
directory_name[directory_len - 1] = '\0';
directories = glob_filename (directory_name, flags & ~GX_MARKDIRS);
if (free_dirname)
{
free (directory_name);
directory_name = NULL;
}
if (directories == NULL)
goto memory_error;
else if (directories == (char **)&glob_error_return)
{
free ((char *) result);
return ((char **) &glob_error_return);
}
else if (*directories == NULL)
{
free ((char *) directories);
free ((char *) result);
return ((char **) &glob_error_return);
}
/* We have successfully globbed the preceding directory name.
For each name in DIRECTORIES, call glob_vector on it and
FILENAME. Concatenate the results together. */
for (i = 0; directories[i] != NULL; ++i)
{
char **temp_results;
/* Scan directory even on a NULL pathname. That way, `*h/'
returns only directories ending in `h', instead of all
files ending in `h' with a `/' appended. */
temp_results = glob_vector (filename, directories[i], flags & ~GX_MARKDIRS);
/* Handle error cases. */
if (temp_results == NULL)
goto memory_error;
else if (temp_results == (char **)&glob_error_return)
/* This filename is probably not a directory. Ignore it. */
;
else
{
char **array;
register unsigned int l;
array = glob_dir_to_array (directories[i], temp_results, flags);
l = 0;
while (array[l] != NULL)
++l;
result =
(char **)realloc (result, (result_size + l) * sizeof (char *));
if (result == NULL)
goto memory_error;
for (l = 0; array[l] != NULL; ++l)
result[result_size++ - 1] = array[l];
result[result_size - 1] = NULL;
/* Note that the elements of ARRAY are not freed. */
free ((char *) array);
}
}
/* Free the directories. */
for (i = 0; directories[i]; i++)
free (directories[i]);
free ((char *) directories);
return (result);
}
/* If there is only a directory name, return it. */
if (*filename == '\0')
{
result = (char **) realloc ((char *) result, 2 * sizeof (char *));
if (result == NULL)
return (NULL);
/* Handle GX_MARKDIRS here. */
result[0] = (char *) malloc (directory_len + 1);
if (result[0] == NULL)
goto memory_error;
bcopy (directory_name, result[0], directory_len + 1);
if (free_dirname)
free (directory_name);
result[1] = NULL;
return (result);
}
else
{
char **temp_results;
/* There are no unquoted globbing characters in DIRECTORY_NAME.
Dequote it before we try to open the directory since there may
be quoted globbing characters which should be treated verbatim. */
if (directory_len > 0)
dequote_pathname (directory_name);
/* We allocated a small array called RESULT, which we won't be using.
Free that memory now. */
free (result);
/* Just return what glob_vector () returns appended to the
directory name. */
temp_results = glob_vector (filename,
(directory_len == 0 ? "." : directory_name),
flags & ~GX_MARKDIRS);
if (temp_results == NULL || temp_results == (char **)&glob_error_return)
{
if (free_dirname)
free (directory_name);
return (temp_results);
}
result = glob_dir_to_array (directory_name, temp_results, flags);
if (free_dirname)
free (directory_name);
return (result);
}
/* We get to memory_error if the program has run out of memory, or
if this is the shell, and we have been interrupted. */
memory_error:
if (result != NULL)
{
register unsigned int i;
for (i = 0; result[i] != NULL; ++i)
free (result[i]);
free ((char *) result);
}
if (free_dirname && directory_name)
free (directory_name);
QUIT;
return (NULL);
}
#if defined (TEST)
main (argc, argv)
int argc;
char **argv;
{
unsigned int i;
for (i = 1; i < argc; ++i)
{
char **value = glob_filename (argv[i], 0);
if (value == NULL)
puts ("Out of memory.");
else if (value == &glob_error_return)
perror (argv[i]);
else
for (i = 0; value[i] != NULL; i++)
puts (value[i]);
}
exit (0);
}
#endif /* TEST. */
+885
View File
@@ -0,0 +1,885 @@
/* glob.c -- file-name wildcard pattern matching for Bash.
Copyright (C) 1985-2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
/* To whomever it may concern: I have never seen the code which most
Unix programs use to perform this function. I wrote this from scratch
based on specifications for the pattern matching. --RMS. */
#include <config.h>
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
#pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */
#include "bashtypes.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include "posixdir.h"
#include "posixstat.h"
#include "shmbutil.h"
#include "xmalloc.h"
#include "filecntl.h"
#if !defined (F_OK)
# define F_OK 0
#endif
#include "stdc.h"
#include "memalloc.h"
#include "quit.h"
#include "glob.h"
#include "strmatch.h"
#if !defined (HAVE_BCOPY) && !defined (bcopy)
# define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
#endif /* !HAVE_BCOPY && !bcopy */
#if !defined (NULL)
# if defined (__STDC__)
# define NULL ((void *) 0)
# else
# define NULL 0x0
# endif /* __STDC__ */
#endif /* !NULL */
#if !defined (FREE)
# define FREE(x) if (x) free (x)
#endif
/* Don't try to alloca() more than this much memory for `struct globval'
in glob_vector() */
#ifndef ALLOCA_MAX
# define ALLOCA_MAX 128 /* Testing, was 100000 */
#endif
extern void throw_to_top_level __P((void));
extern int test_eaccess __P((char *, int));
extern int extended_glob;
/* Global variable which controls whether or not * matches .*.
Non-zero means don't match .*. */
int noglob_dot_filenames = 1;
/* Global variable which controls whether or not filename globbing
is done without regard to case. */
int glob_ignore_case = 0;
/* Global variable to return to signify an error in globbing. */
char *glob_error_return;
/* Some forward declarations. */
static int skipname __P((char *, char *));
#if HANDLE_MULTIBYTE
static int mbskipname __P((char *, char *));
#endif
#if HANDLE_MULTIBYTE
static void udequote_pathname __P((char *));
static void wdequote_pathname __P((char *));
#else
# define dequote_pathname udequote_pathname
#endif
static void dequote_pathname __P((char *));
static int glob_testdir __P((char *));
static char **glob_dir_to_array __P((char *, char **, int));
/* Compile `glob_loop.c' for single-byte characters. */
#define CHAR unsigned char
#define INT int
#define L(CS) CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_pattern_p
#include "glob_loop.c"
/* Compile `glob_loop.c' again for multibyte characters. */
#if HANDLE_MULTIBYTE
#define CHAR wchar_t
#define INT wint_t
#define L(CS) L##CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_wpattern_p
#include "glob_loop.c"
#endif /* HANDLE_MULTIBYTE */
/* And now a function that calls either the single-byte or multibyte version
of internal_glob_pattern_p. */
int
glob_pattern_p (pattern)
const char *pattern;
{
#if HANDLE_MULTIBYTE
size_t n;
wchar_t *wpattern;
int r;
if (MB_CUR_MAX == 1)
return (internal_glob_pattern_p (pattern));
/* Convert strings to wide chars, and call the multibyte version. */
n = xdupmbstowcs (&wpattern, NULL, pattern);
if (n == (size_t)-1)
/* Oops. Invalid multibyte sequence. Try it as single-byte sequence. */
return (internal_glob_pattern_p (pattern));
r = internal_glob_wpattern_p (wpattern);
free (wpattern);
return r;
#else
return (internal_glob_pattern_p (pattern));
#endif
}
/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned
with matching leading `.'. */
static int
skipname (pat, dname)
char *pat;
char *dname;
{
/* If a leading dot need not be explicitly matched, and the pattern
doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.') &&
(dname[0] == '.' &&
(dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
return 1;
/* If a dot must be explicity matched, check to see if they do. */
else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.'))
return 1;
return 0;
}
#if HANDLE_MULTIBYTE
/* Return 1 if DNAME should be skipped according to PAT. Handles multibyte
characters in PAT and DNAME. Mostly concerned with matching leading `.'. */
static int
mbskipname (pat, dname)
char *pat, *dname;
{
int ret;
wchar_t *pat_wc, *dn_wc;
size_t pat_n, dn_n, n;
pat_n = xdupmbstowcs (&pat_wc, NULL, pat);
dn_n = xdupmbstowcs (&dn_wc, NULL, dname);
ret = 0;
if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
{
/* If a leading dot need not be explicitly matched, and the
pattern doesn't start with a `.', don't match `.' or `..' */
if (noglob_dot_filenames == 0 && pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.') &&
(dn_wc[0] == L'.' &&
(dn_wc[1] == L'\0' || (dn_wc[1] == L'.' && dn_wc[2] == L'\0'))))
ret = 1;
/* If a leading dot must be explicity matched, check to see if the
pattern and dirname both have one. */
else if (noglob_dot_filenames && dn_wc[0] == L'.' &&
pat_wc[0] != L'.' &&
(pat_wc[0] != L'\\' || pat_wc[1] != L'.'))
ret = 1;
}
FREE (pat_wc);
FREE (dn_wc);
return ret;
}
#endif /* HANDLE_MULTIBYTE */
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
udequote_pathname (pathname)
char *pathname;
{
register int i, j;
for (i = j = 0; pathname && pathname[i]; )
{
if (pathname[i] == '\\')
i++;
pathname[j++] = pathname[i++];
if (pathname[i - 1] == 0)
break;
}
pathname[j] = '\0';
}
#if HANDLE_MULTIBYTE
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
wdequote_pathname (pathname)
char *pathname;
{
mbstate_t ps;
size_t len, n;
wchar_t *wpathname;
int i, j;
wchar_t *orig_wpathname;
len = strlen (pathname);
/* Convert the strings into wide characters. */
n = xdupmbstowcs (&wpathname, NULL, pathname);
if (n == (size_t) -1)
/* Something wrong. */
return;
orig_wpathname = wpathname;
for (i = j = 0; wpathname && wpathname[i]; )
{
if (wpathname[i] == L'\\')
i++;
wpathname[j++] = wpathname[i++];
if (wpathname[i - 1] == L'\0')
break;
}
wpathname[j] = L'\0';
/* Convert the wide character string into unibyte character set. */
memset (&ps, '\0', sizeof(mbstate_t));
n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
pathname[len] = '\0';
/* Can't just free wpathname here; wcsrtombs changes it in many cases. */
free (orig_wpathname);
}
static void
dequote_pathname (pathname)
char *pathname;
{
if (MB_CUR_MAX > 1)
wdequote_pathname (pathname);
else
udequote_pathname (pathname);
}
#endif /* HANDLE_MULTIBYTE */
/* Test whether NAME exists. */
#if defined (HAVE_LSTAT)
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
# if !defined (AFS)
# define GLOB_TESTNAME(name) (test_eaccess (nextname, F_OK))
# else /* AFS */
# define GLOB_TESTNAME(name) (access (nextname, F_OK))
# endif /* AFS */
#endif /* !HAVE_LSTAT */
/* Return 0 if DIR is a directory, -1 otherwise. */
static int
glob_testdir (dir)
char *dir;
{
struct stat finfo;
if (stat (dir, &finfo) < 0)
return (-1);
if (S_ISDIR (finfo.st_mode) == 0)
return (-1);
return (0);
}
/* Return a vector of names of files in directory DIR
whose names match glob pattern PAT.
The names are not in any particular order.
Wildcards at the beginning of PAT do not match an initial period.
The vector is terminated by an element that is a null pointer.
To free the space allocated, first free the vector's elements,
then free the vector.
Return 0 if cannot get enough memory to hold the pointer
and the names.
Return -1 if cannot access directory DIR.
Look in errno for more information. */
char **
glob_vector (pat, dir, flags)
char *pat;
char *dir;
int flags;
{
struct globval
{
struct globval *next;
char *name;
};
DIR *d;
register struct dirent *dp;
struct globval *lastlink;
register struct globval *nextlink;
register char *nextname, *npat;
unsigned int count;
int lose, skip;
register char **name_vector;
register unsigned int i;
int mflags; /* Flags passed to strmatch (). */
int nalloca;
struct globval *firstmalloc, *tmplink;
lastlink = 0;
count = lose = skip = 0;
firstmalloc = 0;
/* If PAT is empty, skip the loop, but return one (empty) filename. */
if (pat == 0 || *pat == '\0')
{
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
nextlink = (struct globval *)alloca (sizeof (struct globval));
if (nextlink == NULL)
return ((char **) NULL);
nextlink->next = (struct globval *)0;
nextname = (char *) malloc (1);
if (nextname == 0)
lose = 1;
else
{
lastlink = nextlink;
nextlink->name = nextname;
nextname[0] = '\0';
count = 1;
}
skip = 1;
}
/* If the filename pattern (PAT) does not contain any globbing characters,
we can dispense with reading the directory, and just see if there is
a filename `DIR/PAT'. If there is, and we can access it, just make the
vector to return and bail immediately. */
if (skip == 0 && glob_pattern_p (pat) == 0)
{
int dirlen;
struct stat finfo;
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
dirlen = strlen (dir);
nextname = (char *)malloc (dirlen + strlen (pat) + 2);
npat = (char *)malloc (strlen (pat) + 1);
if (nextname == 0 || npat == 0)
lose = 1;
else
{
strcpy (npat, pat);
dequote_pathname (npat);
strcpy (nextname, dir);
nextname[dirlen++] = '/';
strcpy (nextname + dirlen, npat);
if (GLOB_TESTNAME (nextname) >= 0)
{
free (nextname);
nextlink = (struct globval *)alloca (sizeof (struct globval));
if (nextlink)
{
nextlink->next = (struct globval *)0;
lastlink = nextlink;
nextlink->name = npat;
count = 1;
}
else
lose = 1;
}
else
{
free (nextname);
free (npat);
}
}
skip = 1;
}
if (skip == 0)
{
/* Open the directory, punting immediately if we cannot. If opendir
is not robust (i.e., it opens non-directories successfully), test
that DIR is a directory and punt if it's not. */
#if defined (OPENDIR_NOT_ROBUST)
if (glob_testdir (dir) < 0)
return ((char **) &glob_error_return);
#endif
d = opendir (dir);
if (d == NULL)
return ((char **) &glob_error_return);
/* Compute the flags that will be passed to strmatch(). We don't
need to do this every time through the loop. */
mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
#ifdef FNM_CASEFOLD
if (glob_ignore_case)
mflags |= FNM_CASEFOLD;
#endif
if (extended_glob)
mflags |= FNM_EXTMATCH;
/* Scan the directory, finding all names that match.
For each name that matches, allocate a struct globval
on the stack and store the name in it.
Chain those structs together; lastlink is the front of the chain. */
while (1)
{
/* Make globbing interruptible in the shell. */
if (interrupt_state)
{
lose = 1;
break;
}
dp = readdir (d);
if (dp == NULL)
break;
/* If this directory entry is not to be used, try again. */
if (REAL_DIR_ENTRY (dp) == 0)
continue;
#if 0
if (dp->d_name == 0 || *dp->d_name == 0)
continue;
#endif
#if HANDLE_MULTIBYTE
if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name))
continue;
else
#endif
if (skipname (pat, dp->d_name))
continue;
if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
{
if (nalloca < ALLOCA_MAX)
{
nextlink = (struct globval *) alloca (sizeof (struct globval));
nalloca += sizeof (struct globval);
}
else
{
nextlink = (struct globval *) malloc (sizeof (struct globval));
if (firstmalloc == 0)
firstmalloc = nextlink;
}
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
if (nextlink == 0 || nextname == 0)
{
lose = 1;
break;
}
nextlink->next = lastlink;
lastlink = nextlink;
nextlink->name = nextname;
bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
++count;
}
}
(void) closedir (d);
}
if (lose == 0)
{
name_vector = (char **) malloc ((count + 1) * sizeof (char *));
lose |= name_vector == NULL;
}
/* Have we run out of memory? */
if (lose)
{
tmplink = 0;
/* Here free the strings we have got. */
while (lastlink)
{
if (firstmalloc)
{
if (lastlink == firstmalloc)
firstmalloc = 0;
tmplink = lastlink;
}
free (lastlink->name);
lastlink = lastlink->next;
FREE (tmplink);
}
QUIT;
return ((char **)NULL);
}
/* Copy the name pointers from the linked list into the vector. */
for (tmplink = lastlink, i = 0; i < count; ++i)
{
name_vector[i] = tmplink->name;
tmplink = tmplink->next;
}
name_vector[count] = NULL;
/* If we allocated some of the struct globvals, free them now. */
if (firstmalloc)
{
tmplink = 0;
while (lastlink)
{
tmplink = lastlink;
if (lastlink == firstmalloc)
lastlink = firstmalloc = 0;
else
lastlink = lastlink->next;
free (tmplink);
}
}
return (name_vector);
}
/* Return a new array which is the concatenation of each string in ARRAY
to DIR. This function expects you to pass in an allocated ARRAY, and
it takes care of free()ing that array. Thus, you might think of this
function as side-effecting ARRAY. This should handle GX_MARKDIRS. */
static char **
glob_dir_to_array (dir, array, flags)
char *dir, **array;
int flags;
{
register unsigned int i, l;
int add_slash;
char **result, *new;
struct stat sb;
l = strlen (dir);
if (l == 0)
{
if (flags & GX_MARKDIRS)
for (i = 0; array[i]; i++)
{
if ((stat (array[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
l = strlen (array[i]);
new = (char *)realloc (array[i], l + 2);
if (new == 0)
return NULL;
new[l] = '/';
new[l+1] = '\0';
array[i] = new;
}
}
return (array);
}
add_slash = dir[l - 1] != '/';
i = 0;
while (array[i] != NULL)
++i;
result = (char **) malloc ((i + 1) * sizeof (char *));
if (result == NULL)
return (NULL);
for (i = 0; array[i] != NULL; i++)
{
/* 3 == 1 for NUL, 1 for slash at end of DIR, 1 for GX_MARKDIRS */
result[i] = (char *) malloc (l + strlen (array[i]) + 3);
if (result[i] == NULL)
return (NULL);
strcpy (result[i], dir);
if (add_slash)
result[i][l] = '/';
strcpy (result[i] + l + add_slash, array[i]);
if (flags & GX_MARKDIRS)
{
if ((stat (result[i], &sb) == 0) && S_ISDIR (sb.st_mode))
{
size_t rlen;
rlen = strlen (result[i]);
result[i][rlen] = '/';
result[i][rlen+1] = '\0';
}
}
}
result[i] = NULL;
/* Free the input array. */
for (i = 0; array[i] != NULL; i++)
free (array[i]);
free ((char *) array);
return (result);
}
/* Do globbing on PATHNAME. Return an array of pathnames that match,
marking the end of the array with a null-pointer as an element.
If no pathnames match, then the array is empty (first element is null).
If there isn't enough memory, then return NULL.
If a file system error occurs, return -1; `errno' has the error code. */
char **
glob_filename (pathname, flags)
char *pathname;
int flags;
{
char **result;
unsigned int result_size;
char *directory_name, *filename;
unsigned int directory_len;
int free_dirname; /* flag */
result = (char **) malloc (sizeof (char *));
result_size = 1;
if (result == NULL)
return (NULL);
result[0] = NULL;
directory_name = NULL;
/* Find the filename. */
filename = strrchr (pathname, '/');
if (filename == NULL)
{
filename = pathname;
directory_name = "";
directory_len = 0;
free_dirname = 0;
}
else
{
directory_len = (filename - pathname) + 1;
directory_name = (char *) malloc (directory_len + 1);
if (directory_name == 0) /* allocation failed? */
return (NULL);
bcopy (pathname, directory_name, directory_len);
directory_name[directory_len] = '\0';
++filename;
free_dirname = 1;
}
/* If directory_name contains globbing characters, then we
have to expand the previous levels. Just recurse. */
if (glob_pattern_p (directory_name))
{
char **directories;
register unsigned int i;
if (directory_name[directory_len - 1] == '/')
directory_name[directory_len - 1] = '\0';
directories = glob_filename (directory_name, flags & ~GX_MARKDIRS);
if (free_dirname)
{
free (directory_name);
directory_name = NULL;
}
if (directories == NULL)
goto memory_error;
else if (directories == (char **)&glob_error_return)
{
free ((char *) result);
return ((char **) &glob_error_return);
}
else if (*directories == NULL)
{
free ((char *) directories);
free ((char *) result);
return ((char **) &glob_error_return);
}
/* We have successfully globbed the preceding directory name.
For each name in DIRECTORIES, call glob_vector on it and
FILENAME. Concatenate the results together. */
for (i = 0; directories[i] != NULL; ++i)
{
char **temp_results;
/* Scan directory even on a NULL pathname. That way, `*h/'
returns only directories ending in `h', instead of all
files ending in `h' with a `/' appended. */
temp_results = glob_vector (filename, directories[i], flags & ~GX_MARKDIRS);
/* Handle error cases. */
if (temp_results == NULL)
goto memory_error;
else if (temp_results == (char **)&glob_error_return)
/* This filename is probably not a directory. Ignore it. */
;
else
{
char **array;
register unsigned int l;
array = glob_dir_to_array (directories[i], temp_results, flags);
l = 0;
while (array[l] != NULL)
++l;
result =
(char **)realloc (result, (result_size + l) * sizeof (char *));
if (result == NULL)
goto memory_error;
for (l = 0; array[l] != NULL; ++l)
result[result_size++ - 1] = array[l];
result[result_size - 1] = NULL;
/* Note that the elements of ARRAY are not freed. */
free ((char *) array);
}
}
/* Free the directories. */
for (i = 0; directories[i]; i++)
free (directories[i]);
free ((char *) directories);
return (result);
}
/* If there is only a directory name, return it. */
if (*filename == '\0')
{
result = (char **) realloc ((char *) result, 2 * sizeof (char *));
if (result == NULL)
return (NULL);
/* Handle GX_MARKDIRS here. */
result[0] = (char *) malloc (directory_len + 1);
if (result[0] == NULL)
goto memory_error;
bcopy (directory_name, result[0], directory_len + 1);
if (free_dirname)
free (directory_name);
result[1] = NULL;
return (result);
}
else
{
char **temp_results;
/* There are no unquoted globbing characters in DIRECTORY_NAME.
Dequote it before we try to open the directory since there may
be quoted globbing characters which should be treated verbatim. */
if (directory_len > 0)
dequote_pathname (directory_name);
/* We allocated a small array called RESULT, which we won't be using.
Free that memory now. */
free (result);
/* Just return what glob_vector () returns appended to the
directory name. */
temp_results = glob_vector (filename,
(directory_len == 0 ? "." : directory_name),
flags & ~GX_MARKDIRS);
if (temp_results == NULL || temp_results == (char **)&glob_error_return)
{
if (free_dirname)
free (directory_name);
return (temp_results);
}
result = glob_dir_to_array (directory_name, temp_results, flags);
if (free_dirname)
free (directory_name);
return (result);
}
/* We get to memory_error if the program has run out of memory, or
if this is the shell, and we have been interrupted. */
memory_error:
if (result != NULL)
{
register unsigned int i;
for (i = 0; result[i] != NULL; ++i)
free (result[i]);
free ((char *) result);
}
if (free_dirname && directory_name)
free (directory_name);
QUIT;
return (NULL);
}
#if defined (TEST)
main (argc, argv)
int argc;
char **argv;
{
unsigned int i;
for (i = 1; i < argc; ++i)
{
char **value = glob_filename (argv[i], 0);
if (value == NULL)
puts ("Out of memory.");
else if (value == &glob_error_return)
perror (argv[i]);
else
for (i = 0; value[i] != NULL; i++)
puts (value[i]);
}
exit (0);
}
#endif /* TEST. */
+1 -1
View File
@@ -1414,7 +1414,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin)
insert_some_chars (nfd, lendiff, col_lendiff);
_rl_last_c_pos += col_lendiff;
}
else if (*ols == 0 && lendiff > 0)
else if ((MB_CUR_MAX == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0)
{
/* At the end of a line the characters do not have to
be "inserted". They can just be placed on the screen. */
File diff suppressed because it is too large Load Diff
+17 -15
View File
@@ -206,23 +206,24 @@ get_history_event (string, caller_index, delimiting_quote)
/* Only a closing `?' or a newline delimit a substring search string. */
for (local_index = i; c = string[i]; i++)
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int v;
mbstate_t ps;
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
int v;
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t));
/* These produce warnings because we're passing a const string to a
function that takes a non-const string. */
_rl_adjust_point ((char *)string, i, &ps);
if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
{
i += v - 1;
continue;
}
}
memset (&ps, 0, sizeof (mbstate_t));
/* These produce warnings because we're passing a const string to a
function that takes a non-const string. */
_rl_adjust_point ((char *)string, i, &ps);
if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
{
i += v - 1;
continue;
}
}
else
#endif /* HANDLE_MULTIBYTE */
if ((!substring_okay && (whitespace (c) || c == ':' ||
(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
@@ -230,6 +231,7 @@ get_history_event (string, caller_index, delimiting_quote)
string[i] == '\n' ||
(substring_okay && string[i] == '?'))
break;
}
which = i - local_index;
temp = (char *)xmalloc (1 + which);
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -97,7 +97,7 @@ extern int _rl_read_mbstring PARAMS((int, char *, int));
extern int _rl_is_mbchar_matched PARAMS((char *, int, int, char *, int));
extern int _rl_char_value PARAMS((char *, int));
extern wchar_t _rl_char_value PARAMS((char *, int));
extern int _rl_walphabetic PARAMS((wchar_t));
#define MB_NEXTCHAR(b,s,c,f) \
+4
View File
@@ -1124,6 +1124,10 @@ rl_delete_horizontal_space (count, ignore)
rl_delete_text (start, rl_point);
rl_point = start;
}
if (rl_point < 0)
rl_point = 0;
return 0;
}
+1558
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -2730,7 +2730,11 @@ parse_matched_pair (qc, open, close, lenp, flags)
start_lineno = line_number;
while (count)
{
#if 0
ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0);
#else
ch = shell_getc (qc != '\'' && pass_next_character == 0);
#endif
if (ch == EOF)
{
free (ret);
+2
View File
@@ -1089,6 +1089,8 @@ shell_is_restricted (name)
if (restricted)
return 1;
temp = base_pathname (name);
if (*temp == '-')
temp++;
return (STREQ (temp, RESTRICTED_SHELL_NAME));
}
+1786
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -6503,10 +6503,12 @@ add_string:
assignoff = sindex;
if (sindex == assignoff && string[sindex+1] == '~') /* XXX */
word->flags |= W_ITILDE;
#if 0
else if ((word->flags & W_ASSIGNMENT) &&
(posixly_correct == 0 || (word->flags & W_TILDEEXP)) &&
string[sindex+1] == '~')
word->flags |= W_ITILDE;
#endif
goto add_character;
case ':':
+12 -1
View File
@@ -2201,7 +2201,11 @@ do_assignment_internal (word, expand)
tlen = STRLEN (temp);
#if defined (ARRAY_VARS)
# if 0
if (expand && temp[0] == LPAREN && temp[tlen-1] == RPAREN)
#else
if (expand && (word->flags & W_COMPASSIGN))
#endif
{
assign_list = ni = 1;
value = extract_array_assignment_list (temp, &ni);
@@ -4347,7 +4351,7 @@ command_substitute (string, quoted)
char *string;
int quoted;
{
pid_t pid, old_pid, old_pipeline_pgrp;
pid_t pid, old_pid, old_pipeline_pgrp, old_async_pid;
char *istring;
int result, fildes[2], function_value, pflags, rc;
@@ -4395,7 +4399,14 @@ command_substitute (string, quoted)
cleanup_the_pipeline ();
#endif
old_async_pid = last_asynchronous_pid;
#if 0
pid = make_child ((char *)NULL, 0);
#else
pid = make_child ((char *)NULL, subshell_environment&SUBSHELL_ASYNC);
#endif
last_asynchronous_pid = old_async_pid;
if (pid == 0)
/* Reset the signal handlers in the child, but don't free the
trap strings. */
+1 -1
View File
@@ -1,4 +1,4 @@
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
+18 -4
View File
@@ -128,6 +128,10 @@ grep [ 123 ] *
length = 3
value = new1 new2 new3
./array.tests: line 237: narray: unbound variable
./array1.sub: line 1: syntax error near unexpected token `('
./array1.sub: line 1: `printf "%s\n" -a a=(a 'b c')'
./array2.sub: line 1: syntax error near unexpected token `('
./array2.sub: line 1: `declare -a ''=(a 'b c')'
a b c d e f g
for case if then else
@@ -135,10 +139,10 @@ for case if then else
12 14 16 18 20
4414758999202
aaa bbb
./array.tests: line 277: syntax error near unexpected token `<>'
./array.tests: line 277: `metas=( <> < > ! )'
./array.tests: line 278: syntax error near unexpected token `<>'
./array.tests: line 278: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
./array.tests: line 280: syntax error near unexpected token `<>'
./array.tests: line 280: `metas=( <> < > ! )'
./array.tests: line 281: syntax error near unexpected token `<>'
./array.tests: line 281: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
abc 3
case 4
abc case if then else 5
@@ -178,3 +182,13 @@ negative offset 2 - expect seven
seven
out-of-range offset
e
4
1 4 7 10
'b
b c
$0
t
[3]=abcde r s t u v
e
9
+11
View File
@@ -177,3 +177,14 @@ seven
negative offset 2 - expect seven
seven
out-of-range offset
e
4
1 4 7 10
'b
b c
$0
t
[3]=abcde r s t u v
e
9
+62
View File
@@ -236,6 +236,9 @@ echo "value = ${barray[*]}"
set -u
( echo ${#narray[4]} )
${THIS_SH} ./array1.sub
${THIS_SH} ./array2.sub
# some old bugs and ksh93 compatibility tests
set +u
cd /tmp
@@ -332,3 +335,62 @@ echo ${av[@]: -1:2}
echo out-of-range offset
echo ${av[@]:12}
# parsing problems and other inconsistencies not fixed until post bash-3.0
unset x
declare -a x=(')' $$)
[ ${x[1]} -eq $$ ] || echo bad
unset x
declare -a x=(a b c d e)
echo ${x[4]}
z=([1]=one [4]=four [7]=seven [10]=ten)
echo ${#z[@]}
echo ${!z[@]}
unset x
declare -a x=(a \'b c\')
echo "${x[1]}"
unset x
declare -a x=(a 'b c')
echo "${x[1]}"
unset x
declare -a x=($0)
[ "${x[@]}" = $0 ] || echo double expansion of \$0
declare -a x=(\$0)
echo "${x[@]}"
: ${TMPDIR:=/tmp}
mkdir $TMPDIR/bash-test-$$
cd $TMPDIR/bash-test-$$
trap "cd / ; rm -rf $TMPDIR/bash-test/$$" 0 1 2 3 6 15
touch '[3]=abcde'
touch r s t u v
declare -a x=(*)
echo ${x[3]}
echo ${x[@]}
unset x
x=(a b c d e)
echo ${x[*]: -1}
unset x[4]
unset x[2]
x[9]='9'
echo ${x[*]: -1}
+62
View File
@@ -332,3 +332,65 @@ echo ${av[@]: -1:2}
echo out-of-range offset
echo ${av[@]:12}
# parsing problems and other inconsistencies not fixed until post bash-3.0
unset x
declare -a x=(')' $$)
[ ${x[1]} -eq $$ ] || echo bad
unset x
declare -a x=(a b c d e)
echo ${x[4]}
z=([1]=one [4]=four [7]=seven [10]=ten)
echo ${#z[@]}
echo ${!z[@]}
unset x
declare -a x=(a \'b c\')
echo "${x[1]}"
unset x
declare -a x=(a 'b c')
echo "${x[1]}"
unset x
declare -a x=($0)
[ "${x[@]}" = $0 ] || echo double expansion of \$0
declare -a x=(\$0)
echo "${x[@]}"
: ${TMPDIR:=/tmp}
mkdir $TMPDIR/bash-test-$$
cd $TMPDIR/bash-test-$$
trap "cd / ; rm -rf $TMPDIR/bash-test/$$" 0 1 2 3 6 15
touch '[3]=abcde'
touch r s t u v
declare -a x=(*)
echo ${x[3]}
echo ${x[@]}
unset x
x=(a b c d e)
echo ${x[*]: -1}
unset x[4]
unset x[2]
x[9]='9'
echo ${x[*]: -1}
${THIS_SH} ./array1.sub
${THIS_SH} ./array2.sub
+1
View File
@@ -0,0 +1 @@
printf "%s\n" -a a=(a 'b c')
+1
View File
@@ -0,0 +1 @@
declare -a ''=(a 'b c')
+1
View File
@@ -51,3 +51,4 @@ this is ohio-state
0
1
testb
after
+53
View File
@@ -0,0 +1,53 @@
before exec1.sub: one two three
calling exec1.sub
aa bb cc dd ee
after exec1.sub with args: 0
after exec1.sub without args: 0
./execscript: line 20: notthere: command not found
127
/tmp/bash: notthere: No such file or directory
127
/bin/sh: /bin/sh: cannot execute binary file
126
./execscript: line 39: /: is a directory
126
/: /: cannot execute binary file
126
./execscript: line 46: .: /: is a directory
1
127
0
this is bashenv
./exec3.sub: line 3: /tmp/bash-notthere: No such file or directory
./exec3.sub: line 3: exec: /tmp/bash-notthere: cannot execute: No such file or directory
126
./execscript: line 68: notthere: No such file or directory
127
./execscript: line 71: notthere: No such file or directory
127
./execscript: line 74: notthere: No such file or directory
127
this is sh
this is sh
unset
ok
5
./exec5.sub: line 4: exec: bash-notthere: not found
127
this is ohio-state
0
1
1
0
42
42
0
1
1
0
0
1
0
1
testb
+4
View File
@@ -104,3 +104,7 @@ ${THIS_SH} ./exec6.sub
# checks for properly deciding what constitutes an executable file
${THIS_SH} ./exec7.sub
true | `echo true` &
echo after
+106
View File
@@ -0,0 +1,106 @@
export LC_ALL=C
export LANG=C
if [ $UID -eq 0 ]; then
echo "execscript: the test suite should not be run as root" >&2
fi
set -- one two three
echo before exec1.sub: "$@"
echo calling exec1.sub
./exec1.sub aa bb cc dd ee
echo after exec1.sub with args: $?
./exec1.sub
echo after exec1.sub without args: $?
# set up a fixed path so we know notthere will not be found
PATH=/usr/bin:/bin:/usr/local/bin:
export PATH
notthere
echo $?
# this is iffy, since the error messages may vary from system to system
# and /tmp might not exist
ln -s ${THIS_SH} /tmp/bash 2>/dev/null
if [ -f /tmp/bash ]; then
/tmp/bash notthere
else
${THIS_SH} notthere
fi
echo $?
rm -f /tmp/bash
# /bin/sh should be there on all systems
${THIS_SH} /bin/sh
echo $?
# try executing a directory
/
echo $?
${THIS_SH} /
echo $?
# try sourcing a directory
. /
echo $?
# try sourcing a binary file -- post-2.04 versions don't do the binary file
# check, and will probably fail with `command not found', or status 127
. ${THIS_SH} 2>/dev/null
echo $?
# post-bash-2.05 versions allow sourcing non-regular files
. /dev/null
echo $?
# kill two birds with one test -- test out the BASH_ENV code
echo echo this is bashenv > /tmp/bashenv
export BASH_ENV=/tmp/bashenv
${THIS_SH} ./exec3.sub
rm -f /tmp/bashenv
unset BASH_ENV
# we're resetting the $PATH to empty, so this should be last
PATH=
notthere
echo $?
command notthere
echo $?
command -p notthere
echo $?
# but -p should guarantee that we find all the standard utilities, even
# with an empty or unset $PATH
command -p sh -c 'echo this is $0'
unset PATH
command -p sh -c 'echo this is $0'
# a bug in bash before bash-2.01 caused PATH to be set to the empty string
# when command -p was run with PATH unset
echo ${PATH-unset}
echo "echo ok" | ${THIS_SH} -t
${THIS_SH} ./exec2.sub
echo $?
${THIS_SH} ./exec4.sub
# try exec'ing a command that cannot be found in $PATH
${THIS_SH} ./exec5.sub
# this was a bug in bash versions before bash-2.04
${THIS_SH} -c 'cat </dev/null | cat >/dev/null' >&-
# checks for proper return values in subshell commands with inverted return
# values
${THIS_SH} ./exec6.sub
# checks for properly deciding what constitutes an executable file
${THIS_SH} ./exec7.sub
+6
View File
@@ -372,3 +372,9 @@ a="a b c d e"
declare b=$a
expect '<a> <b> <c> <d> <e>'
recho $b
a="a?b?c"
echo ${a//\\?/ }
echo ${a//\?/ }
+374
View File
@@ -0,0 +1,374 @@
#
# A suite of tests for bash word expansions
#
# This tests parameter and variable expansion, with an empahsis on
# proper quoting behavior.
#
# Chet Ramey
#
# If you comment out the body of this function, you can do a diff against
# `expansion-tests.right' to see if the shell is behaving correctly
#
expect()
{
echo expect "$@"
}
# Test the substitution quoting characters (CTLESC and CTLNUL) in different
# combinations
expect "<^A>"
recho `echo ''`
expect "<^A>"
recho `echo ""`
expect "<^B>"
recho `echo ''`
expect "<^B>"
recho `echo ""`
expect "<^A>"
recho `echo `
expect "<^B>"
recho `echo `
# Test null strings without variable expansion
expect "<abcdefgh>"
recho abcd""efgh
expect "<abcdefgh>"
recho abcd''efgh
expect "<abcdefgh>"
recho ""abcdefgh
expect "<abcdefgh>"
recho ''abcdefgh
expect "<abcd>"
recho abcd""
expect "<abcd>"
recho abcd''
# Test the quirky behavior of $@ in ""
expect nothing
recho "$@"
expect "< >"
recho " $@"
expect "<-->"
recho "-${@}-"
# Test null strings with variable expansion that fails
expect '<>'
recho $xxx""
expect '<>'
recho ""$xxx
expect '<>'
recho $xxx''
expect '<>'
recho ''$xxx
expect '<>'
recho $xxx""$yyy
expect '<>'
recho $xxx''$yyy
# Test null strings with variable expansion that succeeds
xxx=abc
yyy=def
expect '<abc>'
recho $xxx""
expect '<abc>'
recho ""$xxx
expect '<abc>'
recho $xxx''
expect '<abc>'
recho ''$xxx
expect '<abcdef>'
recho $xxx""$yyy
expect '<abcdef>'
recho $xxx''$yyy
unset xxx yyy
# Test the unquoted special quoting characters
expect "<^A>"
recho 
expect "<^B>"
recho 
expect "<^A>"
recho ""
expect "<^B>"
recho ""
expect "<^A>"
recho ''
expect "<^B>"
recho ''
# Test expansion of a variable that is unset
expect nothing
recho $xxx
expect '<>'
recho "$xxx"
expect nothing
recho "$xxx${@}"
# Test empty string expansion
expect '<>'
recho ""
expect '<>'
recho ''
# Test command substitution with (disabled) history substitution
expect '<Hello World!>'
# set +H
recho "`echo \"Hello world!\"`"
# Test some shell special characters
expect '<`>'
recho "\`"
expect '<">'
recho "\""
expect '<\^A>'
recho "\"
expect '<\$>'
recho "\\$"
expect '<\\>'
recho "\\\\"
# This should give argv[1] = a argv[2] = b
expect '<a> <b>'
FOO=`echo 'a b' | tr ' ' '\012'`
recho $FOO
# This should give argv[1] = ^A argv[2] = ^B
expect '<^A> <^B>'
FOO=`echo ' ' | tr ' ' '\012'`
recho $FOO
# Test quoted and unquoted globbing characters
expect '<**>'
recho "*"*
expect '<\.\./*/>'
recho "\.\./*/"
# Test patterns that come up when the shell quotes funny character
# combinations
expect '<^A^B^A^B>'
recho ''
expect '<^A^A>'
recho ''
expect '<^A^B>'
recho ''
expect '<^A^A^B>'
recho ''
# More tests of "$@"
set abc def ghi jkl
expect '< abc> <def> <ghi> <jkl >'
recho " $@ "
expect '< abc> <def> <ghi> <jkl >'
recho "${1+ $@ }"
set abc def ghi jkl
expect '<--abc> <def> <ghi> <jkl-->'
recho "--$@--"
set "a b" cd ef gh
expect '<a b> <cd> <ef> <gh>'
recho ${1+"$@"}
expect '<a b> <cd> <ef> <gh>'
recho ${foo:-"$@"}
expect '<a b> <cd> <ef> <gh>'
recho "${@}"
expect '< >'
recho " "
expect '< - >'
recho " - "
# Test combinations of different types of quoting in a fully-quoted string
# (so the WHOLLY_QUOTED tests fail and it doesn't get set)
expect '</^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/>'
recho "/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*"'$'"/\1/"
# Test the various Posix parameter expansions
expect '<foo bar>'
recho "${x:-$(echo "foo bar")}"
expect '<foo> <bar>'
recho ${x:-$(echo "foo bar")}
unset X
expect '<abc>'
recho ${X:=abc}
expect '<abc>'
recho $X
set a b c
expect '<posix>'
recho ${3:+posix}
POSIX=/usr/posix
expect '<10>'
recho ${#POSIX}
# remove shortest trailing match
x=file.c
expect '<file.o>'
recho ${x%.c}.o
# remove longest trailing match
x=posix/src/std
expect '<posix>'
recho ${x%%/*}
# remove shortest leading pattern
x=$HOME/src/cmd
expect '</src/cmd>'
recho ${x#$HOME}
# remove longest leading pattern
x=/one/two/three
expect '<three>'
recho ${x##*/}
# pattern removal of patterns that don't match
z=abcdef
expect '<abcdef>'
recho ${z#xyz}
expect '<abcdef>'
recho ${z##xyz}
expect '<abcdef>'
recho ${z%xyz}
expect '<abcdef>'
recho ${z%%xyz}
# Command substitution and the quirky differences between `` and $()
expect '<\$x>'
recho '\$x'
expect '<$x>'
recho `echo '\$x'`
expect '<\$x>'
recho $(echo '\$x')
# The difference between $* "$*" and "$@"
set "abc" "def ghi" "jkl"
expect '<abc> <def> <ghi> <jkl>'
recho $*
expect '<abc def ghi jkl>'
recho "$*"
OIFS="$IFS"
IFS=":$IFS"
# The special behavior of "$*", using the first character of $IFS as separator
expect '<abc:def ghi:jkl>'
recho "$*"
IFS="$OIFS"
expect '<abc> <def ghi> <jkl>'
recho "$@"
expect '<xxabc> <def ghi> <jklyy>'
recho "xx$@yy"
expect '<abc> <def ghi> <jklabc> <def ghi> <jkl>'
recho "$@$@"
foo=abc
bar=def
expect '<abcdef>'
recho "$foo""$bar"
unset foo
set $foo bar '' xyz "$foo" abc
expect '<bar> <> <xyz> <> <abc>'
recho "$@"
# More tests of quoting and deferred evaluation
foo=10 x=foo
y='$'$x
expect '<$foo>'
recho $y
eval y='$'$x
expect '<10>'
recho $y
# case statements
NL='
'
x='ab
cd'
expect '<newline expected>'
case "$x" in
*$NL*) recho "newline expected" ;;
esac
expect '<got it>'
case \? in
*"?"*) recho "got it" ;;
esac
expect '<got it>'
case \? in
*\?*) recho "got it" ;;
esac
set one two three four five
expect '<one> <three> <five>'
recho $1 $3 ${5} $8 ${9}
# length tests on positional parameters and some special parameters
expect '<5> <5>'
recho $# ${#}
expect '<3>'
recho ${#1}
expect '<1>'
recho ${##}
expect '<1>'
recho ${#?}
expect '<5>'
recho ${#@}
expect '<5>'
recho ${#*}
expect '<5>'
recho "${#@}"
expect '<5>'
recho "${#*}"
expect '<42>'
recho $((28 + 14))
expect '<26>'
recho $[ 13 * 2 ]
expect '<\>'
recho `echo \\\\`
expect '<~>'
recho '~'
expect nothing
recho $!
expect nothing
recho ${!}
# test word splitting of assignment statements not preceding a command
a="a b c d e"
declare b=$a
expect '<a> <b> <c> <d> <e>'
recho $b
+2
View File
@@ -143,3 +143,5 @@ argv[2] = <b>
argv[3] = <c>
argv[4] = <d>
argv[5] = <e>
a?b?c
a b c
+1
View File
@@ -24,3 +24,4 @@ match 15
match 16
match 17
match 18
ok 19
+26
View File
@@ -0,0 +1,26 @@
match 1
match 2
match 3
match 4
match 1a
match 1b
match 2a
match 2b
match 3a
match 3b
match 4a
match 4b
match 5
match 6
match 7
match 8
match 9
match 10
match 11
match 12
match 13
match 14
match 15
match 16
match 17
match 18
+3
View File
@@ -51,3 +51,6 @@ shopt -s extglob
[[ ab/../ == +(??|a*)/..?(/) ]] && echo match 17
[[ ab/../ == +(a*)/..?(/) ]] && echo match 18
#
j="@(x)" ; [[ x == $j ]] && echo ok 19
+53
View File
@@ -0,0 +1,53 @@
shopt -s extglob
[[ ab/../ == @(ab|+([^/]))/..?(/) ]] && echo match 1
[[ ab/../ == +([^/])/..?(/) ]] && echo match 2
[[ ab/../ == @(ab|?b)/..?(/) ]] && echo match 3
[[ ab/../ == +([^/])/../ ]] && echo match 4
[[ ab/../ == +([!/])/..?(/) ]] && echo match 1a
[[ ab/../ == @(ab|+([!/]))/..?(/) ]] && echo match 1b
[[ ab/../ == +([!/])/../ ]] && echo match 2a
[[ ab/../ == +([!/])/..?(/) ]] && echo match 2b
[[ ab/../ == +([!/])/..@(/) ]] && echo match 3a
[[ ab/../ == +(ab)/..?(/) ]] && echo match 3b
[[ ab/../ == [!/][!/]/../ ]] && echo match 4a
[[ ab/../ == @(ab|?b)/..?(/) ]] && echo match 4b
[[ ab/../ == [^/][^/]/../ ]] && echo match 5
[[ ab/../ == ?b/..?(/) ]] && echo match 6
[[ ab/../ == +(?b)/..?(/) ]] && echo match 7
[[ ab/../ == +(?b|?b)/..?(/) ]] && echo match 8
[[ ab/../ == @(?b|?b)/..?(/) ]] && echo match 9
[[ ab/../ == @(a?|?b)/..?(/) ]] && echo match 10
[[ ab/../ == ?(ab)/..?(/) ]] && echo match 11
[[ ab/../ == ?(ab|??)/..?(/) ]] && echo match 12
[[ ab/../ == @(??)/..?(/) ]] && echo match 13
[[ ab/../ == @(??|a*)/..?(/) ]] && echo match 14
[[ ab/../ == @(a*)/..?(/) ]] && echo match 15
[[ ab/../ == +(??)/..?(/) ]] && echo match 16
[[ ab/../ == +(??|a*)/..?(/) ]] && echo match 17
[[ ab/../ == +(a*)/..?(/) ]] && echo match 18
+10
View File
@@ -0,0 +1,10 @@
é
1
AéB
B
B
ok 1
ok 2
aéb
0000000 141 303 251 142
0000004
+38
View File
@@ -0,0 +1,38 @@
LANG=en_US.UTF-8
a=$'\303\251'
echo "$a"
echo ${#a}
b=$'A\303\251B'
echo "$b"
echo ${b: -1}
c=AeB
echo ${c: -1}
unset a
a=$(printf '%b' 'A\303\251B')
IFS=$(printf '%b' '\303\251')
case "$a" in
"A${IFS}B") echo ok 1 ;;
*) echo bad 1 ;;
esac
set $a
case $1 in
A) echo ok 2 ;;
*) echo bad 2 ;;
esac
set a b
printf '%s\n' "$*"
printf '%s' "$*" | od -b
+38
View File
@@ -0,0 +1,38 @@
LANG=en_US.UTF-8
a=$'\303\251'
echo "$a"
echo ${#a}
b=$'A\303\251B'
echo "$b"
echo ${b: -1}
c=AeB
echo ${c: -1}
unset a
a=$(printf '%b' 'A\303\251B')
IFS=$(printf '%b' '\303\251')
case "$a" in
"A${IFS}B") echo yes ;;
*) echo no ;;
esac
set $a
case $1 in
A) echo ok 2 ;;
*) echo bad 2 ;;
esac
set a b
printf '%s\n' "$*"
printf '%s' "$*" | od -b
+2
View File
@@ -94,3 +94,5 @@ after read
0
0
0
before block
after block
+15
View File
@@ -156,3 +156,18 @@ ${THIS_SH} ./redir5.sub
# test behavior after a write error with a builtin command
${THIS_SH} ./redir6.sub
# problem with redirections using fds bash uses internally
: ${TMPDIR:=/tmp}
trap 'rm -f $TMPDIR/bash-redir-$$' 0 1 2 3 6 15
echo before block
{
echo before redir
exec 10>&1
echo after redir
} > $TMPDIR/bash-redir-$$
echo after block
+158
View File
@@ -0,0 +1,158 @@
export LC_ALL=C
export LANG=C
# catch-all for remaining untested redirection stuff
set +o posix
echo abc > /tmp/redir-test
cat /tmp/redir-test
set -o noclobber
#this should be an error
echo def > /tmp/redir-test
cat /tmp/redir-test
# but this should succeed
echo def > /tmp/redir-test-2
cat /tmp/redir-test-2
# and so should this
echo def >| /tmp/redir-test
cat /tmp/redir-test
set +o noclobber
rm /tmp/redir-test /tmp/redir-test-2
# this should be an error
z="a b"
cat < $z
echo "Point 1"
exec 3</etc/passwd
exec 4>/tmp/bash-a
exec 5>/tmp/bash-b
echo "Point 2"
echo to a 1>&4
echo to b 1>&5
cat /tmp/bash-a
cat /tmp/bash-b
exec 11</dev/null
echo "Point 3"
echo to a 1>&4
echo to b 1>&5
cat /tmp/bash-a
cat /tmp/bash-b
exec 11<&-
echo "Point 4"
exec 6<>/tmp/bash-c
echo to c 1>&6
cat /tmp/bash-c
echo "Point 5"
rm -f /tmp/bash-a /tmp/bash-b /tmp/bash-c
#
# Test the effect of input buffering on the shell's input
#
${THIS_SH} < redir1.sub
# more open, close, duplicate file descriptors
${THIS_SH} ./redir3.sub < ./redir3.in1
# still more redirections
${THIS_SH} ./redir4.sub < redir4.in1
# various forms of null redirection
testf()
{
if [ -f "$1" ]; then
rm -f "$1"
else
echo oops -- $1 not found
fi
}
> /tmp/null-redir-a
testf /tmp/null-redir-a
$EXIT > /tmp/null-redir-b
testf /tmp/null-redir-b
( > /tmp/null-redir-c )
testf /tmp/null-redir-c
$EXIT > /tmp/null-redir-d &
wait
testf /tmp/null-redir-d
exit 3 | $EXIT > /tmp/null-redir-e
echo $? -- ${PIPESTATUS[@]}
testf /tmp/null-redir-e
exit 4 | > /tmp/null-redir-f
echo $? -- ${PIPESTATUS[@]}
testf /tmp/null-redir-f
> /tmp/null-redir-g &
wait
testf /tmp/null-redir-g
exec >/tmp/null-redir-h &
wait
testf /tmp/null-redir-h
# make sure async commands don't get /dev/null as stdin when an explicit
# input redirection is supplied
for x in 1 2 3; do
{ read line ; echo $line ; } &
wait
{ read line ; echo $line ; } &
wait
done << EOF
ab
cd
ef
gh
ij
kl
EOF
# make sure async commands get /dev/null as stdin in the absence of any
# input redirection
/bin/cat &
wait
echo $?
# make sure that loops work OK with here documents and are not run in
# subshells
while read line; do
echo $line
l2=$line
done << EOF
ab
cd
EOF
echo $l2
# These should not echo anything -- bug in versions before 2.04
( ( echo hello 1>&3 ) 3>&1 ) >/dev/null 2>&1
( ( echo hello 1>&3 ) 3>&1 ) >/dev/null 2>&1 | cat
# in posix mode, non-interactive shells are not allowed to perform
# filename expansion on input redirections, even if they expand to
# a single filename
set -o posix
cat < redir1.*
# test ksh93 dup-and-close (move fd) redirections
${THIS_SH} ./redir5.sub
# test behavior after a write error with a builtin command
${THIS_SH} ./redir6.sub
+7
View File
@@ -0,0 +1,7 @@
# See whether or not we can use `diff -a'
( diff -a ./intl.right ./intl.right >/dev/null 2>&1 ) && AFLAG=-a
echo "warning: some of these tests will fail if you do not have UTF-8" >&2
echo "warning: locales installed on your system." >&2
${THIS_SH} ./intl.tests > /tmp/xx
diff $AFLAG /tmp/xx intl.right && rm -f /tmp/xx
+4
View File
@@ -0,0 +1,4 @@
echo "warning: some of these tests will fail if you do not have UTF-8" >&2
echo "warning: locales installed on your system." >&2
${THIS_SH} ./intl.tests > /tmp/xx
diff /tmp/xx intl.right && rm -f /tmp/xx
+7
View File
@@ -15,3 +15,10 @@ ok
~
make -k FOO=/usr/xyz/mumble
/usr/xyz/mumble
HOME=~
HOME=~
/usr/$x/abc
HOME=~
/usr/$x/abc
HOME=/usr/$x/abc
/usr/$x/abc
+24
View File
@@ -44,3 +44,27 @@ echo make -k FOO=~/mumble
typeset FOO=~/mumble
echo "$FOO"
h=HOME=~
echo $h
export h=HOME=~
echo $h
x=1234
HOME='/usr/$x/abc'
echo ~
# behavior differs here in posix mode
set -o posix
eval echo $h
eval $h
echo $HOME
set +o posix
eval echo $h
eval $h
echo $HOME
+62
View File
@@ -0,0 +1,62 @@
HOME=/usr/xyz
XPATH=/bin:/usr/bin:.
ADDPATH=PATH=~/bin:$XPATH
echo $ADDPATH
unset ADDPATH
: ${ADDPATH:=~/bin:~/bin2:$XPATH}
echo $ADDPATH
unset ADDPATH
: ${ADDPATH:=PATH=~/bin:~/bin2:$XPATH}
echo $ADDPATH
cat << !
~/bin
!
echo "~"
echo ${TPATH:-~}
echo "${TPATH:-~}"
echo "${TPATH:-"~"}"
echo "${XPATH+~}"
recho "\a"
recho "${TPATH:-\a}"
SHELL=~/bash
echo $SHELL
case $SHELL in
~/bash) echo ok;;
*) echo bad;;
esac
somevar=
echo "${somevar:-~}"
echo "${somevar:-"~"}"
echo make -k FOO=~/mumble
typeset FOO=~/mumble
echo "$FOO"
h=HOME=~
echo $h
export h=HOME=~
echo $h
x=1234
HOME='/usr/$x/abc'
echo ~
eval echo $h
eval $h
echo $HOME