X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=fish%2Ffish.c;h=65a0c1ddd493cb45301546670e85caf4911aaa33;hp=fbc364fca8114220701a8663c32d8edb51a250c3;hb=4e529e06a4500959cd783f5029354fea47a253a6;hpb=4bcb267a248977c6b044e18a72266d665102de30 diff --git a/fish/fish.c b/fish/fish.c index fbc364f..65a0c1d 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -1,5 +1,5 @@ /* guestfish - the filesystem interactive shell - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 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 @@ -45,6 +45,14 @@ #include "closeout.h" #include "progname.h" +/* Return from parse_command_line. See description below. */ +struct parsed_command { + int status; + char *pipe; + char *cmd; + char *argv[64]; +}; + static void set_up_terminal (void); static void prepare_drives (struct drv *drv); static int launch (void); @@ -52,6 +60,8 @@ static void interactive (void); static void shell_script (void); static void script (int prompt); static void cmdline (char *argv[], int optind, int argc); +static struct parsed_command parse_command_line (char *buf, int *exit_on_error_rtn); +static int execute_and_inline (const char *cmd, int exit_on_error); static void initialize_readline (void); static void cleanup_readline (void); #ifdef HAVE_LIBREADLINE @@ -64,6 +74,7 @@ static int override_progress_bars = -1; guestfs_h *g; int read_only = 0; +int live = 0; int quit = 0; int verbose = 0; int remote_control_listen = 0; @@ -88,18 +99,9 @@ usage (int status) fprintf (stdout, _("%s: guest filesystem shell\n" "%s lets you edit virtual machine filesystems\n" - "Copyright (C) 2009-2010 Red Hat Inc.\n" + "Copyright (C) 2009-2011 Red Hat Inc.\n" "Usage:\n" " %s [--options] cmd [: cmd : cmd ...]\n" - " %s [--ro] -i -a disk-image\n" - " %s [--ro] -i -d libvirt-domain\n" - "or for interactive use:\n" - " %s\n" - "or from a shell script:\n" - " %s <= argc && isatty (0)); if (progress_bars) - guestfs_set_progress_callback (g, progress_callback, NULL); + guestfs_set_event_callback (g, progress_callback, + GUESTFS_EVENT_PROGRESS, 0, NULL); /* Interactive, shell script, or command(s) on the command line? */ if (optind >= argc) { @@ -605,13 +622,9 @@ static void script (int prompt) { char *buf; - char *cmd; - char *p, *pend; - char *argv[64]; - int len; int global_exit_on_error = !prompt; int exit_on_error; - int tilde_candidate; + struct parsed_command pcmd; if (prompt) { printf (_("\n" @@ -630,8 +643,6 @@ script (int prompt) } while (!quit) { - char *pipe = NULL; - exit_on_error = global_exit_on_error; buf = rl_gets (prompt); @@ -640,171 +651,243 @@ script (int prompt) break; } - /* Skip any initial whitespace before the command. */ - again: - while (*buf && c_isspace (*buf)) - buf++; + pcmd = parse_command_line (buf, &exit_on_error); + if (pcmd.status == -1 && exit_on_error) + exit (EXIT_FAILURE); + if (pcmd.status == 1) { + if (issue_command (pcmd.cmd, pcmd.argv, pcmd.pipe, exit_on_error) == -1) { + if (exit_on_error) exit (EXIT_FAILURE); + } + } + } + if (prompt) printf ("\n"); +} + +/* Parse a command string, splitting at whitespace, handling '!', '#' etc. + * This destructively updates 'buf'. + * + * 'exit_on_error_rtn' is used to pass in the global exit_on_error + * setting and to return the local setting (eg. if the command begins + * with '-'). + * + * Returns in parsed_command.status: + * 1 = got a guestfish command (returned in cmd_rtn/argv_rtn/pipe_rtn) + * 0 = no guestfish command, but otherwise OK + * -1 = an error + */ +static struct parsed_command +parse_command_line (char *buf, int *exit_on_error_rtn) +{ + struct parsed_command pcmd; + char *p, *pend; + int len; + int tilde_candidate; + int r; + const size_t argv_len = sizeof pcmd.argv / sizeof pcmd.argv[0]; + + /* Note that pcmd.pipe must be set to NULL for correct usage. Other + * fields do not need to be, but this silences a gcc warning. + */ + memset (&pcmd, 0, sizeof pcmd); - if (!*buf) continue; + again: + /* Skip any initial whitespace before the command. */ + while (*buf && c_isspace (*buf)) + buf++; - /* If the next character is '#' then this is a comment. */ - if (*buf == '#') continue; + if (!*buf) { + pcmd.status = 0; + return pcmd; + } - /* If the next character is '!' then pass the whole lot to system(3). */ - if (*buf == '!') { - int r; + /* If the next character is '#' then this is a comment. */ + if (*buf == '#') { + pcmd.status = 0; + return pcmd; + } - r = system (buf+1); - if (exit_on_error) { - if (r == -1 || - (WIFSIGNALED (r) && - (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) || - WEXITSTATUS (r) != 0) - exit (EXIT_FAILURE); - } - continue; - } + /* If the next character is '!' then pass the whole lot to system(3). */ + if (*buf == '!') { + r = system (buf+1); + if (r == -1 || + (WIFSIGNALED (r) && + (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) || + WEXITSTATUS (r) != 0) + pcmd.status = -1; + else + pcmd.status = 0; + return pcmd; + } - /* If the next character is '-' allow the command to fail without - * exiting on error (just for this one command though). - */ - if (*buf == '-') { - exit_on_error = 0; - buf++; - goto again; - } + /* If the next two characters are " 0 && line[n-1] == '\n') + line[n-1] = '\0'; + + pcmd = parse_command_line (line, &exit_on_error); + if (pcmd.status == -1 && exit_on_error) + exit (EXIT_FAILURE); + if (pcmd.status == 1) { + if (issue_command (pcmd.cmd, pcmd.argv, pcmd.pipe, exit_on_error) == -1) { + if (exit_on_error) exit (EXIT_FAILURE); + } } + } - next_command:; + free (line); + + if (pclose (pp) == -1) { + perror ("pclose"); + return -1; } - if (prompt) printf ("\n"); + + return 0; } static void