diff options
author | Chet Ramey <chet.ramey@case.edu> | 2012-07-07 12:25:38 -0400 |
---|---|---|
committer | Chet Ramey <chet.ramey@case.edu> | 2012-07-07 12:25:38 -0400 |
commit | c74f63f39ff38c335bffa1da56603bd58bc49b7b (patch) | |
tree | 0f5167ac4ae8461c551cabb3a453dd4b32504c86 | |
parent | 66773245d792b68fcc1eec16badb1657bd365715 (diff) | |
download | bash-c74f63f39ff38c335bffa1da56603bd58bc49b7b.tar.gz |
commit bash-20120622 snapshot
-rw-r--r-- | CWRU/CWRU.chlog | 48 | ||||
-rw-r--r-- | CWRU/CWRU.chlog~ | 61 | ||||
-rw-r--r-- | builtins/mapfile.def | 15 | ||||
-rw-r--r-- | builtins/set.def | 6 | ||||
-rw-r--r-- | execute_cmd.c | 9 | ||||
-rw-r--r-- | jobs.h | 4 | ||||
-rw-r--r-- | lib/glob/glob.c | 7 | ||||
-rw-r--r-- | lib/glob/glob.c~ | 1244 | ||||
-rw-r--r-- | mailcheck.c | 10 | ||||
-rw-r--r-- | nojobs.c | 3 |
10 files changed, 1387 insertions, 20 deletions
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index b91edcc1..dd2098d4 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -14007,3 +14007,51 @@ lib/readline/complete.c multibyte characters, even when doing case-sensitive or case-mapping comparisons. Fixes problem reported by Nikolay Shirokovskiy <nshyrokovskiy@gmail.com> + + 6/20 + ---- +builtins/mapfile.def + - mapfile: move the line count increment and check for having read + the specified number of lines to the end of the loop to avoid + reading an additional line with zgetline. Fixes bug reported by + Dan Douglas <ormaaj@gmail.com> + + 6/21 + ---- + +execute_cmd.c + - execute_pipeline: make sure `lastpipe_flag' is initialized to 0 on + all systems, since it's tested later in the function. Fixes bug + reported by John E. Malmberg <wb8tyw@qsl.net> + + 6/22 + ---- +mailcheck.c + - file_mod_date_changed: return 0 right away if mailstat() does not + return success. Fixes bug with using uninitialized values reported + by szymon.kalasz@uj.edu.pl + +builtins/set.def + - the `monitor' option is not available when the shell is compiled + without job control, since the underlying `m' flag is not available + +nojobs.c + - job_control: now declared as int variable, initialized to 0, never + modified + +jobs.h + - job_control: extern declaration no longer dependent on JOB_CONTROL + +execute_cmd.c + - execute_pipeline: made necessary changes so `lastpipe' shell option + is now available in all shells, even those compiled without + JOB_CONTROL defined + + 6/23 + ---- +lib/glob/glob.c + - glob_filename: check for interrupts before returning if glob_vector + returns NULL or an error. Bug reported by Serge van den Boom + <svdb@stack.nl>, fix from Andreas Schwab <schwab@linux-m68k.org> + - call run_pending_traps after each call to QUIT or test of + interrupt_state, like we do in mainline shell code diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index 2ab08182..482b2d76 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -13992,5 +13992,64 @@ execute_cmd.c and restore it with unwind-protect builtins/evalstring.c - - parse_and_execute: save and restore line_number_for_err_trap along + - parse_prologue: save and restore line_number_for_err_trap along with line_number + - restore_lastcom: new function, unwind-protect to restore + the_printed_command_except_trap + - parse_prologue: use restore_lastcom to save and restore the value + of the_printed_command_except_trap around calls to parse_and_execute + (eval/source/.) + + 6/15 + ---- +lib/readline/complete.c + - complete_fncmp: change filename comparison code to understand + multibyte characters, even when doing case-sensitive or case-mapping + comparisons. Fixes problem reported by Nikolay Shirokovskiy + <nshyrokovskiy@gmail.com> + + 6/20 + ---- +builtins/mapfile.def + - mapfile: move the line count increment and check for having read + the specified number of lines to the end of the loop to avoid + reading an additional line with zgetline. Fixes bug reported by + Dan Douglas <ormaaj@gmail.com> + + 6/21 + ---- + +execute_cmd.c + - execute_pipeline: make sure `lastpipe_flag' is initialized to 0 on + all systems, since it's tested later in the function. Fixes bug + reported by John E. Malmberg <wb8tyw@qsl.net> + + 6/22 + ---- +mailcheck.c + - file_mod_date_changed: return 0 right away if mailstat() does not + return success. Fixes bug with using uninitialized values reported + by szymon.kalasz@uj.edu.pl + +builtins/set.def + - the `monitor' option is not available when the shell is compiled + without job control, since the underlying `m' flag is not available + +nojobs.c + - job_control: now declared as int variable, initialized to 0, never + modified + +jobs.h + - job_control: extern declaration no longer dependent on JOB_CONTROL + +execute_cmd.c + - execute_pipeline: made necessary changes so `lastpipe' shell option + is now available in all shells, even those compiled without + JOB_CONTROL defined + + 6/23 + ---- +lib/glob/glob.c + - glob_filename: check for interrupts before returning if glob_vector + returns NULL or an error. Bug reported by Serge van den Boom + <svdb@stack.nl>, fix from Andreas Schwab <schwab@linux-m68k.org> diff --git a/builtins/mapfile.def b/builtins/mapfile.def index ec1e32e5..3c578e00 100644 --- a/builtins/mapfile.def +++ b/builtins/mapfile.def @@ -2,7 +2,7 @@ This file is mapfile.def, from which is created mapfile.c. It implements the builtin "mapfile" in Bash. Copyright (C) 2005-2006 Rocky Bernstein for Free Software Foundation, Inc. -Copyright (C) 2008-2010 Free Software Foundation, Inc. +Copyright (C) 2008-2012 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -195,13 +195,9 @@ mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_n /* Reset the buffer for bash own stream */ interrupt_immediately++; for (array_index = origin, line_count = 1; - zgetline (fd, &line, &line_length, unbuffered_read) != -1; - array_index++, line_count++) + zgetline (fd, &line, &line_length, unbuffered_read) != -1; + array_index++) { - /* Have we exceeded # of lines to store? */ - if (line_count_goal != 0 && line_count > line_count_goal) - break; - /* Remove trailing newlines? */ if (flags & MAPF_CHOP) do_chop (line); @@ -217,6 +213,11 @@ mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_n } bind_array_element (entry, array_index, line, 0); + + /* Have we exceeded # of lines to store? */ + line_count++; + if (line_count_goal != 0 && line_count > line_count_goal) + break; } xfree (line); diff --git a/builtins/set.def b/builtins/set.def index 466fb017..c57efcda 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -96,12 +96,16 @@ Options: interactive-comments allow comments to appear in interactive commands keyword same as -k +#if defined (JOB_CONTROL) monitor same as -m +#endif noclobber same as -C noexec same as -n noglob same as -f nolog currently accepted but ignored +#if defined (JOB_CONTROL) notify same as -b +#endif nounset same as -u onecmd same as -t physical same as -P @@ -205,7 +209,9 @@ const struct { { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL }, { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "keyword", 'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#if defined (JOB_CONTROL) { "monitor", 'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, +#endif { "noclobber", 'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "noexec", 'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "noglob", 'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, diff --git a/execute_cmd.c b/execute_cmd.c index 7dfa77f2..710da986 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -118,6 +118,8 @@ extern time_t shell_start_time; extern char *glob_argv_flags; #endif +extern int job_control; /* XXX */ + extern int close __P((int)); /* Static functions defined and used in this file. */ @@ -2317,8 +2319,8 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) if (ignore_return && cmd) cmd->flags |= CMD_IGNORE_RETURN; -#if defined (JOB_CONTROL) lastpipe_flag = 0; + begin_unwind_frame ("lastpipe-exec"); lstdin = -1; /* If the `lastpipe' option is set with shopt, and job control is not @@ -2342,14 +2344,11 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) } if (prev >= 0) add_unwind_protect (close, prev); -#endif exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close); -#if defined (JOB_CONTROL) if (lstdin > 0) restore_stdin (lstdin); -#endif if (prev >= 0) close (prev); @@ -2372,9 +2371,7 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) unfreeze_jobs_list (); } -#if defined (JOB_CONTROL) discard_unwind_frame ("lastpipe-exec"); -#endif return (exec_result); } @@ -243,8 +243,6 @@ extern void close_pgrp_pipe __P((void)); extern void save_pgrp_pipe __P((int *, int)); extern void restore_pgrp_pipe __P((int *)); -#if defined (JOB_CONTROL) -extern int job_control; -#endif +extern int job_control; /* set to 0 in nojobs.c */ #endif /* _JOBS_H_ */ diff --git a/lib/glob/glob.c b/lib/glob/glob.c index 14a44a0a..e0bab02e 100644 --- a/lib/glob/glob.c +++ b/lib/glob/glob.c @@ -683,7 +683,8 @@ glob_vector (pat, dir, flags) lose = 1; break; } - + run_pending_traps (); + dp = readdir (d); if (dp == NULL) break; @@ -857,6 +858,7 @@ glob_vector (pat, dir, flags) } QUIT; + run_pending_traps (); return ((char **)NULL); } @@ -1186,6 +1188,8 @@ glob_filename (pathname, flags) { if (free_dirname) free (directory_name); + QUIT; /* XXX - shell */ + run_pending_traps (); return (temp_results); } @@ -1210,6 +1214,7 @@ glob_filename (pathname, flags) free (directory_name); QUIT; + run_pending_traps (); return (NULL); } diff --git a/lib/glob/glob.c~ b/lib/glob/glob.c~ new file mode 100644 index 00000000..d5869668 --- /dev/null +++ b/lib/glob/glob.c~ @@ -0,0 +1,1244 @@ +/* glob.c -- file-name wildcard pattern matching for Bash. + + Copyright (C) 1985-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne-Again SHell. + + Bash 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 3 of the License, or + (at your option) any later version. + + Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* 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 "shell.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 100000 +#endif + +struct globval + { + struct globval *next; + char *name; + }; + +extern void throw_to_top_level __P((void)); +extern int sh_eaccess __P((char *, int)); +extern char *sh_makepath __P((const char *, const 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; + +static struct globval finddirs_error_return; + +/* Some forward declarations. */ +static int skipname __P((char *, char *, int)); +#if HANDLE_MULTIBYTE +static int mbskipname __P((char *, char *, int)); +#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)); + +/* Make sure these names continue to agree with what's in smatch.c */ +extern char *glob_patscan __P((char *, char *, int)); +extern wchar_t *glob_patscan_wc __P((wchar_t *, wchar_t *, 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 ((unsigned char *)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 ((unsigned char *)pattern)); + + r = internal_glob_wpattern_p (wpattern); + free (wpattern); + + return r; +#else + return (internal_glob_pattern_p (pattern)); +#endif +} + +#if EXTENDED_GLOB +/* Return 1 if all subpatterns in the extended globbing pattern PAT indicate + that the name should be skipped. XXX - doesn't handle pattern negation, + not sure if it should */ +static int +extglob_skipname (pat, dname, flags) + char *pat, *dname; + int flags; +{ + char *pp, *pe, *t; + int n, r; + + pp = pat + 2; + pe = pp + strlen (pp) - 1; /*(*/ + if (*pe != ')') + return 0; + if ((t = strchr (pp, '|')) == 0) /* easy case first */ + { + *pe = '\0'; + r = skipname (pp, dname, flags); /*(*/ + *pe = ')'; + return r; + } + while (t = glob_patscan (pp, pe, '|')) + { + n = t[-1]; + t[-1] = '\0'; + r = skipname (pp, dname, flags); + t[-1] = n; + if (r == 0) /* if any pattern says not skip, we don't skip */ + return r; + pp = t; + } /*(*/ + + if (pp == pe) /* glob_patscan might find end of pattern */ + return r; + + *pe = '\0'; + r = mbskipname (pp, dname, flags); /*(*/ + *pe = ')'; + return r; +} +#endif + +/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned + with matching leading `.'. */ +static int +skipname (pat, dname, flags) + char *pat; + char *dname; + int flags; +{ +#if EXTENDED_GLOB + if (extglob_pattern (pat)) /* XXX */ + return (extglob_skipname (pat, dname, flags)); +#endif + + /* 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 + +static int +wchkname (pat_wc, dn_wc) + wchar_t *pat_wc, *dn_wc; +{ + /* 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')))) + return 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'.')) + return 1; + + return 0; +} + +static int +wextglob_skipname (pat, dname, flags) + wchar_t *pat, *dname; + int flags; +{ +#if EXTENDED_GLOB + wchar_t *pp, *pe, *t, n; + int r; + + pp = pat + 2; + pe = pp + wcslen (pp) - 1; /*(*/ + if (*pe != L')') + return 0; + if ((t = wcschr (pp, L'|')) == 0) + { + *pe = L'\0'; + r = wchkname (pp, dname); /*(*/ + *pe = L')'; + return r; + } + while (t = glob_patscan_wc (pp, pe, '|')) + { + n = t[-1]; + t[-1] = L'\0'; + r = wchkname (pp, dname); + t[-1] = n; + if (r == 0) + return 0; + pp = t; + } + + if (pp == pe) /* glob_patscan_wc might find end of pattern */ + return r; + + *pe = L'\0'; + r = wchkname (pp, dname); /*(*/ + *pe = L')'; + return r; +#else + return (wchkname (pat, dname)); +#endif +} + +/* 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, flags) + char *pat, *dname; + int flags; +{ + int ret, ext; + wchar_t *pat_wc, *dn_wc; + size_t pat_n, dn_n; + + if (mbsmbchar (dname) == 0 && mbsmbchar (pat) == 0) + return (skipname (pat, dname, flags)); + + ext = 0; +#if EXTENDED_GLOB + ext = extglob_pattern (pat); +#endif + + pat_wc = dn_wc = (wchar_t *)NULL; + + pat_n = xdupmbstowcs (&pat_wc, NULL, pat); + if (pat_n != (size_t)-1) + dn_n = xdupmbstowcs (&dn_wc, NULL, dname); + + ret = 0; + if (pat_n != (size_t)-1 && dn_n !=(size_t)-1) + ret = ext ? wextglob_skipname (pat_wc, dn_wc, flags) : wchkname (pat_wc, dn_wc); + else + ret = skipname (pat, dname, flags); + + 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; + } + if (pathname) + 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. Fall back to single-byte */ + return udequote_pathname (pathname); + 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; + } + if (wpathname) + 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) (sh_eaccess (name, F_OK)) +# else /* AFS */ +# define GLOB_TESTNAME(name) (access (name, 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; + +/*itrace("glob_testdir: testing %s", dir);*/ + if (stat (dir, &finfo) < 0) + return (-1); + + if (S_ISDIR (finfo.st_mode) == 0) + return (-1); + + return (0); +} + +/* Recursively scan SDIR for directories matching PAT (PAT is always `**'). + FLAGS is simply passed down to the recursive call to glob_vector. Returns + a list of matching directory names. EP, if non-null, is set to the last + element of the returned list. NP, if non-null, is set to the number of + directories in the returned list. These two variables exist for the + convenience of the caller (always glob_vector). */ +static struct globval * +finddirs (pat, sdir, flags, ep, np) + char *pat; + char *sdir; + int flags; + struct globval **ep; + int *np; +{ + char **r, *n; + int ndirs; + struct globval *ret, *e, *g; + +/*itrace("finddirs: pat = `%s' sdir = `%s' flags = 0x%x", pat, sdir, flags);*/ + e = ret = 0; + r = glob_vector (pat, sdir, flags); + if (r == 0 || r[0] == 0) + { + if (np) + *np = 0; + if (ep) + *ep = 0; + if (r && r != &glob_error_return) + free (r); + return (struct globval *)0; + } + for (ndirs = 0; r[ndirs] != 0; ndirs++) + { + g = (struct globval *) malloc (sizeof (struct globval)); + if (g == 0) + { + while (ret) /* free list built so far */ + { + g = ret->next; + free (ret); + ret = g; + } + + free (r); + if (np) + *np = 0; + if (ep) + *ep = 0; + return (&finddirs_error_return); + } + if (e == 0) + e = g; + + g->next = ret; + ret = g; + + g->name = r[ndirs]; + } + + free (r); + if (ep) + *ep = e; + if (np) + *np = ndirs; + + return ret; +} + +/* 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; +{ + DIR *d; + register struct dirent *dp; + struct globval *lastlink, *e, *dirlist; + register struct globval *nextlink; + register char *nextname, *npat, *subdir; + unsigned int count; + int lose, skip, ndirs, isdir, sdlen, add_current, patlen; + register char **name_vector; + register unsigned int i; + int mflags; /* Flags passed to strmatch (). */ + int pflags; /* flags passed to sh_makepath () */ + int nalloca; + struct globval *firstmalloc, *tmplink; + char *convfn; + + lastlink = 0; + count = lose = skip = add_current = 0; + + firstmalloc = 0; + nalloca = 0; + +/*itrace("glob_vector: pat = `%s' dir = `%s' flags = 0x%x", pat, dir, flags);*/ + /* 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; + } + + patlen = (pat && *pat) ? strlen (pat) : 0; + + /* 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 + patlen + 2); + npat = (char *)malloc (patlen + 1); + if (nextname == 0 || npat == 0) + { + FREE (nextname); + FREE (npat); + 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 + { + free (npat); + 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; + + add_current = ((flags & (GX_ALLDIRS|GX_ADDCURDIR)) == (GX_ALLDIRS|GX_ADDCURDIR)); + + /* 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 || terminating_signal) + { + lose = 1; + break; + } + run_pending_traps (); + + 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, flags)) + continue; + else +#endif + if (skipname (pat, dp->d_name, flags)) + continue; + + /* If we're only interested in directories, don't bother with files */ + if (flags & (GX_MATCHDIRS|GX_ALLDIRS)) + { + pflags = (flags & GX_ALLDIRS) ? MP_RMDOT : 0; + if (flags & GX_NULLDIR) + pflags |= MP_IGNDOT; + subdir = sh_makepath (dir, dp->d_name, pflags); + isdir = glob_testdir (subdir); + if (isdir < 0 && (flags & GX_MATCHDIRS)) + { + free (subdir); + continue; + } + } + + if (flags & GX_ALLDIRS) + { + if (isdir == 0) + { + dirlist = finddirs (pat, subdir, (flags & ~GX_ADDCURDIR), &e, &ndirs); + if (dirlist == &finddirs_error_return) + { + free (subdir); + lose = 1; + break; + } + if (ndirs) /* add recursive directories to list */ + { + if (firstmalloc == 0) + firstmalloc = e; + e->next = lastlink; + lastlink = dirlist; + count += ndirs; + } + } + + nextlink = (struct globval *) malloc (sizeof (struct globval)); + if (firstmalloc == 0) + firstmalloc = nextlink; + sdlen = strlen (subdir); + nextname = (char *) malloc (sdlen + 1); + if (nextlink == 0 || nextname == 0) + { + FREE (nextlink); + FREE (nextname); + free (subdir); + lose = 1; + break; + } + nextlink->next = lastlink; + lastlink = nextlink; + nextlink->name = nextname; + bcopy (subdir, nextname, sdlen + 1); + free (subdir); + ++count; + continue; + } + else if (flags & GX_MATCHDIRS) + free (subdir); + + convfn = fnx_fromfs (dp->d_name, D_NAMLEN (dp)); + if (strmatch (pat, convfn, 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) + { + FREE (nextlink); + FREE (nextname); + lose = 1; + break; + } + nextlink->next = lastlink; + lastlink = nextlink; + nextlink->name = nextname; + bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1); + ++count; + } + } + + (void) closedir (d); + } + + /* compat: if GX_ADDCURDIR, add the passed directory also. Add an empty + directory name as a placeholder if GX_NULLDIR (in which case the passed + directory name is "."). */ + if (add_current) + { + sdlen = strlen (dir); + nextname = (char *)malloc (sdlen + 1); + nextlink = (struct globval *) malloc (sizeof (struct globval)); + if (nextlink == 0 || nextname == 0) + { + FREE (nextlink); + FREE (nextname); + lose = 1; + } + else + { + nextlink->name = nextname; + nextlink->next = lastlink; + lastlink = nextlink; + if (flags & GX_NULLDIR) + nextname[0] = '\0'; + else + bcopy (dir, nextname, sdlen + 1); + ++count; + } + } + + 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) + { + /* Since we build the list in reverse order, the first N entries + will be allocated with malloc, if firstmalloc is set, from + lastlink to firstmalloc. */ + if (firstmalloc) + { + if (lastlink == firstmalloc) + firstmalloc = 0; + tmplink = lastlink; + } + else + tmplink = 0; + free (lastlink->name); + lastlink = lastlink->next; + FREE (tmplink); + } + + QUIT; + run_pending_traps (); + + 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) + { + int ind; + for (ind = 0; ind < i; ind++) + free (result[ind]); + free (result); + 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, *dname; + unsigned int directory_len; + int free_dirname; /* flag */ + int dflags; + + 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; + + dflags = flags & ~GX_MARKDIRS; + if ((flags & GX_GLOBSTAR) && directory_name[0] == '*' && directory_name[1] == '*' && (directory_name[2] == '/' || directory_name[2] == '\0')) + dflags |= GX_ALLDIRS|GX_ADDCURDIR; + + if (directory_name[directory_len - 1] == '/') + directory_name[directory_len - 1] = '\0'; + + directories = glob_filename (directory_name, dflags); + + 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; + + /* XXX -- we've recursively scanned any directories resulting from + a `**', so turn off the flag. We turn it on again below if + filename is `**' */ + /* Scan directory even on a NULL filename. That way, `*h/' + returns only directories ending in `h', instead of all + files ending in `h' with a `/' appended. */ + dname = directories[i]; + dflags = flags & ~(GX_MARKDIRS|GX_ALLDIRS|GX_ADDCURDIR); + if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + dflags |= GX_ALLDIRS|GX_ADDCURDIR; + if (dname[0] == '\0' && filename[0]) + { + dflags |= GX_NULLDIR; + dname = "."; /* treat null directory name and non-null filename as current directory */ + } + temp_results = glob_vector (filename, dname, dflags); + + /* 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; + + /* If we're expanding **, we don't need to glue the directory + name to the results; we've already done it in glob_vector */ + if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + array = temp_results; + else + 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. */ + if (array != temp_results) + free ((char *) array); + else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + free (temp_results); /* expanding ** case above */ + } + } + /* 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. */ + /* If flags & GX_ALLDIRS, we're called recursively */ + dflags = flags & ~GX_MARKDIRS; + if (directory_len == 0) + dflags |= GX_NULLDIR; + if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0') + { + dflags |= GX_ALLDIRS|GX_ADDCURDIR; +#if 0 + /* If we want all directories (dflags & GX_ALLDIRS) and we're not + being called recursively as something like `echo [star][star]/[star].o' + ((flags & GX_ALLDIRS) == 0), we want to prevent glob_vector from + adding a null directory name to the front of the temp_results + array. We turn off ADDCURDIR if not called recursively and + dlen == 0 */ +#endif + if (directory_len == 0 && (flags & GX_ALLDIRS) == 0) + dflags &= ~GX_ADDCURDIR; + } + temp_results = glob_vector (filename, + (directory_len == 0 ? "." : directory_name), + dflags); + + if (temp_results == NULL || temp_results == (char **)&glob_error_return) + { + if (free_dirname) + free (directory_name); +/* QUIT; /* XXX - shell */ + run_pending_traps (); + return (temp_results); + } + + result = glob_dir_to_array ((dflags & GX_ALLDIRS) ? "" : 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; + run_pending_traps (); + + 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. */ diff --git a/mailcheck.c b/mailcheck.c index bd95f0d6..05f58b9d 100644 --- a/mailcheck.c +++ b/mailcheck.c @@ -267,7 +267,10 @@ file_mod_date_changed (i) file = mailfiles[i]->name; mtime = mailfiles[i]->mod_time; - if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) + if (mailstat (file, &finfo) != 0) + return (0); + + if (finfo.st_size > 0) return (mtime < finfo.st_mtime); if (finfo.st_size == 0 && mailfiles[i]->file_size > 0) @@ -288,7 +291,10 @@ file_access_date_changed (i) file = mailfiles[i]->name; atime = mailfiles[i]->access_time; - if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) + if (mailstat (file, &finfo) != 0) + return (0); + + if (finfo.st_size > 0) return (atime < finfo.st_atime); return (0); @@ -96,6 +96,9 @@ int shell_tty = -1; exits from get_tty_state(). */ int check_window_size = CHECKWINSIZE_DEFAULT; +/* We don't have job control. */ +int job_control = 0; + /* STATUS and FLAGS are only valid if pid != NO_PID STATUS is only valid if (flags & PROC_RUNNING) == 0 */ struct proc_status { |