From: Richard Jones Date: Mon, 22 Jun 2009 08:35:43 +0000 (+0100) Subject: Add 'glob' command for guestfish. X-Git-Tag: 1.0.50~1 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=394c8bec21d47b74567a4148fdbf87318c301441 Add 'glob' command for guestfish. --- diff --git a/fish/Makefile.am b/fish/Makefile.am index c3fbc6f..1e5b108 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -26,6 +26,7 @@ guestfish_SOURCES = \ edit.c \ fish.c \ fish.h \ + glob.c \ lcd.c guestfish_CFLAGS = \ diff --git a/fish/fish.c b/fish/fish.c index e66880f..bf82b8a 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -56,7 +56,6 @@ static void interactive (void); static void shell_script (void); static void script (int prompt); static void cmdline (char *argv[], int optind, int argc); -static int issue_command (const char *cmd, char *argv[]); static void initialize_readline (void); static void cleanup_readline (void); static void add_history_line (const char *); @@ -569,7 +568,7 @@ cmdline (char *argv[], int optind, int argc) } } -static int +int issue_command (const char *cmd, char *argv[]) { int argc; @@ -601,6 +600,8 @@ issue_command (const char *cmd, char *argv[]) return do_edit (cmd, argc, argv); else if (strcasecmp (cmd, "lcd") == 0) return do_lcd (cmd, argc, argv); + else if (strcasecmp (cmd, "glob") == 0) + return do_glob (cmd, argc, argv); else return run_action (cmd, argc, argv); } @@ -622,6 +623,8 @@ list_builtin_commands (void) "edit", _("edit a file in the image")); printf ("%-20s %s\n", "lcd", _("local change directory")); + printf ("%-20s %s\n", + "glob", _("expand wildcards in command")); /* actions are printed after this (see list_commands) */ } @@ -676,6 +679,13 @@ display_builtin_command (const char *cmd) " Change guestfish's current directory. This command is\n" " useful if you want to download files to a particular\n" " place.\n")); + else if (strcasecmp (cmd, "glob") == 0) + printf (_("glob - expand wildcards in command\n" + " glob [ ...]\n" + "\n" + " Glob runs with wildcards expanded in any\n" + " command args. Note that the command is run repeatedly\n" + " once for each expanded argument.\n")); else if (strcasecmp (cmd, "help") == 0) printf (_("help - display a list of commands or help on a command\n" " help cmd\n" diff --git a/fish/fish.h b/fish/fish.h index 028deec..8815807 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -34,6 +34,7 @@ extern guestfs_h *g; extern int quit; extern int verbose; +extern int issue_command (const char *cmd, char *argv[]); extern void pod2text (const char *heading, const char *body); extern void list_builtin_commands (void); extern void display_builtin_command (const char *cmd); @@ -69,6 +70,9 @@ extern int do_edit (const char *cmd, int argc, char *argv[]); /* in lcd.c */ extern int do_lcd (const char *cmd, int argc, char *argv[]); +/* in glob.c */ +extern int do_glob (const char *cmd, int argc, char *argv[]); + /* This should just list all the built-in commands so they can * be added to the generated auto-completion code. */ @@ -77,7 +81,8 @@ extern int do_lcd (const char *cmd, int argc, char *argv[]); "quit", "exit", "q", \ "alloc", "allocate", \ "echo", \ - "edit", "vi", "emacs" \ - "lcd" + "edit", "vi", "emacs", \ + "lcd", \ + "glob" #endif /* FISH_H */ diff --git a/fish/glob.c b/fish/glob.c new file mode 100644 index 0000000..827e062 --- /dev/null +++ b/fish/glob.c @@ -0,0 +1,163 @@ +/* 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 "fish.h" + +/* A bit tricky because in the case where there are multiple + * paths we have to perform a Cartesian product. + */ +static void glob_issue (char *cmd, int argc, char ***globs, int *posn, int *count, int *r); + +int +do_glob (const char *cmd, int argc, char *argv[]) +{ + /* For 'glob cmd foo /s* /usr/s*' this could be: + * + * (globs[0]) globs[1] globs[1] globs[2] + * (cmd) foo /sbin /usr/sbin + * /srv /usr/share + * /sys /usr/src + * + * and then we call every combination (ie. 1x3x3) of + * argv[1-]. + */ + char **globs[argc]; + int posn[argc]; + int count[argc]; + int i, r = 0; + + if (argc < 1) { + fprintf (stderr, _("use 'glob command [args...]'\n")); + return -1; + } + + /* This array will record the current execution position + * in the Cartesian product. + * NB. globs[0], posn[0], count[0] are ignored. + */ + for (i = 1; i < argc; ++i) + posn[i] = 0; + for (i = 1; i < argc; ++i) + globs[i] = NULL; + + for (i = 1; i < argc; ++i) { + char **pp; + + /* Only if it begins with '/' can it possibly be a globbable path. */ + if (argv[i][0] == '/') { + pp = guestfs_glob_expand (g, argv[i]); + if (pp == NULL) { /* real error in glob_expand */ + fprintf (stderr, _("glob: guestfs_glob_expand call failed: %s\n"), + argv[i]); + goto error0; + } + + /* If there were no matches, then we add a single element list + * containing just the original argv[i] string. + */ + if (pp[0] == NULL) { + char **pp2; + + pp2 = realloc (pp, sizeof (char *) * 2); + if (pp2 == NULL) { + perror ("realloc"); + free (pp); + goto error0; + } + pp = pp2; + + pp[0] = strdup (argv[i]); + if (pp[0] == NULL) { + perror ("strdup"); + free (pp); + goto error0; + } + pp[1] = NULL; + } + } + /* Doesn't begin with '/' */ + else { + pp = malloc (sizeof (char *) * 2); + if (pp == NULL) { + perror ("malloc"); + goto error0; + } + pp[0] = strdup (argv[i]); + if (pp[0] == NULL) { + perror ("strdup"); + free (pp); + goto error0; + } + pp[1] = NULL; + } + + globs[i] = pp; + count[i] = count_strings (pp); + } + + /* Issue the commands. */ + glob_issue (argv[0], argc, globs, posn, count, &r); + + /* Free resources. */ + error0: + for (i = 1; i < argc; ++i) + if (globs[i]) + free_strings (globs[i]); + return r; +} + +static void +glob_issue (char *cmd, int argc, + char ***globs, int *posn, int *count, + int *r) +{ + int i; + char *argv[argc+1]; + + argv[0] = cmd; + argv[argc] = NULL; + + again: + printf ("%s", argv[0]); + for (i = 1; i < argc; ++i) { + argv[i] = globs[i][posn[i]]; + printf (" %s", argv[i]); + } + printf ("\n"); + + if (issue_command (argv[0], &argv[1]) == -1) + r = -1; /* ... but don't exit */ + + for (i = argc-1; i >= 1; --i) { + posn[i]++; + if (posn[i] < count[i]) + break; + posn[i] = 0; + } + if (i == 0) /* All done. */ + return; + + goto again; +} diff --git a/guestfish.pod b/guestfish.pod index 19c9d43..d83a61c 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -184,6 +184,33 @@ a space-separated list, enclosed in quotes. For example: vgcreate VG "/dev/sda1 /dev/sdb1" +=head1 WILDCARDS AND GLOBBING + +Neither guestfish nor the underlying guestfs API performs +wildcard expansion (globbing) by default. So for example the +following will not do what you expect: + + rm-rf /home/* + +Assuming you don't have a directory literally called C +then the above command will return an error. + +To perform wildcard expansion, use the C command. + + glob rm-rf /home/* + +runs C on each path that matches (ie. potentially running +the command many times), equivalent to: + + rm-rf /home/jim + rm-rf /home/joe + rm-rf /home/mary + +C only works on simple guest paths and not on device names. + +If you have several parameters, each containing a wildcard, then glob +will perform a cartesian product. + =head1 COMMENTS Any line which starts with a I<#> character is treated as a comment @@ -294,6 +321,15 @@ itself. Note that C won't do what you might expect. +=head2 glob + + glob command args... + +Expand wildcards in any paths in the args list, and run C +repeatedly on each matching path. + +See section WILDCARDS AND GLOBBING. + @ACTIONS@ =head1 ENVIRONMENT VARIABLES