No specific user configuration
[platform/upstream/bash.git] / general.c
index 1d9cda0..087689e 100644 (file)
--- a/general.c
+++ b/general.c
@@ -1,27 +1,27 @@
 /* general.c -- Stuff that is used by all files. */
 
-/* Copyright (C) 1987-1999 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, 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 "chartypes.h"
 #include <errno.h>
 
+#include "bashintl.h"
+
 #include "shell.h"
+#include "test.h"
+#include "trap.h"
+
 #include <tilde/tilde.h>
 
 #if !defined (errno)
@@ -44,14 +49,17 @@ 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;
 
+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
@@ -62,6 +70,7 @@ posix_initialize (on)
   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. */
@@ -90,7 +99,7 @@ string_to_rlimtype (s)
   neg = 0;
   while (s && *s && whitespace (*s))
     s++;
-  if (*s == '-' || *s == '+')
+  if (s && (*s == '-' || *s == '+'))
     {
       neg = *s == '-';
       s++;
@@ -154,27 +163,30 @@ all_digits (string)
    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;
 
+  if (string == 0)
+    return 0;
+
   errno = 0;
-  value = strtol (string, &ep, 10);
-  if (errno)
+  value = strtoimax (string, &ep, 10);
+  if (errno || ep == string)
     return 0;  /* errno is set on overflow or underflow */
 
-  /* Skip any trailing whitespace, since strtol does not. */
+  /* 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;
@@ -220,18 +232,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         */
@@ -279,6 +359,24 @@ sh_unset_nodelay_mode (fd)
   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
    of /dev/tty to fail. */
 
@@ -304,7 +402,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
@@ -352,8 +451,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;
@@ -362,13 +461,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);
 }
  
@@ -390,8 +491,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);
     }
 
@@ -400,26 +500,84 @@ check_binary_file (sample, sample_len)
 
 /* **************************************************************** */
 /*                                                                 */
-/*                 Functions to manipulate pathnames               */
+/*                 Functions to manipulate pipes                   */
 /*                                                                 */
 /* **************************************************************** */
 
-/* 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
+sh_openpipe (pv)
+     int *pv;
 {
-  char *result;
+  int r;
 
-  if (dot_path == 0 || ABSPATH(string))
-    result = savestring (string);
-  else
-    result = sh_makepath (dot_path, string, 0);
+  if ((r = pipe (pv)) < 0)
+    return r;
 
-  return (result);
+  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)
+     char *fn;
+{
+  struct stat sb;
+
+  return (stat (fn, &sb) == 0);
+}
+
+int
+file_isdir (fn)
+     char *fn;
+{
+  struct stat sb;
+
+  return ((stat (fn, &sb) == 0) && S_ISDIR (sb.st_mode));
+}
+
+int
+file_iswdir (fn)
+     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'
@@ -450,19 +608,57 @@ int
 absolute_program (string)
      const char *string;
 {
-  return ((char *)strchr (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)
+     char *string, *dot_path;
+{
+  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 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);
@@ -478,7 +674,7 @@ full_pathname (file)
 {
   char *ret;
 
-  file = (*file == '~') ? bash_tilde_expand (file) : savestring (file);
+  file = (*file == '~') ? bash_tilde_expand (file, 0) : savestring (file);
 
   if (ABSPATH(file))
     return (file);
@@ -515,6 +711,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. */
@@ -572,9 +834,10 @@ extract_colon_unit (string, p_index)
 extern char *get_dirstack_from_string __P((char *));
 #endif
 
-/* Reserved for post-bash-2.05a */
 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
@@ -617,19 +880,27 @@ tilde_initialize ()
      tilde_initialize () is called from within bashline_reinitialize (). */
   if (times_called++ == 0)
     {
-      bash_tilde_prefixes = alloc_array (3);
+      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 = alloc_array (3);
+      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;
     }
 }
 
@@ -661,19 +932,80 @@ unquoted_tilde_word (s)
   return 1;
 }
 
-/* Tilde-expand S by running it through the tilde expansion library. */
+/* 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)
+bash_tilde_find_word (s, flags, lenp)
      const char *s;
+     int flags, *lenp;
 {
-  int old_immed, r;
+  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, old_term, r;
   char *ret;
 
   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;
+
+  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);
 }
 
@@ -692,20 +1024,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 ()
 {
@@ -798,7 +1116,6 @@ get_group_list (ngp)
 {
   static char **group_vector = (char **)NULL;
   register int i;
-  char *nbuf;
 
   if (group_vector)
     {
@@ -817,12 +1134,9 @@ get_group_list (ngp)
       return (char **)NULL;
     }
 
-  group_vector = alloc_array (ngroups);
+  group_vector = strvec_create (ngroups);
   for (i = 0; i < ngroups; i++)
-    {
-      nbuf = itos (group_array[i]);
-      group_vector[i] = nbuf;
-    }
+    group_vector[i] = itos (group_array[i]);
 
   if (ngp)
     *ngp = ngroups;