Imported from ../bash-2.0.tar.gz.
[platform/upstream/bash.git] / general.c
index 9ccfce6..9623df0 100644 (file)
--- a/general.c
+++ b/general.c
    with Bash; see the file COPYING.  If not, write to the Free Software
    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
 
-#include "config.h"    /* includes unistd.h for us */
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
+#include "config.h"
+
 #include "bashtypes.h"
 #include <sys/param.h>
-#if defined (_POSIX_VERSION)
-#  if defined (amiga) && defined (USGr4)
-#    define _POSIX_SOURCE
-#  endif
-#  include <signal.h>
-#  if defined (amiga) && defined (USGr4)
-#    undef _POSIX_SOURCE
-#  endif
-#endif /* _POSIX_VERSION */
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
 #include "filecntl.h"
 #include "bashansi.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
 #include "shell.h"
 #include <tilde/tilde.h>
 
-#if !defined (USG) || defined (HAVE_RESOURCE)
+#if defined (TIME_WITH_SYS_TIME)
 #  include <sys/time.h>
+#  include <time.h>
+#else
+#  if defined (HAVE_SYS_TIME_H)
+#    include <sys/time.h>
+#  else
+#    include <time.h>
+#  endif
 #endif
 
 #include <sys/times.h>
 extern int errno;
 #endif /* !errno */
 
-/* Make the functions strchr and strrchr if they do not exist. */
-#if !defined (HAVE_STRCHR)
-char *
-strchr (string, c)
-     char *string;
-     int c;
-{
-  register int i;
-
-  for (i = 0; string && string[i]; i++)
-    if (string[i] == c)
-      return ((char *) (string + i));
-
-  return ((char *) NULL);
-}
-
-char *
-strrchr (string, c)
-     char *string;
-     int c;
-{
-  register int i;
-
-  if (string)
-    i = strlen (string) - 1;
-  else
-    i = -1;
-
-  for (; string && i > -1; i--)
-    if (string[i] == c)
-      return ((char *) (string + i));
-
-  return ((char *) NULL);
-}
-#endif /* !HAVE_STRCHR */
-
-/* **************************************************************** */
-/*                                                                 */
-/*                Memory Allocation and Deallocation.              */
-/*                                                                 */
-/* **************************************************************** */
-
-char *
-xmalloc (size)
-     int size;
-{
-  register char *temp = (char *)malloc (size);
-
-  if (!temp)
-    fatal_error ("Out of virtual memory!");
-
-  return (temp);
-}
-
-char *
-xrealloc (pointer, size)
-     GENPTR pointer;
-     int size;
-{
-  char *temp;
-
-  if (!pointer)
-    temp = xmalloc (size);
-  else
-    temp = (char *)realloc (pointer, size);
-
-  if (!temp)
-    fatal_error ("Out of virtual memory!");
+#ifndef to_upper
+#  define to_upper(c) (islower(c) ? toupper(c) : (c))
+#  define to_lower(c) (isupper(c) ? tolower(c) : (c))
+#endif
 
-  return (temp);
-}
+extern int interrupt_immediately;
+extern int interactive_comments;
+extern char *bash_getcwd_errstr;
 
-/* Use this as the function to call when adding unwind protects so we
-   don't need to know what free() returns. */
+/* Do whatever is necessary to initialize `Posix mode'. */
 void
-xfree (string)
-     char *string;
+posix_initialize (on)
+     int on;
 {
-  free (string);
+  interactive_comments = on != 0;
 }
 
 /* **************************************************************** */
@@ -161,8 +103,8 @@ itos (i)
 
   ui = (unsigned int) i;
 
-  buf[MAX_INT_LEN - 1] = '\0';
-  p = &buf[MAX_INT_LEN - 2];
+  p = buf + MAX_INT_LEN - 2;
+  p[1] = '\0';
 
   do
     *p-- = (ui % 10) + '0';
@@ -176,21 +118,6 @@ itos (i)
   return (ret);
 }
 
-/* Return non-zero if all of the characters in STRING are digits. */
-int
-all_digits (string)
-     char *string;
-{
-  while (*string)
-    {
-      if (!digit (*string))
-       return (0);
-      else
-       string++;
-    }
-  return (1);
-}
-
 /* atol(3) is not universal */
 long
 string_to_long (s)
@@ -211,6 +138,12 @@ string_to_long (s)
   return (neg ? -ret : ret);
 }
 
+/* **************************************************************** */
+/*                                                                 */
+/*  Functions to convert to and from and display non-standard types */
+/*                                                                 */
+/* **************************************************************** */
+
 #if defined (RLIMTYPE)
 RLIMTYPE
 string_to_rlimtype (s)
@@ -258,6 +191,162 @@ print_rlimtype (n, addnl)
 }
 #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;
+}
+  
+/* 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;
+}
+
+/* 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);
+}
+#endif /* HAVE_TIMES */
+
+/* **************************************************************** */
+/*                                                                 */
+/*                    Input Validation Functions                   */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return non-zero if all of the characters in STRING are digits. */
+int
+all_digits (string)
+     char *string;
+{
+  while (*string)
+    {
+      if (!digit (*string))
+       return (0);
+      else
+       string++;
+    }
+  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. */
+int
+legal_number (string, result)
+     char *string;
+     long *result;
+{
+  int sign;
+  long value;
+
+  sign = 1;
+  value = 0;
+
+  if (result)
+    *result = 0;
+
+  /* Skip leading whitespace characters. */
+  while (whitespace (*string))
+    string++;
+
+  if (!*string)
+    return (0);
+
+  /* We allow leading `-' or `+'. */
+  if (*string == '-' || *string == '+')
+    {
+      if (!digit (string[1]))
+       return (0);
+
+      if (*string == '-')
+       sign = -1;
+
+      string++;
+    }
+
+  while (digit (*string))
+    {
+      if (result)
+       value = (value * 10) + digit_value (*string);
+      string++;
+    }
+
+  /* Skip trailing whitespace, if any. */
+  while (whitespace (*string))
+    string++;
+
+  /* Error if not at end of string. */
+  if (*string)
+    return (0);
+
+  if (result)
+    *result = value * sign;
+
+  return (1);
+}
+
 /* Return 1 if this token is a legal shell `identifier'; that is, it consists
    solely of letters, digits, and underscores, and does not begin with a
    digit. */
@@ -267,12 +356,12 @@ legal_identifier (name)
 {
   register char *s;
 
-  if (!name || !*name || digit (*name))
+  if (!name || !*name || (legal_variable_starter (*name) == 0))
     return (0);
 
-  for (s = name; s && *s; s++)
+  for (s = name + 1; *s; s++)
     {
-      if (!isletter (*s) && !digit (*s) && (*s != '_'))
+      if (legal_variable_char (*s) == 0)
         return (0);
     }
   return (1);
@@ -283,24 +372,31 @@ legal_identifier (name)
    does it consist of all digits.  If CHECK_WORD is non-zero,
    the word is checked to ensure that it consists of only letters,
    digits, and underscores. */
+int
 check_identifier (word, check_word)
      WORD_DESC *word;
      int check_word;
 {
-  if (word->dollar_present || word->quoted || all_digits (word->word))
+  if ((word->flags & (W_HASDOLLAR|W_QUOTED)) || all_digits (word->word))
     {
-      report_error ("`%s' is 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)
     {
-      report_error ("`%s' is not a valid identifier", word->word);
+      internal_error ("`%s': not a valid identifier", word->word);
       return (0);
     }
   else
     return (1);
 }
 
+/* **************************************************************** */
+/*                                                                 */
+/*          Functions to manage files and file descriptors         */
+/*                                                                 */
+/* **************************************************************** */
+
 /* A function to unset no-delay mode on a file descriptor.  Used in shell.c
    to unset it on the fd passed as stdin.  Should be called on stdin if
    readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */
@@ -316,258 +412,159 @@ void
 unset_nodelay_mode (fd)
      int fd;
 {
-  int flags, set = 0;
+  int flags, set;
 
   if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
     return;
 
-#if defined (O_NONBLOCK)
+  set = 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)
     {
       flags &= ~O_NONBLOCK;
       set++;
     }
-#endif /* O_NONBLOCK */
-
-#if defined (O_NDELAY)
-  if (flags & O_NDELAY)
-    {
-      flags &= ~O_NDELAY;
-      set++;
-    }
-#endif /* O_NDELAY */
 
   if (set)
     fcntl (fd, F_SETFL, flags);
 }
 
-
-/* **************************************************************** */
-/*                                                                 */
-/*                     Generic List Functions                      */
-/*                                                                 */
-/* **************************************************************** */
-
-/* Call FUNCTION on every member of LIST, a generic list. */
+  /* There is a bug in the NeXT 2.1 rlogind that causes opens
+     of /dev/tty to fail. */
 void
-map_over_list (list, function)
-     GENERIC_LIST *list;
-     Function *function;
+check_dev_tty ()
 {
-  while (list)
-    {
-      (*function) (list);
-      list = list->next;
-    }
-}
+  int tty_fd;
+  char *tty;
 
-/* Call FUNCTION on every string in WORDS. */
-void
-map_over_words (words, function)
-     WORD_LIST *words;
-     Function *function;
-{
-  while (words)
-    {
-      (*function)(words->word->word);
-      words = words->next;
-    }
-}
-
-/* Reverse the chain of structures in LIST.  Output the new head
-   of the chain.  You should always assign the output value of this
-   function to something, or you will lose the chain. */
-GENERIC_LIST *
-reverse_list (list)
-     GENERIC_LIST *list;
-{
-  register GENERIC_LIST *next, *prev = (GENERIC_LIST *)NULL;
+  tty_fd = open ("/dev/tty", O_RDWR);
 
-  while (list)
+  if (tty_fd < 0)
     {
-      next = list->next;
-      list->next = prev;
-      prev = list;
-      list = next;
+      tty = (char *)ttyname (fileno (stdin));
+      if (tty == 0)
+       return;
+      tty_fd = open (tty, O_RDWR);
     }
-  return (prev);
+  close (tty_fd);
 }
 
-/* Return the number of elements in LIST, a generic list. */
+/* Return 1 if PATH1 and PATH2 are the same file.  This is kind of
+   expensive.  If non-NULL STP1 and STP2 point to stat structures
+   corresponding to PATH1 and PATH2, respectively. */
 int
-list_length (list)
-     GENERIC_LIST *list;
+same_file (path1, path2, stp1, stp2)
+     char *path1, *path2;
+     struct stat *stp1, *stp2;
 {
-  register int i;
-
-  for (i = 0; list; list = list->next, i++);
-  return (i);
-}
+  struct stat st1, st2;
 
-/* A global variable which acts as a sentinel for an `error' list return. */
-GENERIC_LIST global_error_list;
-
-/* Delete the element of LIST which satisfies the predicate function COMPARER.
-   Returns the element that was deleted, so you can dispose of it, or -1 if
-   the element wasn't found.  COMPARER is called with the list element and
-   then ARG.  Note that LIST contains the address of a variable which points
-   to the list.  You might call this function like this:
-
-   SHELL_VAR *elt = delete_element (&variable_list, check_var_has_name, "foo");
-   dispose_variable (elt);
-*/
-GENERIC_LIST *
-delete_element (list, comparer, arg)
-     GENERIC_LIST **list;
-     Function *comparer;
-     char *arg;
-{
-  register GENERIC_LIST *prev = (GENERIC_LIST *)NULL;
-  register GENERIC_LIST *temp = *list;
-
-  while (temp)
+  if (stp1 == NULL)
     {
-      if ((*comparer) (temp, arg))
-       {
-         if (prev)
-           prev->next = temp->next;
-         else
-           *list = temp->next;
-         return (temp);
-       }
-      prev = temp;
-      temp = temp->next;
+      if (stat (path1, &st1) != 0)
+       return (0);
+      stp1 = &st1;
     }
-  return ((GENERIC_LIST *)&global_error_list);
-}
 
-/* Find NAME in ARRAY.  Return the index of NAME, or -1 if not present.
-   ARRAY should be NULL terminated. */
-int
-find_name_in_list (name, array)
-     char *name, **array;
-{
-  int i;
-
-  for (i = 0; array[i]; i++)
-    if (strcmp (name, array[i]) == 0)
-      return (i);
+  if (stp2 == NULL)
+    {
+      if (stat (path2, &st2) != 0)
+       return (0);
+      stp2 = &st2;
+    }
 
-  return (-1);
+  return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
 }
 
-/* Return the length of ARRAY, a NULL terminated array of char *. */
+/* Move FD to a number close to the maximum number of file descriptors
+   allowed in the shell process, to avoid the user stepping on it with
+   redirection and causing us extra work.  If CHECK_NEW is non-zero,
+   we check whether or not the file descriptors are in use before
+   duplicating FD onto them. */
 int
-array_len (array)
-     char **array;
+move_to_high_fd (fd, check_new)
+     int fd, check_new;
 {
-  register int i;
-  for (i = 0; array[i]; i++);
-  return (i);
-}
+  int script_fd, nfds, ignore;
 
-/* Free the contents of ARRAY, a NULL terminated array of char *. */
-void
-free_array (array)
-     char **array;
-{
-  register int i = 0;
+  nfds = getdtablesize ();
+  if (nfds <= 0)
+    nfds = 20;
+  if (nfds > 256)
+    nfds = 256;
 
-  if (!array) return;
+  for (nfds--; check_new && nfds > 3; nfds--)
+    if (fcntl (nfds, F_GETFD, &ignore) == -1)
+      break;
 
-  while (array[i])
-    free (array[i++]);
-  free (array);
-}
-
-/* Allocate and return a new copy of ARRAY and its contents. */
-char **
-copy_array (array)
-     char **array;
-{
-  register int i;
-  int len;
-  char **new_array;
+  if (nfds && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1)
+    {
+      if (check_new == 0 || fd != fileno (stderr))     /* don't close stderr */
+       close (fd);
+      return (script_fd);
+    }
 
-  len = array_len (array);
+  return (fd);
+}
+/* Return non-zero if the characters from SAMPLE are not all valid
+   characters to be found in the first line of a shell script.  We
+   check up to the first newline, or SAMPLE_LEN, whichever comes first.
+   All of the characters must be printable or whitespace. */
 
-  new_array = (char **)xmalloc ((len + 1) * sizeof (char *));
-  for (i = 0; array[i]; i++)
-    new_array[i] = savestring (array[i]);
-  new_array[i] = (char *)NULL;
+#if !defined (isspace)
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
+#endif
 
-  return (new_array);
-}
+#if !defined (isprint)
+#define isprint(c) (isletter(c) || digit(c) || ispunct(c))
+#endif
 
-/* Comparison routine for use with qsort() on arrays of strings. */
 int
-qsort_string_compare (s1, s2)
-     register char **s1, **s2;
+check_binary_file (sample, sample_len)
+     unsigned char *sample;
+     int sample_len;
 {
-  int result;
-
-  if ((result = **s1 - **s2) == 0)
-    result = strcmp (*s1, *s2);
-
-  return (result);
-}
+  register int i;
 
-/* Append LIST2 to LIST1.  Return the header of the list. */
-GENERIC_LIST *
-list_append (head, tail)
-     GENERIC_LIST *head, *tail;
-{
-  register GENERIC_LIST *t_head = head;
+  for (i = 0; i < sample_len; i++)
+    {
+      if (sample[i] == '\n')
+       return (0);
 
-  if (!t_head)
-    return (tail);
+      if (isspace (sample[i]) == 0 && isprint (sample[i]) == 0)
+       return (1);
+    }
 
-  while (t_head->next)
-    t_head = t_head->next;
-  t_head->next = tail;
-  return (head);
+  return (0);
 }
 
-/* Some random string stuff. */
-
-/* Remove all leading whitespace from STRING.  This includes
-   newlines.  STRING should be terminated with a zero. */
-void
-strip_leading (string)
-     char *string;
-{
-  char *start = string;
-
-  while (*string && (whitespace (*string) || *string == '\n'))
-    string++;
-
-  if (string != start)
-    {
-      int len = strlen (string);
-      FASTCOPY (string, start, len);
-      start[len] = '\0';
-    }
-}
+/* **************************************************************** */
+/*                                                                 */
+/*                 Functions to manipulate pathnames               */
+/*                                                                 */
+/* **************************************************************** */
 
-/* Remove all trailing whitespace from STRING.  This includes
-   newlines.  If NEWLINES_ONLY is non-zero, only trailing newlines
-   are removed.  STRING should be terminated with a zero. */
-void
-strip_trailing (string, newlines_only)
-     char *string;
-     int newlines_only;
+/* Return 1 if PATH corresponds to a directory. */
+static int
+canon_stat (path)
+     char *path;
 {
-  int len = strlen (string) - 1;
+  int l;
+  char *s;
+  struct stat sb;
 
-  while (len >= 0)
-    {
-      if ((newlines_only && string[len] == '\n') ||
-          (!newlines_only && whitespace (string[len])))
-        len--;
-      else
-        break;
-    }
-  string[len + 1] = '\0';
+  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
@@ -618,8 +615,21 @@ canonicalize_pathname (path)
        {
          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] == '\\')
@@ -653,8 +663,27 @@ canonicalize_pathname (path)
          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;
            }
@@ -679,16 +708,16 @@ make_absolute (string, dot_path)
 {
   char *result;
   int result_len;
-  
+
   if (!dot_path || *string == '/')
     result = savestring (string);
   else
     {
       if (dot_path && dot_path[0])
        {
-         result = xmalloc (2 + strlen (dot_path) + strlen (string));
+         result_len = strlen (dot_path);
+         result = xmalloc (2 + result_len + strlen (string));
          strcpy (result, dot_path);
-         result_len = strlen (result);
          if (result[result_len - 1] != '/')
            {
              result[result_len++] = '/';
@@ -721,10 +750,8 @@ absolute_pathname (string)
 
   if (*string++ == '.')
     {
-      if (!*string || *string == '/')
-       return (1);
-
-      if (*string == '.' && (string[1] == '\0' || string[1] == '/'))
+      if (!*string || *string == '/' ||
+          (*string == '.' && (string[1] == '\0' || string[1] == '/')))
        return (1);
     }
   return (0);
@@ -752,10 +779,7 @@ base_pathname (string)
     return (string);
 
   p = (char *)strrchr (string, '/');
-  if (p)
-    return (++p);
-  else
-    return (string);
+  return (p ? ++p : string);
 }
 
 /* Return the full pathname of FILE.  Easy.  Filenames that begin
@@ -767,342 +791,39 @@ full_pathname (file)
      char *file;
 {
   char *disposer;
+  char *current_dir;
+  int dlen;
 
-  if (*file == '~')
-    file = tilde_expand (file);
-  else
-    file = savestring (file);
+  file = (*file == '~') ? bash_tilde_expand (file) : savestring (file);
 
   if ((*file == '/') && absolute_pathname (file))
     return (file);
 
   disposer = file;
 
-  {
-    char *current_dir = xmalloc (2 + MAXPATHLEN + strlen (file));
-    int dlen;
-    if (getwd (current_dir) == 0)
-      {
-       report_error (current_dir);
-       free (current_dir);
-       return ((char *)NULL);
-      }
-    dlen = strlen (current_dir);
-    current_dir[dlen++] = '/';
-
-    /* Turn /foo/./bar into /foo/bar. */
-    if (file[0] == '.' && file[1] == '/')
-      file += 2;
-
-    strcpy (current_dir + dlen, file);
-    free (disposer);
-    return (current_dir);
-  }
-}
-
-#if !defined (HAVE_STRCASECMP)
-
-#if !defined (to_upper)
-#  define to_upper(c) (islower(c) ? toupper(c) : (c))
-#endif /* to_upper */
-
-/* Compare at most COUNT characters from string1 to string2.  Case
-   doesn't matter. */
-int
-strnicmp (string1, string2, count)
-     char *string1, *string2;
-     int count;
-{
-  register char ch1, ch2;
-
-  while (count)
-    {
-      ch1 = *string1++;
-      ch2 = *string2++;
-      if (to_upper(ch1) == to_upper(ch2))
-       count--;
-      else
-       break;
-    }
-  return (count);
-}
-
-/* strcmp (), but caseless. */
-int
-stricmp (string1, string2)
-     char *string1, *string2;
-{
-  register char ch1, ch2;
-
-  while (*string1 && *string2)
-    {
-      ch1 = *string1++;
-      ch2 = *string2++;
-      if (to_upper(ch1) != to_upper(ch2))
-       return (1);
-    }
-  return (*string1 - *string2);
-}
-#endif /* !HAVE_STRCASECMP */
-
-/* Determine if s2 occurs in s1.  If so, return a pointer to the
-   match in s1.  The compare is case insensitive. */
-char *
-strindex (s1, s2)
-     char *s1, *s2;
-{
-  register int i, l = strlen (s2);
-  register int len = strlen (s1);
-
-  for (i = 0; (len - i) >= l; i++)
-    if (strnicmp (s1 + i, s2, l) == 0)
-      return (s1 + i);
-  return ((char *)NULL);
-}
-
-/* Set the environment variables $LINES and $COLUMNS in response to
-   a window size change. */
-void
-set_lines_and_columns (lines, cols)
-     int lines, cols;
-{
-  char *val;
-
-  val = itos (lines);
-  bind_variable ("LINES", val);
-  free (val);
-
-  val = itos (cols);
-  bind_variable ("COLUMNS", val);
-  free (val);
-}
-
-/* A wrapper for bcopy that can be prototyped in general.h */
-void
-xbcopy (s, d, n)
-     char *s, *d;
-     int n;
-{
-  FASTCOPY (s, d, n);
-}
-
-/* Return a string corresponding to the error number E.  From
-   the ANSI C spec. */
-#if defined (strerror)
-#  undef strerror
-#endif
-
-#if !defined (HAVE_STRERROR)
-char *
-strerror (e)
-     int e;
-{
-  extern int sys_nerr;
-  extern char *sys_errlist[];
-  static char emsg[40];
-
-  if (e > 0 && e < sys_nerr)
-    return (sys_errlist[e]);
-  else
-    {
-      sprintf (emsg, "Unknown error %d", e);
-      return (&emsg[0]);
-    }
-}
-#endif /* HAVE_STRERROR */
-
-#if (defined (USG) && !defined (HAVE_TIMEVAL)) || defined (Minix)
-#  define TIMEVAL_MISSING
-#endif
-
-#if !defined (TIMEVAL_MISSING) || defined (HAVE_RESOURCE)
-/* Print the contents of a struct timeval * in a standard way. */
-void
-print_timeval (tvp)
-     struct timeval *tvp;
-{
-  int minutes, seconds_fraction;
-  long seconds;
-
-  seconds = tvp->tv_sec;
-
-  seconds_fraction = tvp->tv_usec % 1000000;
-  seconds_fraction = (seconds_fraction * 100) / 1000000;
-
-  minutes = seconds / 60;
-  seconds %= 60;
-
-  printf ("%0dm%0ld.%02ds",  minutes, seconds, seconds_fraction);
-}
-#endif /* !TIMEVAL_MISSING || HAVE_RESOURCE */
-
-/* Print the time defined by a time_t (returned by the `times' and `time'
-   system calls) in a standard way.  This is scaled in terms of HZ, which
-   is what is returned by the `times' call. */
-
-#if !defined (BrainDeath)
-#  if !defined (HZ)
-#    if defined (USG)
-#      define HZ 100           /* From my Sys V.3.2 manual for times(2) */
-#    else
-#      define HZ 60            /* HZ is always 60 on BSD systems */
-#    endif /* USG */
-#  endif /* HZ */
-
-void
-print_time_in_hz (t)
-  time_t t;
-{
-  int minutes, seconds_fraction;
-  long seconds;
-
-  seconds_fraction = t % HZ;
-  seconds_fraction = (seconds_fraction * 100) / HZ;
-
-  seconds = t / HZ;
-
-  minutes = seconds / 60;
-  seconds %= 60;
-
-  printf ("%0dm%0ld.%02ds",  minutes, seconds, seconds_fraction);
-}
-#endif /* BrainDeath */
-
-#if !defined (HAVE_DUP2)
-/* Replacement for dup2 (), for those systems which either don't have it,
-   or supply one with broken behaviour. */
-int
-dup2 (fd1, fd2)
-     int fd1, fd2;
-{
-  extern int getdtablesize ();
-  int saved_errno, r;
-
-  /* If FD1 is not a valid file descriptor, then return immediately with
-     an error. */
-  if (fcntl (fd1, F_GETFL, 0) == -1)
-    return (-1);
-
-  if (fd2 < 0 || fd2 >= getdtablesize ())
+  current_dir = xmalloc (2 + PATH_MAX + strlen (file));
+  if (getcwd (current_dir, PATH_MAX) == 0)
     {
-      errno = EBADF;
-      return (-1);
+      sys_error (bash_getcwd_errstr);
+      free (disposer);
+      free (current_dir);
+      return ((char *)NULL);
     }
+  dlen = strlen (current_dir);
+  current_dir[dlen++] = '/';
 
-  if (fd1 == fd2)
-    return (0);
-
-  saved_errno = errno;
-
-  (void) close (fd2);
-  r = fcntl (fd1, F_DUPFD, fd2);
-
-  if (r >= 0)
-    errno = saved_errno;
-  else
-    if (errno == EINVAL)
-      errno = EBADF;
-
-  /* Force the new file descriptor to remain open across exec () calls. */
-  SET_OPEN_ON_EXEC (fd2);
-  return (r);
-}
-#endif /* !HAVE_DUP2 */
-
-/*
- * Return the total number of available file descriptors.
- *
- * On some systems, like 4.2BSD and its descendents, there is a system call
- * that returns the size of the descriptor table: getdtablesize().  There are
- * lots of ways to emulate this on non-BSD systems.
- *
- * On System V.3, this can be obtained via a call to ulimit:
- *     return (ulimit(4, 0L));
- *
- * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
- * (this is what we assume below), so we can simply use it:
- *     return (NOFILE);
- *
- * On POSIX systems, there are specific functions for retrieving various
- * configuration parameters:
- *     return (sysconf(_SC_OPEN_MAX));
- *
- */
-
-#if !defined (USG) && !defined (HPUX) && !defined (HAVE_GETDTABLESIZE)
-#  define HAVE_GETDTABLESIZE
-#endif /* !USG && !HPUX && !HAVE_GETDTABLESIZE */
-
-#if defined (hppa) && (defined (hpux_8) || defined (hpux_9))
-#  undef HAVE_GETDTABLESIZE
-#endif /* hppa && hpux_8 */
-
-#if !defined (HAVE_GETDTABLESIZE)
-int
-getdtablesize ()
-{
-#  if defined (_POSIX_VERSION) && defined (_SC_OPEN_MAX)
-  return (sysconf(_SC_OPEN_MAX));      /* Posix systems use sysconf */
-#  else /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
-#    if defined (USGr3)
-  return (ulimit (4, 0L));     /* System V.3 systems use ulimit(4, 0L) */
-#    else /* !USGr3 */
-#      if defined (NOFILE)     /* Other systems use NOFILE */
-  return (NOFILE);
-#      else /* !NOFILE */
-  return (20);                 /* XXX - traditional value is 20 */
-#      endif /* !NOFILE */
-#    endif /* !USGr3 */
-#  endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
-}
-#endif /* !HAVE_GETDTABLESIZE */
-
-#if defined (USG)
-
-#if !defined (HAVE_BCOPY)
-bcopy (s,d,n) char *d,*s; { FASTCOPY (s, d, n); }
-bzero (s,n) char *s; int n; { memset(s, '\0', n); }
-#endif /* !HAVE_BCOPY */
-
-#if !defined (HAVE_GETHOSTNAME)
-#include <sys/utsname.h>
-int
-gethostname (name, namelen)
-     char *name;
-     int namelen;
-{
-  int i;
-  struct utsname ut;
-
-  --namelen;
+  /* Turn /foo/./bar into /foo/bar. */
+  if (file[0] == '.' && file[1] == '/')
+    file += 2;
 
-  uname (&ut);
-  i = strlen (ut.nodename) + 1;
-  strncpy (name, ut.nodename, i < namelen ? i : namelen);
-  name[namelen] = '\0';
-  return (0);
+  strcpy (current_dir + dlen, file);
+  free (disposer);
+  return (current_dir);
 }
-#endif /* !HAVE_GETHOSTNAME */
-#endif /* USG */
-
-#if !defined (HAVE_GETWD)
-char *
-getwd (string)
-     char *string;
-{
-  extern char *getcwd ();
-  char *result;
-
-  result = getcwd (string, MAXPATHLEN);
-  if (result == NULL)
-    strcpy (string, "getwd: cannot access parent directories");
-  return (result);
-}
-#endif /* !HAVE_GETWD */
 
 /* A slightly related function.  Get the prettiest name of this
    directory possible. */
-static char tdir[MAXPATHLEN];
+static char tdir[PATH_MAX];
 
 /* Return a pretty pathname.  If the first part of the pathname is
    the same as $HOME, then replace that with `~'.  */
@@ -1110,9 +831,11 @@ char *
 polite_directory_format (name)
      char *name;
 {
-  char *home = get_string_value ("HOME");
-  int l = home ? strlen (home) : 0;
+  char *home;
+  int l;
 
+  home = get_string_value ("HOME");
+  l = home ? strlen (home) : 0;
   if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
     {
       strcpy (tdir + 1, name + l);
@@ -1123,64 +846,57 @@ polite_directory_format (name)
     return (name);
 }
 
-#if defined (NO_READ_RESTART_ON_SIGNAL)
-static char localbuf[128];
-static int local_index = 0, local_bufused = 0;
-
-/* Posix and USG systems do not guarantee to restart read () if it is
-   interrupted by a signal.  We do the read ourselves, and restart it
-   if it returns EINTR. */
-int
-getc_with_restart (stream)
-     FILE *stream;
+/* 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. */
+char *
+extract_colon_unit (string, p_index)
+     char *string;
+     int *p_index;
 {
-  /* Try local buffering to reduce the number of read(2) calls. */
-  if (local_index == local_bufused || local_bufused == 0)
-    {
-      while (1)
-       {
-         local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
-         if (local_bufused > 0)
-           break;
-         else if (local_bufused == 0 || errno != EINTR)
-           {
-             local_index = 0;
-             return EOF;
-           }
-       }
-      local_index = 0;
-    }
-  return (localbuf[local_index++]);
-}
+  int i, start, len;
+  char *value;
 
-int
-ungetc_with_restart (c, fp)
-     int c;
-     FILE *fp;
-{
-  if (local_index == 0 || local_bufused == 0 || c == EOF)
-    return EOF;
-  return (localbuf[--local_index] = c);
-}
+  if (string == 0)
+    return (string);
 
-#endif /* NO_READ_RESTART_ON_SIGNAL */
+  len = strlen (string);
+  if (*p_index >= len)
+    return ((char *)NULL);
 
-#if defined (USG) || defined (AIX) || (defined (_POSIX_VERSION) && defined (Ultrix))
-/* USG and strict POSIX systems do not have killpg ().  But we use it in
-   jobs.c, nojobs.c and some of the builtins.  This can also be redefined
-   as a macro if necessary. */
-#if !defined (_POSIX_VERSION)
-#  define pid_t int
-#endif /* _POSIX_VERSION */
+  i = *p_index;
 
-int
-killpg (pgrp, sig)
-     pid_t pgrp;
-     int sig;
-{
-  return (kill (-pgrp, sig));
+  /* Each call to this routine leaves the index pointing at a colon if
+     there is more to the path.  If I is > 0, then increment past the
+     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
+     are handled OK by the `else' part of the if statement; an empty
+     string is returned in that case. */
+  if (i && string[i] == ':')
+    i++;
+
+  for (start = i; string[i] && string[i] != ':'; i++)
+    ;
+
+  *p_index = i;
+
+  if (i == start)
+    {
+      if (string[i])
+       (*p_index)++;
+      /* Return "" in the case of a trailing `:'. */
+      value = xmalloc (1);
+      value[0] = '\0';
+    }
+  else
+    {
+      len = i - start;
+      value = xmalloc (1 + len);
+      strncpy (value, string + start, len);
+      value [len] = '\0';
+    }
+
+  return (value);
 }
-#endif /* USG  || AIX || (_POSIX_VERSION && Ultrix) */
 
 /* **************************************************************** */
 /*                                                                 */
@@ -1192,12 +908,13 @@ killpg (pgrp, sig)
    is a special shell expansion.  This function is installed as the
    tilde_expansion_failure_hook.  It knows how to expand ~- and ~+. */
 static char *
-bash_tilde_expand (text)
+bash_tilde_expansion_failure_hook (text)
      char *text;
 {
-  char *result = (char *)NULL;
+  char *result;
 
-  if (!text[1])
+  result = (char *)NULL;
+  if (text[1] == '\0')
     {
       if (*text == '+')
         result = get_string_value ("PWD");
@@ -1205,10 +922,7 @@ bash_tilde_expand (text)
         result = get_string_value ("OLDPWD");
     }
 
-  if (result)
-    result = savestring (result);
-
-  return (result);
+  return (result ? savestring (result) : (char *)NULL);
 }
 
 /* Initialize the tilde expander.  In Bash, we handle `~-' and `~+', as
@@ -1220,7 +934,7 @@ tilde_initialize ()
   static int times_called = 0;
 
   /* Tell the tilde expander that we want a crack if it fails. */
-  tilde_expansion_failure_hook = (CPFunction *)bash_tilde_expand;
+  tilde_expansion_failure_hook = (CPFunction *)bash_tilde_expansion_failure_hook;
 
   /* Tell the tilde expander about special strings which start a tilde
      expansion, and the special strings that end one.  Only do this once.
@@ -1240,34 +954,16 @@ tilde_initialize ()
   times_called++;
 }
 
-#if defined (_POSIX_VERSION)
-
-#if !defined (SA_INTERRUPT)
-#  define SA_INTERRUPT 0
-#endif
-
-#if !defined (SA_RESTART)
-#  define SA_RESTART 0
-#endif
-
-SigHandler *
-set_signal_handler (sig, handler)
-     int sig;
-     SigHandler *handler;
+char *
+bash_tilde_expand (s)
+     char *s;
 {
-  struct sigaction act, oact;
+  int old_immed;
+  char *ret;
 
-  act.sa_handler = handler;
-  act.sa_flags = 0;
-#if 0
-  if (sig == SIGALRM)
-    act.sa_flags |= SA_INTERRUPT;      /* XXX */
-  else
-    act.sa_flags |= SA_RESTART;                /* XXX */
-#endif
-  sigemptyset (&act.sa_mask);
-  sigemptyset (&oact.sa_mask);
-  sigaction (sig, &act, &oact);
-  return (oact.sa_handler);
+  old_immed = interrupt_immediately;
+  interrupt_immediately = 1;
+  ret = tilde_expand (s);
+  interrupt_immediately = old_immed;
+  return (ret);
 }
-#endif /* _POSIX_VERSION */