mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
174 lines
4.4 KiB
C
174 lines
4.4 KiB
C
/* 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;
|
|
}
|