mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Add tab-completion of guest filenames (currently disabled).
This commit is contained in:
@@ -21,6 +21,7 @@ guestfish_SOURCES = \
|
||||
alloc.c \
|
||||
cmds.c \
|
||||
completion.c \
|
||||
destpaths.c \
|
||||
echo.c \
|
||||
edit.c \
|
||||
fish.c \
|
||||
|
||||
@@ -191,6 +191,8 @@ generator (const char *text, int state)
|
||||
len = strlen (text);
|
||||
}
|
||||
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
while ((name = commands[index]) != NULL) {
|
||||
index++;
|
||||
if (strncasecmp (name, text, len) == 0)
|
||||
@@ -207,8 +209,12 @@ char **do_completion (const char *text, int start, int end)
|
||||
char **matches = NULL;
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
rl_completion_append_character = ' ';
|
||||
|
||||
if (start == 0)
|
||||
matches = rl_completion_matches (text, generator);
|
||||
else if (complete_dest_paths)
|
||||
matches = rl_completion_matches (text, complete_dest_paths_generator);
|
||||
#endif
|
||||
|
||||
return matches;
|
||||
|
||||
173
fish/destpaths.c
Normal file
173
fish/destpaths.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/* 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>
|
||||
|
||||
#define _GNU_SOURCE // for strndup, asprintf
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
#include <readline/readline.h>
|
||||
#endif
|
||||
|
||||
#include <guestfs.h>
|
||||
|
||||
#include "fish.h"
|
||||
|
||||
/* Readline completion for paths on the guest filesystem, also for
|
||||
* devices and LVM names.
|
||||
*/
|
||||
|
||||
int complete_dest_paths = 0; /* SEE NOTE */
|
||||
|
||||
/* NOTE: This is currently disabled by default (with no way to
|
||||
* enable it). That's because it's not particularly natural.
|
||||
*
|
||||
* Also there is a quite serious performance problem. When listing
|
||||
* even moderately long directories, this takes many seconds. The
|
||||
* reason is because it calls guestfs_is_dir on each directory
|
||||
* entry, thus lots of round trips to the server. We could have
|
||||
* a "readdir and stat each entry" call to ease this.
|
||||
*/
|
||||
|
||||
char *
|
||||
complete_dest_paths_generator (const char *text, int state)
|
||||
{
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
|
||||
static int len, index;
|
||||
static char **words = NULL;
|
||||
static int nr_words = 0;
|
||||
char *word;
|
||||
guestfs_error_handler_cb old_error_cb;
|
||||
void *old_error_cb_data;
|
||||
|
||||
/* Temporarily replace the error handler so that messages don't
|
||||
* get printed to stderr while we are issuing commands.
|
||||
*/
|
||||
#define SAVE_ERROR_CB \
|
||||
old_error_cb = guestfs_get_error_handler (g, &old_error_cb_data); \
|
||||
guestfs_set_error_handler (g, NULL, NULL);
|
||||
|
||||
/* Restore error handler. */
|
||||
#define RESTORE_ERROR_CB \
|
||||
guestfs_set_error_handler (g, old_error_cb, old_error_cb_data);
|
||||
|
||||
if (!state) {
|
||||
char **strs;
|
||||
int i, n;
|
||||
|
||||
len = strlen (text);
|
||||
index = 0;
|
||||
|
||||
if (words) {
|
||||
/* NB. 'words' array is NOT NULL-terminated. */
|
||||
for (i = 0; i < nr_words; ++i)
|
||||
free (words[i]);
|
||||
free (words);
|
||||
}
|
||||
|
||||
words = NULL;
|
||||
nr_words = 0;
|
||||
|
||||
SAVE_ERROR_CB
|
||||
|
||||
#define APPEND_STRS_AND_FREE \
|
||||
if (strs) { \
|
||||
n = count_strings (strs); \
|
||||
words = realloc (words, sizeof (char *) * (nr_words + n)); \
|
||||
for (i = 0; i < n; ++i) \
|
||||
words[nr_words++] = strs[i]; \
|
||||
free (strs); \
|
||||
}
|
||||
|
||||
/* Is it a device? */
|
||||
if (len < 5 || strncmp (text, "/dev/", 5) == 0) {
|
||||
/* Get a list of everything that can possibly begin with /dev/ */
|
||||
strs = guestfs_list_devices (g);
|
||||
APPEND_STRS_AND_FREE
|
||||
|
||||
strs = guestfs_list_partitions (g);
|
||||
APPEND_STRS_AND_FREE
|
||||
|
||||
strs = guestfs_lvs (g);
|
||||
APPEND_STRS_AND_FREE
|
||||
}
|
||||
|
||||
if (len < 1 || text[0] == '/') {
|
||||
/* If we've got a partial path already, we need to list everything
|
||||
* in that directory, otherwise list everything in /
|
||||
*/
|
||||
char *p, *dir;
|
||||
|
||||
p = strrchr (text, '/');
|
||||
dir = p && p > text ? strndup (text, p - text) : strdup ("/");
|
||||
|
||||
strs = guestfs_ls (g, dir);
|
||||
|
||||
/* Prepend directory to names. */
|
||||
if (strs) {
|
||||
for (i = 0; strs[i]; ++i) {
|
||||
p = NULL;
|
||||
if (strcmp (dir, "/") == 0)
|
||||
asprintf (&p, "/%s", strs[i]);
|
||||
else
|
||||
asprintf (&p, "%s/%s", dir, strs[i]);
|
||||
free (strs[i]);
|
||||
strs[i] = p;
|
||||
}
|
||||
}
|
||||
|
||||
free (dir);
|
||||
APPEND_STRS_AND_FREE
|
||||
}
|
||||
|
||||
/* else ... In theory we could complete other things here such as VG
|
||||
* names. At the moment we don't do that.
|
||||
*/
|
||||
|
||||
RESTORE_ERROR_CB
|
||||
}
|
||||
|
||||
/* This inhibits ordinary (local filename) completion. */
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
/* Complete the string. */
|
||||
while (index < nr_words) {
|
||||
word = words[index];
|
||||
index++;
|
||||
if (strncasecmp (word, text, len) == 0) {
|
||||
/* Is it a directory? */
|
||||
if (strncmp (word, "/dev/", 5) != 0) {
|
||||
SAVE_ERROR_CB
|
||||
if (guestfs_is_dir (g, word) > 0)
|
||||
rl_completion_append_character = '/';
|
||||
RESTORE_ERROR_CB
|
||||
}
|
||||
|
||||
return strdup (word);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBREADLINE */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
16
fish/fish.c
16
fish/fish.c
@@ -102,6 +102,7 @@ usage (void)
|
||||
" -h|--cmd-help List available commands\n"
|
||||
" -h|--cmd-help cmd Display detailed help on 'cmd'\n"
|
||||
" -a|--add image Add image\n"
|
||||
" -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
|
||||
" -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
|
||||
" -n|--no-sync Don't autosync\n"
|
||||
" -r|--ro Mount read-only\n"
|
||||
@@ -119,6 +120,7 @@ main (int argc, char *argv[])
|
||||
{ "cmd-help", 2, 0, 'h' },
|
||||
{ "help", 0, 0, '?' },
|
||||
{ "mount", 1, 0, 'm' },
|
||||
{ "no-dest-paths", 0, 0, 'D' },
|
||||
{ "no-sync", 0, 0, 'n' },
|
||||
{ "ro", 0, 0, 'r' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
@@ -178,6 +180,10 @@ main (int argc, char *argv[])
|
||||
drvs = drv;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
complete_dest_paths = 0;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
if (optarg)
|
||||
display_command (optarg);
|
||||
@@ -694,6 +700,16 @@ free_strings (char **argv)
|
||||
free (argv);
|
||||
}
|
||||
|
||||
int
|
||||
count_strings (char * const * const argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
for (c = 0; argv[c]; ++c)
|
||||
;
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
print_strings (char * const * const argv)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ extern void pod2text (const char *heading, const char *body);
|
||||
extern void list_builtin_commands (void);
|
||||
extern void display_builtin_command (const char *cmd);
|
||||
extern void free_strings (char **argv);
|
||||
extern int count_strings (char * const * const argv);
|
||||
extern void print_strings (char * const * const argv);
|
||||
extern void print_table (char * const * const argv);
|
||||
extern int launch (guestfs_h *);
|
||||
@@ -52,6 +53,10 @@ extern int run_action (const char *cmd, int argc, char *argv[]);
|
||||
/* in completion.c (auto-generated) */
|
||||
extern char **do_completion (const char *text, int start, int end);
|
||||
|
||||
/* in destpaths.c */
|
||||
extern int complete_dest_paths;
|
||||
extern char *complete_dest_paths_generator (const char *text, int state);
|
||||
|
||||
/* in alloc.c */
|
||||
extern int do_alloc (const char *cmd, int argc, char *argv[]);
|
||||
|
||||
|
||||
@@ -107,6 +107,13 @@ This changes the C<-m> option so that mounts are done read-only
|
||||
Enable very verbose messages. This is particularly useful if you find
|
||||
a bug.
|
||||
|
||||
=item B<-D> | B<--no-dest-paths>
|
||||
|
||||
Don't tab-complete paths on the guest filesystem. It is useful to be
|
||||
able to hit the tab key to complete paths on the guest filesystem, but
|
||||
this causes extra "hidden" guestfs calls to be made, so this option is
|
||||
here to allow this feature to be disabled.
|
||||
|
||||
=back
|
||||
|
||||
=head1 COMMANDS ON COMMAND LINE
|
||||
|
||||
@@ -4803,6 +4803,8 @@ generator (const char *text, int state)
|
||||
len = strlen (text);
|
||||
}
|
||||
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
while ((name = commands[index]) != NULL) {
|
||||
index++;
|
||||
if (strncasecmp (name, text, len) == 0)
|
||||
@@ -4819,8 +4821,12 @@ char **do_completion (const char *text, int start, int end)
|
||||
char **matches = NULL;
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
rl_completion_append_character = ' ';
|
||||
|
||||
if (start == 0)
|
||||
matches = rl_completion_matches (text, generator);
|
||||
else if (complete_dest_paths)
|
||||
matches = rl_completion_matches (text, complete_dest_paths_generator);
|
||||
#endif
|
||||
|
||||
return matches;
|
||||
|
||||
Reference in New Issue
Block a user