Version 1.13.10.
[libguestfs.git] / fish / tilde.c
index 13e6cd8..ee87ce1 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <ctype.h>
 #include <assert.h>
 #include <pwd.h>
 #include <sys/types.h>
 
 #include "fish.h"
 
-static char *expand_home (const char *);
-static const char *find_home_for_username (const char *, int);
+static char *expand_home (char *orig, const char *append);
+static const char *find_home_for_username (const char *, size_t);
+static const char *find_home_for_current_user (void);
 
 /* This is called from the script loop if we find a candidate for
  * ~username (tilde-expansion).
@@ -40,32 +40,28 @@ try_tilde_expansion (char *str)
 {
   assert (str[0] == '~');
 
-  /* Expand current user's home directory.  By simple experimentation
-   * I found out that bash always uses $HOME.
-   */
+  /* Expand "~" to current user's home directory. */
   if (str[1] == '\0')          /* ~ */
-    return expand_home (NULL);
+    return expand_home (str, NULL);
   else if (str[1] == '/')      /* ~/... */
-    return expand_home (&str[1]);
+    return expand_home (str, &str[1]);
 
   /* Try expanding the part up to the following '\0' or '/' as a
    * username from the password file.
    */
   else {
-    int len;
     const char *home, *rest;
-
-    len = strcspn (&str[1], "/");
+    size_t len = strcspn (&str[1], "/");
     rest = &str[1+len];
 
     home = find_home_for_username (&str[1], len);
 
     if (home) {
-      len = strlen (home) + strlen (rest);
+      len = strlen (home) + strlen (rest) + 1;
       str = malloc (len);
       if (str == NULL) {
-       perror ("malloc");
-       exit (1);
+        perror ("malloc");
+        exit (EXIT_FAILURE);
       }
       strcpy (str, home);
       strcat (str, rest);
@@ -79,20 +75,27 @@ try_tilde_expansion (char *str)
 
 /* Return $HOME + append string. */
 static char *
-expand_home (const char *append)
+expand_home (char *orig, const char *append)
 {
   const char *home;
   int len;
   char *str;
 
   home = getenv ("HOME");
-  if (!home) home = "~";
+  if (!home) {
+    /* $HOME not set, bash can look up the current user in the
+     * password file and find their home that way.  (RHBZ#617440).
+     */
+    home = find_home_for_current_user ();
+    if (!home)
+      return orig;
+  }
 
-  len = strlen (home) + (append ? strlen (append) : 0);
+  len = strlen (home) + (append ? strlen (append) : 0) + 1;
   str = malloc (len);
   if (str == NULL) {
     perror ("malloc");
-    exit (1);
+    exit (EXIT_FAILURE);
   }
 
   strcpy (str, home);
@@ -106,14 +109,29 @@ expand_home (const char *append)
  * or NULL if not found.
  */
 static const char *
-find_home_for_username (const char *username, int ulen)
+find_home_for_username (const char *username, size_t ulen)
 {
   struct passwd *pw;
 
   setpwent ();
   while ((pw = getpwent ()) != NULL) {
     if (strlen (pw->pw_name) == ulen &&
-       strncmp (username, pw->pw_name, ulen) == 0)
+        STREQLEN (username, pw->pw_name, ulen))
+      return pw->pw_dir;
+  }
+
+  return NULL;
+}
+
+static const char *
+find_home_for_current_user (void)
+{
+  struct passwd *pw;
+  uid_t euid = geteuid ();
+
+  setpwent ();
+  while ((pw = getpwent ()) != NULL) {
+    if (pw->pw_uid == euid)
       return pw->pw_dir;
   }