]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - general.c
Bash-5.0 patch 4: the wait builtin without arguments only waits for known children...
[thirdparty/bash.git] / general.c
index 4ac4de0419ca74f596ce6d852aa81293aa383c86..9542963aa2d04f9f2465da119c89e8dfb526c80e 100644 (file)
--- 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 <http://www.gnu.org/licenses/>.
+*/
 
 #include "config.h"
 
 #include "bashtypes.h"
-#ifndef _MINIX
+#if defined (HAVE_SYS_PARAM_H)
 #  include <sys/param.h>
 #endif
 #include "posixstat.h"
 #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 <mbstr.h>           /* mbschr */
+#endif
 
 #include <tilde/tilde.h>
 
 extern int errno;
 #endif /* !errno */
 
-extern int expand_aliases;
-extern int interactive_comments;
-extern int check_hashed_filenames;
-extern int source_uses_path;
-extern int source_searches_cwd;
+#ifdef __CYGWIN__
+#  include <sys/cygwin.h>
+#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;
@@ -69,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. */
@@ -76,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 */
@@ -97,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++;
@@ -145,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)
@@ -161,7 +229,7 @@ all_digits (string)
    not null. */
 int
 legal_number (string, result)
-     char *string;
+     const char *string;
      intmax_t *result;
 {
   intmax_t value;
@@ -170,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. */
@@ -181,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;
@@ -199,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))
@@ -215,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,
@@ -239,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 == '/'))
@@ -256,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;
@@ -268,7 +421,7 @@ assignment (string, flags)
   c = string[indx = 0];
 
 #if defined (ARRAY_VARS)
-  if ((legal_variable_starter (c) == 0) && (flags == 0 || c != '[')) /* ] */
+  if ((legal_variable_starter (c) == 0) && ((flags&1) == 0 || c != '[')) /* ] */
 #else
   if (legal_variable_starter (c) == 0)
 #endif
@@ -284,7 +437,9 @@ 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] == '=')
@@ -307,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         */
@@ -354,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)
@@ -362,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. */
 
@@ -387,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
@@ -395,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;
@@ -464,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;
@@ -475,28 +661,66 @@ check_binary_file (sample, sample_len)
       c = sample[i];
       if (c == '\n')
        return (0);
-
-#if 0
-      if (ISSPACE (c) == 0 && ISPRINT (c) == 0)
-#else
       if (c == '\0')
-#endif
        return (1);
-      
     }
 
   return (0);
 }
 
+/* **************************************************************** */
+/*                                                                 */
+/*                 Functions to manipulate pipes                   */
+/*                                                                 */
+/* **************************************************************** */
+
+int
+sh_openpipe (pv)
+     int *pv;
+{
+  int r;
+
+  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
+sh_closepipe (pv)
+     int *pv;
+{
+  if (pv[0] >= 0)
+    close (pv[0]);
+
+  if (pv[1] >= 0)
+    close (pv[1]);
+
+  pv[0] = pv[1] = -1;
+  return 0;
+}
+
 /* **************************************************************** */
 /*                                                                 */
 /*                 Functions to inspect pathnames                  */
 /*                                                                 */
 /* **************************************************************** */
 
+int
+file_exists (fn)
+     const char *fn;
+{
+  struct stat sb;
+
+  return (stat (fn, &sb) == 0);
+}
+
 int
 file_isdir (fn)
-     char *fn;
+     const char *fn;
 {
   struct stat sb;
 
@@ -505,11 +729,27 @@ file_isdir (fn)
 
 int
 file_iswdir (fn)
-     char *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'
    to decide whether or not to look up a directory name in $CDPATH. */
 int
@@ -538,7 +778,7 @@ int
 absolute_program (string)
      const char *string;
 {
-  return ((char *)xstrchr (string, '/') != (char *)NULL);
+  return ((char *)mbschr (string, '/') != (char *)NULL);
 }
 
 /* **************************************************************** */
@@ -553,7 +793,7 @@ absolute_program (string)
    begin with. */
 char *
 make_absolute (string, dot_path)
-     char *string, *dot_path;
+     const char *string, *dot_path;
 {
   char *result;
 
@@ -562,7 +802,8 @@ make_absolute (string, dot_path)
     {
       char pathbuf[PATH_MAX + 1];
 
-      cygwin_conv_to_full_posix_path (string, pathbuf);
+      /* 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
@@ -641,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. */
@@ -844,11 +1172,20 @@ 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;
+  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);
@@ -857,7 +1194,14 @@ bash_tilde_expand (s, assign_p)
 
   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);
 }
 
@@ -1027,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);
+}
+
+