X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=general.c;h=b94b19efc76d5e21e2d6b5315cd6db7bb69de0e6;hb=refs%2Fheads%2Fbash-4.3-testing;hp=5b978dcf82fc565fc9fd03795c53d53045850541;hpb=b72432fdcc59300c6fe7c9d6c8a31ad3447933f5;p=thirdparty%2Fbash.git diff --git a/general.c b/general.c index 5b978dcf8..b94b19efc 100644 --- a/general.c +++ b/general.c @@ -1,28 +1,27 @@ /* general.c -- Stuff that is used by all files. */ -/* Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992 - Free Software Foundation, Inc. +/* Copyright (C) 1987-2011 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 2, or (at your option) any later - version. + 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. + 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; see the file COPYING. If not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ #include "config.h" #include "bashtypes.h" -#ifndef _MINIX +#if defined (HAVE_SYS_PARAM_H) # include #endif #include "posixstat.h" @@ -34,47 +33,51 @@ #include "filecntl.h" #include "bashansi.h" #include -#include +#include "chartypes.h" #include -#include "shell.h" -#include +#include "bashintl.h" -#if defined (TIME_WITH_SYS_TIME) -# include -# include -#else -# if defined (HAVE_SYS_TIME_H) -# include -# else -# include -# endif -#endif +#include "shell.h" +#include "test.h" -#include -#include "maxpath.h" +#include #if !defined (errno) extern int errno; #endif /* !errno */ -#ifndef to_upper -# define to_upper(c) (islower(c) ? toupper(c) : (c)) -# define to_lower(c) (isupper(c) ? tolower(c) : (c)) -#endif - -extern int interrupt_immediately; +extern int expand_aliases; extern int interactive_comments; +extern int check_hashed_filenames; +extern int source_uses_path; +extern int source_searches_cwd; + +static char *bash_special_tilde_expansions __P((char *)); +static int unquoted_tilde_word __P((const char *)); +static void initialize_group_array __P((void)); /* A standard error message to use when getcwd() returns NULL. */ -char *bash_getcwd_errstr = "getcwd: cannot access parent directories"; +const char * const bash_getcwd_errstr = N_("getcwd: cannot access parent directories"); /* Do whatever is necessary to initialize `Posix mode'. */ void posix_initialize (on) int on; { - interactive_comments = on != 0; + /* Things that should be turned on when posix mode is enabled. */ + if (on != 0) + { + interactive_comments = source_uses_path = expand_aliases = 1; + source_searches_cwd = 0; + } + + /* Things that should be turned on when posix mode is disabled. */ + if (on == 0) + { + source_searches_cwd = 1; + expand_aliases = interactive_shell; + } } /* **************************************************************** */ @@ -95,13 +98,13 @@ string_to_rlimtype (s) neg = 0; while (s && *s && whitespace (*s)) s++; - if (*s == '-' || *s == '+') + if (s && (*s == '-' || *s == '+')) { neg = *s == '-'; s++; } - for ( ; s && *s && digit (*s); s++) - ret = (ret * 10) + digit_value (*s); + for ( ; s && *s && DIGIT (*s); s++) + ret = (ret * 10) + TODIGIT (*s); return (neg ? -ret : ret); } @@ -110,120 +113,29 @@ print_rlimtype (n, addnl) RLIMTYPE n; int addnl; { - char s[sizeof (RLIMTYPE) * 3 + 1]; - int len; + char s[INT_STRLEN_BOUND (RLIMTYPE) + 1], *p; - if (n == 0) - { - printf ("0%s", addnl ? "\n" : ""); - return; - } + p = s + sizeof(s); + *--p = '\0'; if (n < 0) { - putchar ('-'); - n = -n; - } + do + *--p = '0' - n % 10; + while ((n /= 10) != 0); - len = sizeof (RLIMTYPE) * 3 + 1; - s[--len] = '\0'; - for ( ; n != 0; n /= 10) - s[--len] = n % 10 + '0'; - printf ("%s%s", s + len, addnl ? "\n" : ""); -} -#endif /* RLIMTYPE */ - -#if defined (HAVE_TIMEVAL) -/* Convert a pointer to a struct timeval to seconds and thousandths of a - second, returning the values in *SP and *SFP, respectively. This does - rounding on the fractional part, not just truncation to three places. */ -void -timeval_to_secs (tvp, sp, sfp) - struct timeval *tvp; - long *sp; - int *sfp; -{ - int rest; - - *sp = tvp->tv_sec; - - *sfp = tvp->tv_usec % 1000000; /* pretty much a no-op */ - rest = *sfp % 1000; - *sfp = (*sfp * 1000) / 1000000; - if (rest >= 500) - *sfp += 1; - - /* Sanity check */ - if (*sfp >= 1000) - { - *sp += 1; - *sfp -= 1000; + *--p = '-'; } -} - -/* Print the contents of a struct timeval * in a standard way to stdio - stream FP. */ -void -print_timeval (fp, tvp) - FILE *fp; - struct timeval *tvp; -{ - int minutes, seconds_fraction; - long seconds; - - timeval_to_secs (tvp, &seconds, &seconds_fraction); - - minutes = seconds / 60; - seconds %= 60; - - fprintf (fp, "%0dm%0ld.%03ds", minutes, seconds, seconds_fraction); -} -#endif /* HAVE_TIMEVAL */ - -#if defined (HAVE_TIMES) -void -clock_t_to_secs (t, sp, sfp) - clock_t t; - long *sp; - int *sfp; -{ - static long clk_tck = 0; - - if (clk_tck == 0) - clk_tck = get_clk_tck (); - - *sfp = t % clk_tck; - *sfp = (*sfp * 1000) / clk_tck; - - *sp = t / clk_tck; - - /* Sanity check */ - if (*sfp >= 1000) + else { - *sp += 1; - *sfp -= 1000; + do + *--p = '0' + n % 10; + while ((n /= 10) != 0); } -} - -/* Print the time defined by a time_t (returned by the `times' and `time' - system calls) in a standard way to stdion stream FP. This is scaled in - terms of HZ, which is what is returned by the `times' call. */ -void -print_time_in_hz (fp, t) - FILE *fp; - clock_t t; -{ - int minutes, seconds_fraction; - long seconds; - - clock_t_to_secs (t, &seconds, &seconds_fraction); - - minutes = seconds / 60; - seconds %= 60; - fprintf (fp, "%0dm%0ld.%03ds", minutes, seconds, seconds_fraction); + printf ("%s%s", p, addnl ? "\n" : ""); } -#endif /* HAVE_TIMES */ +#endif /* RLIMTYPE */ /* **************************************************************** */ /* */ @@ -236,35 +148,44 @@ int all_digits (string) char *string; { - while (*string) - { - if (!digit (*string)) - return (0); - else - string++; - } + register char *s; + + for (s = string; *s; s++) + if (DIGIT (*s) == 0) + return (0); + return (1); } /* Return non-zero if the characters pointed to by STRING constitute a valid number. Stuff the converted number into RESULT if RESULT is - a non-null pointer to a long. */ + not null. */ int legal_number (string, result) - char *string; - long *result; + const char *string; + intmax_t *result; { - long value; + intmax_t value; char *ep; if (result) *result = 0; - value = strtol (string, &ep, 10); + if (string == 0) + return 0; + + errno = 0; + value = strtoimax (string, &ep, 10); + if (errno || ep == string) + return 0; /* errno is set on overflow or underflow */ + + /* Skip any trailing whitespace, since strtoimax does not. */ + while (whitespace (*ep)) + ep++; /* If *string is not '\0' but *ep is '\0' on return, the entire string is valid. */ - if (string && *string && *ep == '\0') + if (*string && *ep == '\0') { if (result) *result = value; @@ -285,14 +206,15 @@ legal_identifier (name) char *name; { register char *s; + unsigned char c; - if (!name || !*name || (legal_variable_starter (*name) == 0)) + if (!name || !(c = *name) || (legal_variable_starter (c) == 0)) return (0); - for (s = name + 1; *s; s++) + for (s = name + 1; (c = *s) != 0; s++) { - if (legal_variable_char (*s) == 0) - return (0); + if (legal_variable_char (c) == 0) + return (0); } return (1); } @@ -309,18 +231,86 @@ check_identifier (word, check_word) { if ((word->flags & (W_HASDOLLAR|W_QUOTED)) || all_digits (word->word)) { - internal_error ("`%s': not a valid identifier", word->word); + internal_error (_("`%s': not a valid identifier"), word->word); return (0); } else if (check_word && legal_identifier (word->word) == 0) { - internal_error ("`%s': not a valid identifier", word->word); + internal_error (_("`%s': not a valid identifier"), word->word); return (0); } else return (1); } +/* Return 1 if STRING comprises a valid alias name. The shell accepts + essentially all characters except those which must be quoted to the + parser (which disqualifies them from alias expansion anyway) and `/'. */ +int +legal_alias_name (string, flags) + char *string; + int flags; +{ + register char *s; + + for (s = string; *s; s++) + if (shellbreak (*s) || shellxquote (*s) || shellexp (*s) || (*s == '/')) + return 0; + return 1; +} + +/* Returns non-zero if STRING is an assignment statement. The returned value + is the index of the `=' sign. */ +int +assignment (string, flags) + const char *string; + int flags; +{ + register unsigned char c; + register int newi, indx; + + c = string[indx = 0]; + +#if defined (ARRAY_VARS) + if ((legal_variable_starter (c) == 0) && (flags == 0 || c != '[')) /* ] */ +#else + if (legal_variable_starter (c) == 0) +#endif + return (0); + + while (c = string[indx]) + { + /* The following is safe. Note that '=' at the start of a word + is not an assignment statement. */ + if (c == '=') + return (indx); + +#if defined (ARRAY_VARS) + if (c == '[') + { + newi = skipsubscript (string, indx, 0); + if (string[newi++] != ']') + return (0); + if (string[newi] == '+' && string[newi+1] == '=') + return (newi + 1); + return ((string[newi] == '=') ? newi : 0); + } +#endif /* ARRAY_VARS */ + + /* Check for `+=' */ + if (c == '+' && string[indx+1] == '=') + return (indx + 1); + + /* Variable names in assignment statements may contain only letters, + digits, and `_'. */ + if (legal_variable_char (c) == 0) + return (0); + + indx++; + } + return (0); +} + /* **************************************************************** */ /* */ /* Functions to manage files and file descriptors */ @@ -338,27 +328,52 @@ check_identifier (word, check_word) #endif /* O_NDELAY */ /* Make sure no-delay mode is not set on file descriptor FD. */ -void -unset_nodelay_mode (fd) +int +sh_unset_nodelay_mode (fd) int fd; { - int flags, set; + int flags, bflags; if ((flags = fcntl (fd, F_GETFL, 0)) < 0) - return; + return -1; - set = 0; + bflags = 0; /* This is defined to O_NDELAY in filecntl.h if O_NONBLOCK is not present and O_NDELAY is defined. */ - if (flags & O_NONBLOCK) +#ifdef O_NONBLOCK + bflags |= O_NONBLOCK; +#endif + +#ifdef O_NDELAY + bflags |= O_NDELAY; +#endif + + if (flags & bflags) { - flags &= ~O_NONBLOCK; - set++; + flags &= ~bflags; + return (fcntl (fd, F_SETFL, flags)); } - if (set) - fcntl (fd, F_SETFL, flags); + return 0; +} + +/* Return 1 if file descriptor FD is valid; 0 otherwise. */ +int +sh_validfd (fd) + int fd; +{ + return (fcntl (fd, F_GETFD, 0) >= 0); +} + +int +fd_ispipe (fd) + int fd; +{ + errno = 0; + if (lseek ((fd), 0L, SEEK_CUR) < 0) + return (errno == ESPIPE); + return 0; } /* There is a bug in the NeXT 2.1 rlogind that causes opens @@ -386,7 +401,8 @@ check_dev_tty () return; tty_fd = open (tty, O_RDWR|O_NONBLOCK); } - close (tty_fd); + if (tty_fd >= 0) + close (tty_fd); } /* Return 1 if PATH1 and PATH2 are the same file. This is kind of @@ -434,8 +450,8 @@ move_to_high_fd (fd, check_new, maxfd) nfds = getdtablesize (); if (nfds <= 0) nfds = 20; - if (nfds > 256) - nfds = 256; + if (nfds > HIGH_FD_MAX) + nfds = HIGH_FD_MAX; /* reasonable maximum */ } else nfds = maxfd; @@ -444,13 +460,15 @@ move_to_high_fd (fd, check_new, maxfd) if (fcntl (nfds, F_GETFD, &ignore) == -1) break; - if (nfds && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1) + if (nfds > 3 && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1) { if (check_new == 0 || fd != fileno (stderr)) /* don't close stderr */ close (fd); return (script_fd); } + /* OK, we didn't find one less than our artificial maximum; return the + original file descriptor. */ return (fd); } @@ -459,27 +477,20 @@ move_to_high_fd (fd, check_new, maxfd) check up to the first newline, or SAMPLE_LEN, whichever comes first. All of the characters must be printable or whitespace. */ -#if !defined (isspace) -#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f') -#endif - -#if !defined (isprint) -#define isprint(c) (isletter(c) || digit(c) || ispunct(c)) -#endif - int check_binary_file (sample, sample_len) - unsigned char *sample; + char *sample; int sample_len; { register int i; + unsigned char c; for (i = 0; i < sample_len; i++) { - if (sample[i] == '\n') + c = sample[i]; + if (c == '\n') return (0); - - if (isspace (sample[i]) == 0 && isprint (sample[i]) == 0) + if (c == '\0') return (1); } @@ -488,235 +499,104 @@ check_binary_file (sample, sample_len) /* **************************************************************** */ /* */ -/* Functions to manipulate pathnames */ +/* Functions to manipulate pipes */ /* */ /* **************************************************************** */ -/* Return 1 if PATH corresponds to a directory. */ -static int -canon_stat (path) - char *path; -{ - int l; - char *s; - struct stat sb; - - l = strlen (path); - s = xmalloc (l + 3); - strcpy (s, path); - s[l] = '/'; - s[l+1] = '.'; - s[l+2] = '\0'; - l = stat (s, &sb) == 0 && S_ISDIR (sb.st_mode); - free (s); - return l; -} - -/* Canonicalize PATH, and return a new path. The new path differs from PATH - in that: - Multple `/'s are collapsed to a single `/'. - Leading `./'s and trailing `/.'s are removed. - Trailing `/'s are removed. - Non-leading `../'s and trailing `..'s are handled by removing - portions of the path. */ -char * -canonicalize_pathname (path) - char *path; +int +sh_openpipe (pv) + int *pv; { - register int i, start; - char stub_char; - char *result; + int r; - /* The result cannot be larger than the input PATH. */ - result = savestring (path); + if ((r = pipe (pv)) < 0) + return r; - stub_char = (*path == '/') ? '/' : '.'; + pv[0] = move_to_high_fd (pv[0], 1, 64); + pv[1] = move_to_high_fd (pv[1], 1, 64); - /* Walk along RESULT looking for things to compact. */ - i = 0; - while (1) - { - if (!result[i]) - break; - - while (result[i] && result[i] != '/') - i++; - - start = i++; + return 0; +} - /* If we didn't find any slashes, then there is nothing left to do. */ - if (!result[start]) - break; +int +sh_closepipe (pv) + int *pv; +{ + if (pv[0] >= 0) + close (pv[0]); - /* Handle multiple `/'s in a row. */ - while (result[i] == '/') - i++; + if (pv[1] >= 0) + close (pv[1]); -#if 0 - if ((start + 1) != i) -#else - /* Leave a leading `//' alone, as POSIX requires. */ - if ((start + 1) != i && (start != 0 || i != 2)) -#endif - { - strcpy (result + start + 1, result + i); - i = start + 1; - /* Make sure that what we have so far corresponds to a directory. - If it does not, just punt. */ - if (*result) - { - char c; - c = result[start]; - result[start] = '\0'; - if (canon_stat (result) == 0) - { - free (result); - return ((char *)NULL); - } - result[start] = c; - } - } -#if 0 - /* Handle backslash-quoted `/'. */ - if (start > 0 && result[start - 1] == '\\') - continue; -#endif + pv[0] = pv[1] = -1; + return 0; +} - /* Check for trailing `/'. */ - if (start && !result[i]) - { - zero_last: - result[--i] = '\0'; - break; - } +/* **************************************************************** */ +/* */ +/* Functions to inspect pathnames */ +/* */ +/* **************************************************************** */ - /* Check for `../', `./' or trailing `.' by itself. */ - if (result[i] == '.') - { - /* Handle trailing `.' by itself. */ - if (!result[i + 1]) - goto zero_last; - - /* Handle `./'. */ - if (result[i + 1] == '/') - { - strcpy (result + i, result + i + 1); - i = (start < 0) ? 0 : start; - continue; - } - - /* Handle `../' or trailing `..' by itself. */ - if (result[i + 1] == '.' && - (result[i + 2] == '/' || !result[i + 2])) - { - /* Make sure that the last component corresponds to a directory - before blindly chopping it off. */ - if (i) - { - result[i] = '\0'; - if (canon_stat (result) == 0) - { - free (result); - return ((char *)NULL); - } - result[i] = '.'; - } - while (--start > -1 && result[start] != '/'); - strcpy (result + start + 1, result + i + 2); -#if 0 /* Unnecessary */ - if (*result && canon_stat (result) == 0) - { - free (result); - return ((char *)NULL); - } -#endif - i = (start < 0) ? 0 : start; - continue; - } - } - } +int +file_exists (fn) + char *fn; +{ + struct stat sb; - if (!*result) - { - *result = stub_char; - result[1] = '\0'; - } + return (stat (fn, &sb) == 0); +} - /* If the result starts with `//', but the original path does not, we - can turn the // into /. */ - if ((result[0] == '/' && result[1] == '/' && result[2] != '/') && - (path[0] != '/' || path[1] != '/' || path[2] == '/')) - { - char *r2; - if (result[2] == '\0') /* short-circuit for bare `//' */ - result[1] = '\0'; - else - { - r2 = savestring (result + 1); - free (result); - result = r2; - } - } +int +file_isdir (fn) + char *fn; +{ + struct stat sb; - return (result); + return ((stat (fn, &sb) == 0) && S_ISDIR (sb.st_mode)); } -/* Turn STRING (a pathname) into an absolute pathname, assuming that - DOT_PATH contains the symbolic location of `.'. This always - returns a new string, even if STRING was an absolute pathname to - begin with. */ -char * -make_absolute (string, dot_path) - char *string, *dot_path; +int +file_iswdir (fn) + char *fn; { - char *result; - int result_len; + return (file_isdir (fn) && sh_eaccess (fn, W_OK) == 0); +} - if (dot_path == 0 || *string == '/') - result = savestring (string); - else - { - if (dot_path[0]) - { - result_len = strlen (dot_path); - result = xmalloc (2 + result_len + strlen (string)); - strcpy (result, dot_path); - if (result[result_len - 1] != '/') - { - result[result_len++] = '/'; - result[result_len] = '\0'; - } - } - else - { - result = xmalloc (3 + strlen (string)); - result[0] = '.'; result[1] = '/'; result[2] = '\0'; - result_len = 2; - } +/* Return 1 if STRING is "." or "..", optionally followed by a directory + separator */ +int +path_dot_or_dotdot (string) + const char *string; +{ + if (string == 0 || *string == '\0' || *string != '.') + return (0); - strcpy (result + result_len, string); - } + /* string[0] == '.' */ + if (PATHSEP(string[1]) || (string[1] == '.' && PATHSEP(string[2]))) + return (1); - return (result); + return (0); } -/* Return 1 if STRING contains an absolute pathname, else 0. */ +/* Return 1 if STRING contains an absolute pathname, else 0. Used by `cd' + to decide whether or not to look up a directory name in $CDPATH. */ int absolute_pathname (string) - char *string; + const char *string; { - if (!string || !*string) + if (string == 0 || *string == '\0') return (0); - if (*string == '/') + if (ABSPATH(string)) + return (1); + + if (string[0] == '.' && PATHSEP(string[1])) /* . and ./ */ + return (1); + + if (string[0] == '.' && string[1] == '.' && PATHSEP(string[2])) /* .. and ../ */ return (1); - if (*string++ == '.') - { - if (!*string || *string == '/' || - (*string == '.' && (string[1] == '\0' || string[1] == '/'))) - return (1); - } return (0); } @@ -725,20 +605,58 @@ absolute_pathname (string) up through $PATH. */ int absolute_program (string) - char *string; + const char *string; +{ + return ((char *)mbschr (string, '/') != (char *)NULL); +} + +/* **************************************************************** */ +/* */ +/* Functions to manipulate pathnames */ +/* */ +/* **************************************************************** */ + +/* Turn STRING (a pathname) into an absolute pathname, assuming that + DOT_PATH contains the symbolic location of `.'. This always + returns a new string, even if STRING was an absolute pathname to + begin with. */ +char * +make_absolute (string, dot_path) + char *string, *dot_path; { - return ((char *)strchr (string, '/') != (char *)NULL); + char *result; + + if (dot_path == 0 || ABSPATH(string)) +#ifdef __CYGWIN__ + { + char pathbuf[PATH_MAX + 1]; + + cygwin_conv_to_full_posix_path (string, pathbuf); + result = savestring (pathbuf); + } +#else + result = savestring (string); +#endif + else + result = sh_makepath (dot_path, string, 0); + + return (result); } /* Return the `basename' of the pathname in STRING (the stuff after the - last '/'). If STRING is not a full pathname, simply return it. */ + last '/'). If STRING is `/', just return it. */ char * base_pathname (string) char *string; { char *p; - if (!absolute_pathname (string)) +#if 0 + if (absolute_pathname (string) == 0) + return (string); +#endif + + if (string[0] == '/' && string[1] == 0) return (string); p = (char *)strrchr (string, '/'); @@ -753,37 +671,17 @@ char * full_pathname (file) char *file; { - char *disposer; - char *current_dir; - int dlen; + char *ret; - file = (*file == '~') ? bash_tilde_expand (file) : savestring (file); + file = (*file == '~') ? bash_tilde_expand (file, 0) : savestring (file); - if ((*file == '/') && absolute_pathname (file)) + if (ABSPATH(file)) return (file); - disposer = file; - - /* XXX - this should probably be just PATH_MAX or PATH_MAX + 1 */ - current_dir = xmalloc (2 + PATH_MAX + strlen (file)); - if (getcwd (current_dir, PATH_MAX) == 0) - { - sys_error (bash_getcwd_errstr); - free (disposer); - free (current_dir); - return ((char *)NULL); - } - dlen = strlen (current_dir); - if (current_dir[0] == '/' && dlen > 1) - current_dir[dlen++] = '/'; - - /* Turn /foo/./bar into /foo/bar. */ - if (file[0] == '.' && file[1] == '/') - file += 2; + ret = sh_makepath ((char *)NULL, file, (MP_DOCWD|MP_RMDOT)); + free (file); - strcpy (current_dir + dlen, file); - free (disposer); - return (current_dir); + return (ret); } /* A slightly related function. Get the prettiest name of this @@ -812,6 +710,72 @@ polite_directory_format (name) return (name); } +/* Trim NAME. If NAME begins with `~/', skip over tilde prefix. Trim to + keep any tilde prefix and PROMPT_DIRTRIM trailing directory components + and replace the intervening characters with `...' */ +char * +trim_pathname (name, maxlen) + char *name; + int maxlen; +{ + int nlen, ndirs; + intmax_t nskip; + char *nbeg, *nend, *ntail, *v; + + if (name == 0 || (nlen = strlen (name)) == 0) + return name; + nend = name + nlen; + + v = get_string_value ("PROMPT_DIRTRIM"); + if (v == 0 || *v == 0) + return name; + if (legal_number (v, &nskip) == 0 || nskip <= 0) + return name; + + /* Skip over tilde prefix */ + nbeg = name; + if (name[0] == '~') + for (nbeg = name; *nbeg; nbeg++) + if (*nbeg == '/') + { + nbeg++; + break; + } + if (*nbeg == 0) + return name; + + for (ndirs = 0, ntail = nbeg; *ntail; ntail++) + if (*ntail == '/') + ndirs++; + if (ndirs < nskip) + return name; + + for (ntail = (*nend == '/') ? nend : nend - 1; ntail > nbeg; ntail--) + { + if (*ntail == '/') + nskip--; + if (nskip == 0) + break; + } + if (ntail == nbeg) + return name; + + /* Now we want to return name[0..nbeg]+"..."+ntail, modifying name in place */ + nlen = ntail - nbeg; + if (nlen <= 3) + return name; + + *nbeg++ = '.'; + *nbeg++ = '.'; + *nbeg++ = '.'; + + nlen = nend - ntail; + memmove (nbeg, ntail, nlen); + nbeg[nlen] = '\0'; + + return name; +} + /* Given a string containing units of information separated by colons, return the next one pointed to by (P_INDEX), or NULL if there are no more. Advance (P_INDEX) to the character after the colon. */ @@ -850,16 +814,11 @@ extract_colon_unit (string, p_index) if (string[i]) (*p_index)++; /* Return "" in the case of a trailing `:'. */ - value = xmalloc (1); + value = (char *)xmalloc (1); value[0] = '\0'; } else - { - len = i - start; - value = xmalloc (1 + len); - strncpy (value, string + start, len); - value [len] = '\0'; - } + value = substring (string, start, i); return (value); } @@ -874,6 +833,11 @@ extract_colon_unit (string, p_index) extern char *get_dirstack_from_string __P((char *)); #endif +static char **bash_tilde_prefixes; +static char **bash_tilde_prefixes2; +static char **bash_tilde_suffixes; +static char **bash_tilde_suffixes2; + /* If tilde_expand hasn't been able to expand the text, perhaps it is a special shell expansion. This function is installed as the tilde_expansion_preexpansion_hook. It knows how to expand ~- and ~+. @@ -892,7 +856,7 @@ bash_special_tilde_expansions (text) else if (text[0] == '-' && text[1] == '\0') result = get_string_value ("OLDPWD"); #if defined (PUSHD_AND_POPD) - else if (isdigit (*text) || ((*text == '+' || *text == '-') && isdigit (text[1]))) + else if (DIGIT (*text) || ((*text == '+' || *text == '-') && DIGIT (text[1]))) result = get_dirstack_from_string (text); #endif @@ -908,36 +872,139 @@ tilde_initialize () static int times_called = 0; /* Tell the tilde expander that we want a crack first. */ - tilde_expansion_preexpansion_hook = (CPFunction *)bash_special_tilde_expansions; + tilde_expansion_preexpansion_hook = bash_special_tilde_expansions; /* Tell the tilde expander about special strings which start a tilde expansion, and the special strings that end one. Only do this once. tilde_initialize () is called from within bashline_reinitialize (). */ if (times_called++ == 0) { - tilde_additional_prefixes = (char **)xmalloc (3 * sizeof (char *)); - tilde_additional_prefixes[0] = "=~"; - tilde_additional_prefixes[1] = ":~"; - tilde_additional_prefixes[2] = (char *)NULL; - - tilde_additional_suffixes = (char **)xmalloc (3 * sizeof (char *)); - tilde_additional_suffixes[0] = ":"; - tilde_additional_suffixes[1] = "=~"; - tilde_additional_suffixes[2] = (char *)NULL; + bash_tilde_prefixes = strvec_create (3); + bash_tilde_prefixes[0] = "=~"; + bash_tilde_prefixes[1] = ":~"; + bash_tilde_prefixes[2] = (char *)NULL; + + bash_tilde_prefixes2 = strvec_create (2); + bash_tilde_prefixes2[0] = ":~"; + bash_tilde_prefixes2[1] = (char *)NULL; + + tilde_additional_prefixes = bash_tilde_prefixes; + + bash_tilde_suffixes = strvec_create (3); + bash_tilde_suffixes[0] = ":"; + bash_tilde_suffixes[1] = "=~"; /* XXX - ?? */ + bash_tilde_suffixes[2] = (char *)NULL; + + tilde_additional_suffixes = bash_tilde_suffixes; + + bash_tilde_suffixes2 = strvec_create (2); + bash_tilde_suffixes2[0] = ":"; + bash_tilde_suffixes2[1] = (char *)NULL; + } +} + +/* POSIX.2, 3.6.1: A tilde-prefix consists of an unquoted tilde character + at the beginning of the word, followed by all of the characters preceding + the first unquoted slash in the word, or all the characters in the word + if there is no slash...If none of the characters in the tilde-prefix are + quoted, the characters in the tilde-prefix following the tilde shell be + treated as a possible login name. */ + +#define TILDE_END(c) ((c) == '\0' || (c) == '/' || (c) == ':') + +static int +unquoted_tilde_word (s) + const char *s; +{ + const char *r; + + for (r = s; TILDE_END(*r) == 0; r++) + { + switch (*r) + { + case '\\': + case '\'': + case '"': + return 0; + } } + return 1; } +/* Find the end of the tilde-prefix starting at S, and return the tilde + prefix in newly-allocated memory. Return the length of the string in + *LENP. FLAGS tells whether or not we're in an assignment context -- + if so, `:' delimits the end of the tilde prefix as well. */ char * -bash_tilde_expand (s) - char *s; +bash_tilde_find_word (s, flags, lenp) + const char *s; + int flags, *lenp; +{ + const char *r; + char *ret; + int l; + + for (r = s; *r && *r != '/'; r++) + { + /* Short-circuit immediately if we see a quote character. Even though + POSIX says that `the first unquoted slash' (or `:') terminates the + tilde-prefix, in practice, any quoted portion of the tilde prefix + will cause it to not be expanded. */ + if (*r == '\\' || *r == '\'' || *r == '"') + { + ret = savestring (s); + if (lenp) + *lenp = 0; + return ret; + } + else if (flags && *r == ':') + break; + } + l = r - s; + ret = xmalloc (l + 1); + strncpy (ret, s, l); + ret[l] = '\0'; + if (lenp) + *lenp = l; + return ret; +} + +/* Tilde-expand S by running it through the tilde expansion library. + ASSIGN_P is 1 if this is a variable assignment, so the alternate + tilde prefixes should be enabled (`=~' and `:~', see above). If + ASSIGN_P is 2, we are expanding the rhs of an assignment statement, + so `=~' is not valid. */ +char * +bash_tilde_expand (s, assign_p) + const char *s; + int assign_p; { - int old_immed; + int old_immed, old_term, r; char *ret; old_immed = interrupt_immediately; - interrupt_immediately = 1; - ret = tilde_expand (s); + old_term = terminate_immediately; + /* We want to be able to interrupt tilde expansion. Ordinarily, we can just + jump to top_level, but we don't want to run any trap commands in a signal + handler context. We might be able to get away with just checking for + things like SIGINT and SIGQUIT. */ + if (any_signals_trapped () < 0) + interrupt_immediately = 1; + terminate_immediately = 1; + + tilde_additional_prefixes = assign_p == 0 ? (char **)0 + : (assign_p == 2 ? bash_tilde_prefixes2 : bash_tilde_prefixes); + if (assign_p == 2) + tilde_additional_suffixes = bash_tilde_suffixes2; + + r = (*s == '~') ? unquoted_tilde_word (s) : 1; + ret = r ? tilde_expand (s) : savestring (s); + interrupt_immediately = old_immed; + terminate_immediately = old_term; + + QUIT; + return (ret); } @@ -956,20 +1023,6 @@ static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL; # define NOGROUP (gid_t) -1 #endif -#if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX) -# define getmaxgroups() sysconf(_SC_NGROUPS_MAX) -#else -# if defined (NGROUPS_MAX) -# define getmaxgroups() NGROUPS_MAX -# else /* !NGROUPS_MAX */ -# if defined (NGROUPS) -# define getmaxgroups() NGROUPS -# else /* !NGROUPS */ -# define getmaxgroups() 64 -# endif /* !NGROUPS */ -# endif /* !NGROUPS_MAX */ -#endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */ - static void initialize_group_array () { @@ -1001,7 +1054,7 @@ initialize_group_array () if (i == ngroups && ngroups < maxgroups) { for (i = ngroups; i > 0; i--) - group_array[i] = group_array[i - 1]; + group_array[i] = group_array[i - 1]; group_array[0] = current_user.gid; ngroups++; } @@ -1012,8 +1065,8 @@ initialize_group_array () if (group_array[0] != current_user.gid) { for (i = 0; i < ngroups; i++) - if (group_array[i] == current_user.gid) - break; + if (group_array[i] == current_user.gid) + break; if (i < ngroups) { group_array[i] = group_array[0]; @@ -1062,7 +1115,6 @@ get_group_list (ngp) { static char **group_vector = (char **)NULL; register int i; - char *nbuf; if (group_vector) { @@ -1081,12 +1133,9 @@ get_group_list (ngp) return (char **)NULL; } - group_vector = (char **)xmalloc (ngroups * sizeof (char *)); + group_vector = strvec_create (ngroups); for (i = 0; i < ngroups; i++) - { - nbuf = itos ((int)group_array[i]); - group_vector[i] = nbuf; - } + group_vector[i] = itos (group_array[i]); if (ngp) *ngp = ngroups;