From 9449b0fce4145a56df9d43169d61e8b2c4e41b09 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 15 Jul 2009 11:39:04 +0100 Subject: [PATCH] guestfish: Add tilde expansion for paths (RHBZ#511372). This commit adds tilde expansion for local users in guestfish: > echo "~" ~ > echo ~ /home/rjones > echo ~foo ~foo > echo ~rjones/bar /home/rjones/bar > echo ~roo ~roo > echo ~root/foo /root/foo > echo ~root /root --- fish/Makefile.am | 1 + fish/fish.c | 14 ++++++- fish/fish.h | 3 ++ fish/tilde.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ guestfish.pod | 14 +++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 fish/tilde.c diff --git a/fish/Makefile.am b/fish/Makefile.am index dd79361..9ec0cf4 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -36,6 +36,7 @@ guestfish_SOURCES = \ rc_protocol.c \ rc_protocol.h \ reopen.c \ + tilde.c \ time.c BUILT_SOURCES = \ diff --git a/fish/fish.c b/fish/fish.c index 4042bbc..f5d538d 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -514,6 +514,7 @@ script (int prompt) char *argv[64]; int i, len; int global_exit_on_error = !prompt; + int tilde_candidate; if (prompt) printf (_("\n" @@ -587,6 +588,8 @@ script (int prompt) /* 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. */ @@ -647,6 +650,11 @@ script (int prompt) *(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'; @@ -659,7 +667,11 @@ script (int prompt) abort (); } - argv[i++] = p; + if (!tilde_candidate) + argv[i] = p; + else + argv[i] = try_tilde_expansion (p); + i++; p = pend; if (*p) diff --git a/fish/fish.h b/fish/fish.h index f911eed..59f2e10 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -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 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. */ diff --git a/fish/tilde.c b/fish/tilde.c new file mode 100644 index 0000000..13e6cd8 --- /dev/null +++ b/fish/tilde.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/guestfish.pod b/guestfish.pod index 4f8cf95..32a3090 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -328,6 +328,20 @@ to quote it, eg: 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, 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 -- 1.8.3.1