X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=general.c;h=9542963aa2d04f9f2465da119c89e8dfb526c80e;hb=16c907aa3bb427618733e5a6f2f4e2fc5a3488d3;hp=df4b11332e2e6abb69d8381bc582bffd1f8b156e;hpb=b80f6443b6b7b620c7272664c66ecb0b120a0998;p=thirdparty%2Fbash.git diff --git a/general.c b/general.c index df4b11332..9542963aa 100644 --- a/general.c +++ b/general.c @@ -1,27 +1,27 @@ /* general.c -- Stuff that is used by all files. */ -/* Copyright (C) 1987-2004 Free Software Foundation, Inc. +/* Copyright (C) 1987-2016 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, 59 Temple Place, Suite 330, Boston, MA 02111 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" @@ -39,27 +39,62 @@ #include "bashintl.h" #include "shell.h" +#include "parser.h" +#include "flags.h" +#include "findcmd.h" +#include "test.h" +#include "trap.h" + +#include "builtins/common.h" + +#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR) +# include /* mbschr */ +#endif + #include #if !defined (errno) extern int errno; #endif /* !errno */ -extern int expand_aliases; -extern int interrupt_immediately; -extern int interactive_comments; -extern int check_hashed_filenames; -extern int source_uses_path; -extern int source_searches_cwd; +#ifdef __CYGWIN__ +# include +#endif 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 = N_("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'. This currently + modifies the following variables which are controlled via shopt: + interactive_comments + source_uses_path + expand_aliases + inherit_errexit + print_shift_error + + and the following variables which cannot be user-modified: + + source_searches_cwd + + If we add to the first list, we need to change the table and functions + below */ + +static struct { + int *posix_mode_var; +} posix_vars[] = +{ + &interactive_comments, + &source_uses_path, + &expand_aliases, + &inherit_errexit, + &print_shift_error, + 0 +}; -/* Do whatever is necessary to initialize `Posix mode'. */ void posix_initialize (on) int on; @@ -68,6 +103,10 @@ posix_initialize (on) if (on != 0) { interactive_comments = source_uses_path = expand_aliases = 1; + inherit_errexit = 1; + source_searches_cwd = 0; + print_shift_error = 1; + } /* Things that should be turned on when posix mode is disabled. */ @@ -75,9 +114,39 @@ posix_initialize (on) { source_searches_cwd = 1; expand_aliases = interactive_shell; + print_shift_error = 0; } } +int +num_posix_options () +{ + return ((sizeof (posix_vars) / sizeof (posix_vars[0])) - 1); +} + +char * +get_posix_options (bitmap) + char *bitmap; +{ + register int i; + + if (bitmap == 0) + bitmap = (char *)xmalloc (num_posix_options ()); /* no trailing NULL */ + for (i = 0; posix_vars[i].posix_mode_var; i++) + bitmap[i] = *(posix_vars[i].posix_mode_var); + return bitmap; +} + +void +set_posix_options (bitmap) + const char *bitmap; +{ + register int i; + + for (i = 0; posix_vars[i].posix_mode_var; i++) + *(posix_vars[i].posix_mode_var) = bitmap[i]; +} + /* **************************************************************** */ /* */ /* Functions to convert to and from and display non-standard types */ @@ -96,7 +165,7 @@ string_to_rlimtype (s) neg = 0; while (s && *s && whitespace (*s)) s++; - if (*s == '-' || *s == '+') + if (s && (*s == '-' || *s == '+')) { neg = *s == '-'; s++; @@ -144,9 +213,9 @@ print_rlimtype (n, addnl) /* Return non-zero if all of the characters in STRING are digits. */ int all_digits (string) - char *string; + const char *string; { - register char *s; + register const char *s; for (s = string; *s; s++) if (DIGIT (*s) == 0) @@ -160,7 +229,7 @@ all_digits (string) not null. */ int legal_number (string, result) - char *string; + const char *string; intmax_t *result; { intmax_t value; @@ -169,9 +238,12 @@ legal_number (string, result) if (result) *result = 0; + if (string == 0) + return 0; + errno = 0; value = strtoimax (string, &ep, 10); - if (errno) + if (errno || ep == string) return 0; /* errno is set on overflow or underflow */ /* Skip any trailing whitespace, since strtoimax does not. */ @@ -180,7 +252,7 @@ legal_number (string, result) /* 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; @@ -198,9 +270,9 @@ legal_number (string, result) digit. */ int legal_identifier (name) - char *name; + const char *name; { - register char *s; + register const char *s; unsigned char c; if (!name || !(c = *name) || (legal_variable_starter (c) == 0)) @@ -214,6 +286,57 @@ legal_identifier (name) return (1); } +/* Return 1 if NAME is a valid value that can be assigned to a nameref + variable. FLAGS can be 2, in which case the name is going to be used + to create a variable. Other values are currently unused, but could + be used to allow values to be stored and indirectly referenced, but + not used in assignments. */ +int +valid_nameref_value (name, flags) + const char *name; + int flags; +{ + if (name == 0 || *name == 0) + return 0; + + /* valid identifier */ +#if defined (ARRAY_VARS) + if (legal_identifier (name) || (flags != 2 && valid_array_reference (name, 0))) +#else + if (legal_identifier (name)) +#endif + return 1; + + return 0; +} + +int +check_selfref (name, value, flags) + const char *name; + char *value; + int flags; +{ + char *t; + + if (STREQ (name, value)) + return 1; + +#if defined (ARRAY_VARS) + if (valid_array_reference (value, 0)) + { + t = array_variable_name (value, 0, (char **)NULL, (int *)NULL); + if (t && STREQ (name, t)) + { + free (t); + return 1; + } + free (t); + } +#endif + + return 0; /* not a self reference */ +} + /* Make sure that WORD is a valid shell identifier, i.e. does not contain a dollar sign, nor is quoted in any way. Nor does it consist of all digits. If CHECK_WORD is non-zero, @@ -238,15 +361,45 @@ check_identifier (word, check_word) return (1); } +/* Return 1 if STRING is a function name that the shell will import from + the environment. Currently we reject attempts to import shell functions + containing slashes, beginning with newlines or containing blanks. In + Posix mode, we require that STRING be a valid shell identifier. Not + used yet. */ +int +importable_function_name (string, len) + const char *string; + size_t len; +{ + if (absolute_program (string)) /* don't allow slash */ + return 0; + if (*string == '\n') /* can't start with a newline */ + return 0; + if (shellblank (*string) || shellblank(string[len-1])) + return 0; + return (posixly_correct ? legal_identifier (string) : 1); +} + +int +exportable_function_name (string) + const char *string; +{ + if (absolute_program (string)) + return 0; + if (mbschr (string, '=') != 0) + return 0; + 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; + const char *string; int flags; { - register char *s; + register const char *s; for (s = string; *s; s++) if (shellbreak (*s) || shellxquote (*s) || shellexp (*s) || (*s == '/')) @@ -255,7 +408,8 @@ legal_alias_name (string, flags) } /* Returns non-zero if STRING is an assignment statement. The returned value - is the index of the `=' sign. */ + is the index of the `=' sign. If FLAGS&1 we are expecting a compound assignment + and don't want an array subscript before the `='. */ int assignment (string, flags) const char *string; @@ -267,7 +421,7 @@ assignment (string, flags) c = string[indx = 0]; #if defined (ARRAY_VARS) - if ((legal_variable_starter (c) == 0) && (flags && c != '[')) /* ] */ + if ((legal_variable_starter (c) == 0) && ((flags&1) == 0 || c != '[')) /* ] */ #else if (legal_variable_starter (c) == 0) #endif @@ -283,13 +437,21 @@ assignment (string, flags) #if defined (ARRAY_VARS) if (c == '[') { - newi = skipsubscript (string, indx); + newi = skipsubscript (string, indx, (flags & 2) ? 1 : 0); + /* XXX - why not check for blank subscripts here, if we do in + valid_array_reference? */ 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) @@ -300,6 +462,20 @@ assignment (string, flags) return (0); } +int +line_isblank (line) + const char *line; +{ + register int i; + + if (line == 0) + return 0; /* XXX */ + for (i = 0; line[i]; i++) + if (isblank ((unsigned char)line[i]) == 0) + break; + return (line[i] == '\0'); +} + /* **************************************************************** */ /* */ /* Functions to manage files and file descriptors */ @@ -347,6 +523,14 @@ sh_unset_nodelay_mode (fd) return 0; } +/* Just a wrapper for the define in include/filecntl.h */ +int +sh_setclexec (fd) + int fd; +{ + return (SET_CLOSE_ON_EXEC (fd)); +} + /* Return 1 if file descriptor FD is valid; 0 otherwise. */ int sh_validfd (fd) @@ -355,6 +539,14 @@ sh_validfd (fd) return (fcntl (fd, F_GETFD, 0) >= 0); } +int +fd_ispipe (fd) + int fd; +{ + errno = 0; + return ((lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE)); +} + /* There is a bug in the NeXT 2.1 rlogind that causes opens of /dev/tty to fail. */ @@ -380,7 +572,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 @@ -388,7 +581,7 @@ check_dev_tty () corresponding to PATH1 and PATH2, respectively. */ int same_file (path1, path2, stp1, stp2) - char *path1, *path2; + const char *path1, *path2; struct stat *stp1, *stp2; { struct stat st1, st2; @@ -457,7 +650,7 @@ move_to_high_fd (fd, check_new, maxfd) int check_binary_file (sample, sample_len) - char *sample; + const char *sample; int sample_len; { register int i; @@ -468,8 +661,7 @@ check_binary_file (sample, sample_len) c = sample[i]; if (c == '\n') return (0); - - if (ISSPACE (c) == 0 && ISPRINT (c) == 0) + if (c == '\0') return (1); } @@ -478,58 +670,84 @@ check_binary_file (sample, sample_len) /* **************************************************************** */ /* */ -/* Functions to inspect pathnames */ +/* Functions to manipulate pipes */ /* */ /* **************************************************************** */ int -file_isdir (fn) - char *fn; +sh_openpipe (pv) + int *pv; { - struct stat sb; + int r; - return ((stat (fn, &sb) == 0) && S_ISDIR (sb.st_mode)); + if ((r = pipe (pv)) < 0) + return r; + + pv[0] = move_to_high_fd (pv[0], 1, 64); + pv[1] = move_to_high_fd (pv[1], 1, 64); + + return 0; } int -file_iswdir (fn) - char *fn; +sh_closepipe (pv) + int *pv; { - return (file_isdir (fn) && test_eaccess (fn, W_OK) == 0); -} + if (pv[0] >= 0) + close (pv[0]); + + if (pv[1] >= 0) + close (pv[1]); + pv[0] = pv[1] = -1; + return 0; +} /* **************************************************************** */ /* */ -/* Functions to manipulate pathnames */ +/* Functions to inspect 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; +int +file_exists (fn) + const char *fn; { - char *result; + struct stat sb; - if (dot_path == 0 || ABSPATH(string)) -#ifdef __CYGWIN__ - { - char pathbuf[PATH_MAX + 1]; + return (stat (fn, &sb) == 0); +} - cygwin_conv_to_full_posix_path (string, pathbuf); - result = savestring (pathbuf); - } -#else - result = savestring (string); -#endif - else - result = sh_makepath (dot_path, string, 0); +int +file_isdir (fn) + const char *fn; +{ + struct stat sb; - return (result); + return ((stat (fn, &sb) == 0) && S_ISDIR (sb.st_mode)); +} + +int +file_iswdir (fn) + const char *fn; +{ + return (file_isdir (fn) && sh_eaccess (fn, W_OK) == 0); +} + +/* 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); + + /* string[0] == '.' */ + if (PATHSEP(string[1]) || (string[1] == '.' && PATHSEP(string[2]))) + return (1); + + return (0); } /* Return 1 if STRING contains an absolute pathname, else 0. Used by `cd' @@ -560,19 +778,58 @@ int absolute_program (string) const char *string; { - return ((char *)xstrchr (string, '/') != (char *)NULL); + 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) + const char *string, *dot_path; +{ + char *result; + + if (dot_path == 0 || ABSPATH(string)) +#ifdef __CYGWIN__ + { + char pathbuf[PATH_MAX + 1]; + + /* WAS cygwin_conv_to_full_posix_path (string, pathbuf); */ + cygwin_conv_path (CCP_WIN_A_TO_POSIX, string, pathbuf, PATH_MAX); + 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 0 if (absolute_pathname (string) == 0) return (string); +#endif + + if (string[0] == '/' && string[1] == 0) + return (string); p = (char *)strrchr (string, '/'); return (p ? ++p : string); @@ -625,6 +882,93 @@ 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; +} + +/* Return a printable representation of FN without special characters. The + caller is responsible for freeing memory if this returns something other + than its argument. If FLAGS is non-zero, we are printing for portable + re-input and should single-quote filenames appropriately. */ +char * +printable_filename (fn, flags) + char *fn; + int flags; +{ + char *newf; + + if (ansic_shouldquote (fn)) + newf = ansic_quote (fn, 0, NULL); + else if (flags && sh_contains_shell_metas (fn)) + newf = sh_single_quote (fn); + else + newf = fn; + + return newf; +} + /* 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. */ @@ -683,7 +1027,9 @@ 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 @@ -731,6 +1077,10 @@ tilde_initialize () 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); @@ -739,6 +1089,10 @@ tilde_initialize () 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; } } @@ -770,23 +1124,84 @@ unquoted_tilde_word (s) 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_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). */ + 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, r; + int old_immed, old_term, r; char *ret; +#if 0 old_immed = interrupt_immediately; - interrupt_immediately = 1; - tilde_additional_prefixes = assign_p ? bash_tilde_prefixes : (char **)0; + 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; +#endif + + 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); + +#if 0 interrupt_immediately = old_immed; + terminate_immediately = old_term; +#endif + + QUIT; + return (ret); } @@ -956,3 +1371,61 @@ get_group_array (ngp) *ngp = ngroups; return group_iarray; } + +/* **************************************************************** */ +/* */ +/* Miscellaneous functions */ +/* */ +/* **************************************************************** */ + +/* Return a value for PATH that is guaranteed to find all of the standard + utilities. This uses Posix.2 configuration variables, if present. It + uses a value defined in config.h as a last resort. */ +char * +conf_standard_path () +{ +#if defined (_CS_PATH) && defined (HAVE_CONFSTR) + char *p; + size_t len; + + len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0); + if (len > 0) + { + p = (char *)xmalloc (len + 2); + *p = '\0'; + confstr (_CS_PATH, p, len); + return (p); + } + else + return (savestring (STANDARD_UTILS_PATH)); +#else /* !_CS_PATH || !HAVE_CONFSTR */ +# if defined (CS_PATH) + return (savestring (CS_PATH)); +# else + return (savestring (STANDARD_UTILS_PATH)); +# endif /* !CS_PATH */ +#endif /* !_CS_PATH || !HAVE_CONFSTR */ +} + +int +default_columns () +{ + char *v; + int c; + + c = -1; + v = get_string_value ("COLUMNS"); + if (v && *v) + { + c = atoi (v); + if (c > 0) + return c; + } + + if (check_window_size) + get_new_window_size (0, (int *)0, &c); + + return (c > 0 ? c : 80); +} + +