Imported from ../bash-2.01.tar.gz.
[platform/upstream/bash.git] / lib / readline / tilde.c
index 69f5768..1d38d9d 100644 (file)
 #  include <config.h>
 #endif
 
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
 #if defined (HAVE_STRING_H)
 #  include <string.h>
 #else /* !HAVE_STRING_H */
 
 #include "tilde.h"
 
+#ifdef SHELL
+#include "shell.h"
+#endif
+
 #if !defined (HAVE_GETPW_DECLS)
 extern struct passwd *getpwuid (), *getpwnam ();
 #endif /* !HAVE_GETPW_DECLS */
@@ -78,6 +86,12 @@ static char *default_prefixes[] =
 static char *default_suffixes[] =
   { " ", "\n", (char *)NULL };
 
+/* If non-null, this contains the address of a function that the application
+   wants called before trying the standard tilde expansions.  The function
+   is called with the text sans tilde, and returns a malloc()'ed string
+   which is the expansion, or a NULL pointer if the expansion fails. */
+CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL;
+
 /* If non-null, this contains the address of a function to call if the
    standard meaning for expanding a tilde fails.  The function is called
    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
@@ -108,7 +122,7 @@ tilde_find_prefix (string, len)
   string_len = strlen (string);
   *len = 0;
 
-  if (!*string || *string == '~')
+  if (*string == '\0' || *string == '~')
     return (0);
 
   if (prefixes)
@@ -135,13 +149,14 @@ tilde_find_suffix (string)
      char *string;
 {
   register int i, j, string_len;
-  register char **suffixes = tilde_additional_suffixes;
+  register char **suffixes;
 
+  suffixes = tilde_additional_suffixes;
   string_len = strlen (string);
 
   for (i = 0; i < string_len; i++)
     {
-      if (string[i] == '/' || !string[i])
+      if (string[i] == '/' /* || !string[i] */)
        break;
 
       for (j = 0; suffixes && suffixes[j]; j++)
@@ -153,16 +168,28 @@ tilde_find_suffix (string)
   return (i);
 }
 
+#if !defined (SHELL)
+static char *
+get_string_value (varname)
+     char *varname;
+{
+  return ((char *)getenv (varname));
+}
+#endif
+
 /* Return a new string which is the result of tilde expanding STRING. */
 char *
 tilde_expand (string)
      char *string;
 {
-  char *result, *tilde_expand_word ();
+  char *result;
   int result_size, result_index;
 
-  result_size = result_index = 0;
-  result = (char *)NULL;
+  result_index = result_size = 0;
+  if (result = strchr (string, '~'))
+    result = xmalloc (result_size = (strlen (string) + 16));
+  else
+    result = xmalloc (result_size = strlen (string));
 
   /* Scan through STRING expanding tildes as we come to them. */
   while (1)
@@ -215,97 +242,143 @@ tilde_expand (string)
   return (result);
 }
 
+/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
+   non-null, the index of the end of the prefix into FNAME is returned in
+   the location it points to. */
+static char *
+isolate_tilde_prefix (fname, lenp)
+     char *fname;
+     int *lenp;
+{
+  char *ret;
+  int i;
+
+  ret = xmalloc (strlen (fname));
+  for (i = 1; fname[i] && fname[i] != '/'; i++)
+    ret[i - 1] = fname[i];
+  ret[i - 1] = '\0';
+  if (lenp)
+    *lenp = i;
+  return ret;
+}
+
+/* Return a string that is PREFIX concatenated with SUFFIX starting at
+   SUFFIND. */
+static char *
+glue_prefix_and_suffix (prefix, suffix, suffind)
+     char *prefix, *suffix;
+     int suffind;
+{
+  char *ret;
+  int plen, slen;
+
+  plen = (prefix && *prefix) ? strlen (prefix) : 0;
+  slen = strlen (suffix + suffind);
+  ret = xmalloc (plen + slen + 1);
+  if (prefix && *prefix)
+    strcpy (ret, prefix);
+  strcpy (ret + plen, suffix + suffind);
+  return ret;
+}
+
+static char *
+get_home_dir ()
+{
+  char *home_dir;
+
+#ifdef SHELL
+  home_dir = (char *)NULL;
+  if (current_user.home_dir == 0)
+    get_current_user_info ();
+  home_dir = current_user.home_dir;
+#else
+  struct passwd *entry;
+
+  home_dir = (char *)NULL;
+  entry = getpwuid (getuid ());
+  if (entry)
+    home_dir = entry->pw_dir;
+#endif
+  return (home_dir);
+}
+
 /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
-   tilde.  If there is no expansion, call tilde_expansion_failure_hook. */
+   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
+   This always returns a newly-allocated string, never static storage. */
 char *
 tilde_expand_word (filename)
      char *filename;
 {
-  char *dirname;
-  char *temp_name;
+  char *dirname, *expansion, *username;
+  int user_len;
+  struct passwd *user_entry;
 
-  if (filename == (char *)0)
+  if (filename == 0)
     return ((char *)NULL);
 
-  dirname = savestring (filename);
-
-  if (*dirname != '~')
-    return (dirname);
+  if (*filename != '~')
+    return (savestring (filename));
 
-  if (!dirname[1] || dirname[1] == '/')
+  /* A leading `~/' or a bare `~' is *always* translated to the value of
+     $HOME or the home directory of the current user, regardless of any
+     preexpansion hook. */
+  if (filename[1] == '\0' || filename[1] == '/')
     {
-      /* Prepend $HOME to the rest of the string. */
-      char *temp_home = (char *)getenv ("HOME");
-      int home_len;
+      /* Prefix $HOME to the rest of the string. */
+      expansion = get_string_value ("HOME");
 
       /* If there is no HOME variable, look up the directory in
         the password database. */
-      if (!temp_home)
-       {
-         struct passwd *entry;
-
-         entry = getpwuid (getuid ());
-         if (entry)
-           temp_home = entry->pw_dir;
-       }
+      if (expansion == 0)
+       expansion = get_home_dir ();
 
-      home_len = temp_home ? strlen (temp_home) : 0;
-      temp_name = xmalloc (1 + strlen (dirname + 1) + home_len);
-                            
-      if (temp_home)
-       strcpy (temp_name, temp_home);
-      strcpy (temp_name + home_len, dirname + 1);
-      free (dirname);
-      dirname = temp_name;
+      return (glue_prefix_and_suffix (expansion, filename, 1));
     }
-  else
-    {
-      char *username;
-      struct passwd *user_entry;
-      int i, len;
 
-      username = xmalloc (strlen (dirname));
-      for (i = 1; dirname[i] && dirname[i] != '/'; i++)
-       username[i - 1] = dirname[i];
-      username[i - 1] = '\0';
+  username = isolate_tilde_prefix (filename, &user_len);
 
-      if ((user_entry = getpwnam (username)) == (struct passwd *)0)
+  if (tilde_expansion_preexpansion_hook)
+    {
+      expansion = (*tilde_expansion_preexpansion_hook) (username);
+      if (expansion)
        {
-         /* If the calling program has a special syntax for
-            expanding tildes, and we couldn't find a standard
-            expansion, then let them try. */
-         if (tilde_expansion_failure_hook)
-           {
-             char *expansion;
-
-             expansion = (*tilde_expansion_failure_hook) (username);
-
-             if (expansion)
-               {
-                 len = strlen (expansion);
-                 temp_name = xmalloc (1 + len + strlen (dirname + i));
-                 strcpy (temp_name, expansion);
-                 strcpy (temp_name + len, dirname + i);
-                 free (expansion);
-                 free (dirname);
-                 dirname = temp_name;
-               }
-           }
-         /* We shouldn't report errors. */
+         dirname = glue_prefix_and_suffix (expansion, filename, user_len);
+         free (username);
+         free (expansion);
+         return (dirname);
        }
-      else
+    }
+
+  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
+     password database. */
+  dirname = (char *)NULL;
+  user_entry = getpwnam (username);
+  if (user_entry == 0)
+    {
+      /* If the calling program has a special syntax for expanding tildes,
+        and we couldn't find a standard expansion, then let them try. */
+      if (tilde_expansion_failure_hook)
        {
-         len = strlen (user_entry->pw_dir);
-         temp_name = xmalloc (1 + len + strlen (dirname + i));
-         strcpy (temp_name, user_entry->pw_dir);
-         strcpy (temp_name + len, dirname + i);
-         free (dirname);
-         dirname = temp_name;
+         expansion = (*tilde_expansion_failure_hook) (username);
+         if (expansion)
+           {
+             dirname = glue_prefix_and_suffix (expansion, filename, user_len);
+             free (expansion);
+           }
        }
-      endpwent ();
       free (username);
+      /* If we don't have a failure hook, or if the failure hook did not
+        expand the tilde, return a copy of what we were passed. */
+      if (dirname == 0)
+       dirname = savestring (filename);
+    }
+  else
+    {
+      free (username);
+      dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
     }
 
+  endpwent ();
   return (dirname);
 }