guestfish: Add tilde expansion for paths (RHBZ#511372).
authorRichard Jones <rjones@trick.home.annexia.org>
Wed, 15 Jul 2009 10:39:04 +0000 (11:39 +0100)
committerRichard Jones <rjones@trick.home.annexia.org>
Wed, 15 Jul 2009 10:39:04 +0000 (11:39 +0100)
This commit adds tilde expansion for local users in guestfish:

  ><fs> echo "~"
  ~
  ><fs> echo ~
  /home/rjones
  ><fs> echo ~foo
  ~foo
  ><fs> echo ~rjones/bar
  /home/rjones/bar
  ><fs> echo ~roo
  ~roo
  ><fs> echo ~root/foo
  /root/foo
  ><fs> echo ~root
  /root

fish/Makefile.am
fish/fish.c
fish/fish.h
fish/tilde.c [new file with mode: 0644]
guestfish.pod

index dd79361..9ec0cf4 100644 (file)
@@ -36,6 +36,7 @@ guestfish_SOURCES = \
        rc_protocol.c \
        rc_protocol.h \
        reopen.c \
        rc_protocol.c \
        rc_protocol.h \
        reopen.c \
+       tilde.c \
        time.c
 
 BUILT_SOURCES = \
        time.c
 
 BUILT_SOURCES = \
index 4042bbc..f5d538d 100644 (file)
@@ -514,6 +514,7 @@ script (int prompt)
   char *argv[64];
   int i, len;
   int global_exit_on_error = !prompt;
   char *argv[64];
   int i, len;
   int global_exit_on_error = !prompt;
+  int tilde_candidate;
 
   if (prompt)
     printf (_("\n"
 
   if (prompt)
     printf (_("\n"
@@ -587,6 +588,8 @@ script (int prompt)
 
     /* Get the parameters. */
     while (*p && i < sizeof argv / sizeof argv[0]) {
 
     /* Get the parameters. */
     while (*p && i < sizeof argv / sizeof argv[0]) {
+      tilde_candidate = 0;
+
       /* Parameters which start with quotes or pipes are treated
        * specially.  Bare parameters are delimited by whitespace.
        */
       /* Parameters which start with quotes or pipes are treated
        * specially.  Bare parameters are delimited by whitespace.
        */
@@ -647,6 +650,11 @@ script (int prompt)
        *(pend-1) = '\0';
        */
       } else if (*p != ' ' && *p != '\t') {
        *(pend-1) = '\0';
        */
       } else if (*p != ' ' && *p != '\t') {
+       /* If the first character is a ~ then note that this parameter
+        * is a candidate for ~username expansion.  NB this does not
+        * apply to quoted parameters.
+        */
+       tilde_candidate = *p == '~';
        len = strcspn (p, " \t");
        if (p[len]) {
          p[len] = '\0';
        len = strcspn (p, " \t");
        if (p[len]) {
          p[len] = '\0';
@@ -659,7 +667,11 @@ script (int prompt)
        abort ();
       }
 
        abort ();
       }
 
-      argv[i++] = p;
+      if (!tilde_candidate)
+       argv[i] = p;
+      else
+       argv[i] = try_tilde_expansion (p);
+      i++;
       p = pend;
 
       if (*p)
       p = pend;
 
       if (*p)
index f911eed..59f2e10 100644 (file)
@@ -88,6 +88,9 @@ extern int do_reopen (const char *cmd, int argc, char *argv[]);
 /* in time.c */
 extern int do_time (const char *cmd, int argc, char *argv[]);
 
 /* in time.c */
 extern int do_time (const char *cmd, int argc, char *argv[]);
 
+/* in tilde.c */
+extern char *try_tilde_expansion (char *path);
+
 /* This should just list all the built-in commands so they can
  * be added to the generated auto-completion code.
  */
 /* This should just list all the built-in commands so they can
  * be added to the generated auto-completion code.
  */
diff --git a/fish/tilde.c b/fish/tilde.c
new file mode 100644 (file)
index 0000000..13e6cd8
--- /dev/null
@@ -0,0 +1,121 @@
+/* guestfish - the filesystem interactive shell
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#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);
+
+/* This is called from the script loop if we find a candidate for
+ * ~username (tilde-expansion).
+ */
+char *
+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.
+   */
+  if (str[1] == '\0')          /* ~ */
+    return expand_home (NULL);
+  else if (str[1] == '/')      /* ~/... */
+    return expand_home (&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], "/");
+    rest = &str[1+len];
+
+    home = find_home_for_username (&str[1], len);
+
+    if (home) {
+      len = strlen (home) + strlen (rest);
+      str = malloc (len);
+      if (str == NULL) {
+       perror ("malloc");
+       exit (1);
+      }
+      strcpy (str, home);
+      strcat (str, rest);
+      return str;
+    }
+  }
+
+  /* No match, return the orignal string. */
+  return str;
+}
+
+/* Return $HOME + append string. */
+static char *
+expand_home (const char *append)
+{
+  const char *home;
+  int len;
+  char *str;
+
+  home = getenv ("HOME");
+  if (!home) home = "~";
+
+  len = strlen (home) + (append ? strlen (append) : 0);
+  str = malloc (len);
+  if (str == NULL) {
+    perror ("malloc");
+    exit (1);
+  }
+
+  strcpy (str, home);
+  if (append)
+    strcat (str, append);
+
+  return str;
+}
+
+/* Lookup username (of length ulen), return home directory if found,
+ * or NULL if not found.
+ */
+static const char *
+find_home_for_username (const char *username, int ulen)
+{
+  struct passwd *pw;
+
+  setpwent ();
+  while ((pw = getpwent ()) != NULL) {
+    if (strlen (pw->pw_name) == ulen &&
+       strncmp (username, pw->pw_name, ulen) == 0)
+      return pw->pw_dir;
+  }
+
+  return NULL;
+}
index 4f8cf95..32a3090 100644 (file)
@@ -328,6 +328,20 @@ to quote it, eg:
 
  echo "|"
 
 
  echo "|"
 
+=head1 HOME DIRECTORIES
+
+If a parameter starts with the character C<~> then the tilde may be
+expanded as a home directory path (either C<~> for the current user's
+home directory, or C<~user> for another user).
+
+Note that home directory expansion happens for users known I<on the
+host>, not in the guest filesystem.
+
+To use a literal argument which begins with a tilde, you have to quote
+it, eg:
+
+ echo "~"
+
 =head1 EXIT ON ERROR BEHAVIOUR
 
 By default, guestfish will ignore any errors when in interactive mode
 =head1 EXIT ON ERROR BEHAVIOUR
 
 By default, guestfish will ignore any errors when in interactive mode