guestfish -i and virt-inspector work on filenames containing spaces (RHBZ#507810).
[libguestfs.git] / fish / fish.c
index 4a7e70b..b7601b9 100644 (file)
@@ -1,5 +1,5 @@
 /* guestfish - the filesystem interactive shell
 /* guestfish - the filesystem interactive shell
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 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
  *
  * 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
@@ -58,6 +58,7 @@ struct mp {
 static void add_drives (struct drv *drv);
 static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
 static void add_drives (struct drv *drv);
 static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
+static int launch (void);
 static void interactive (void);
 static void shell_script (void);
 static void script (int prompt);
 static void interactive (void);
 static void shell_script (void);
 static void script (int prompt);
@@ -65,6 +66,7 @@ static void cmdline (char *argv[], int optind, int argc);
 static void initialize_readline (void);
 static void cleanup_readline (void);
 static void add_history_line (const char *);
 static void initialize_readline (void);
 static void cleanup_readline (void);
 static void add_history_line (const char *);
+static int print_shell_quote (FILE *stream, const char *str);
 
 /* Currently open libguestfs handle. */
 guestfs_h *g;
 
 /* Currently open libguestfs handle. */
 guestfs_h *g;
@@ -72,24 +74,11 @@ guestfs_h *g;
 int read_only = 0;
 int quit = 0;
 int verbose = 0;
 int read_only = 0;
 int quit = 0;
 int verbose = 0;
-int echo_commands = 0;
 int remote_control_listen = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
 
 int remote_control_listen = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
 
-int
-launch (guestfs_h *_g)
-{
-  assert (_g == g);
-
-  if (guestfs_is_config (g)) {
-    if (guestfs_launch (g) == -1)
-      return -1;
-  }
-  return 0;
-}
-
 static void __attribute__((noreturn))
 usage (int status)
 {
 static void __attribute__((noreturn))
 usage (int status)
 {
@@ -361,7 +350,7 @@ main (int argc, char *argv[])
       exit (EXIT_SUCCESS);
 
     case 'x':
       exit (EXIT_SUCCESS);
 
     case 'x':
-      echo_commands = 1;
+      guestfs_set_trace (g, 1);
       break;
 
     case HELP_OPTION:
       break;
 
     case HELP_OPTION:
@@ -374,9 +363,6 @@ main (int argc, char *argv[])
 
   /* Inspector mode invalidates most of the other arguments. */
   if (inspector) {
 
   /* Inspector mode invalidates most of the other arguments. */
   if (inspector) {
-    char cmd[1024];
-    int r;
-
     if (drvs || mps || remote_control_listen || remote_control ||
         guestfs_get_selinux (g)) {
       fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
     if (drvs || mps || remote_control_listen || remote_control ||
         guestfs_get_selinux (g)) {
       fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
@@ -391,42 +377,89 @@ main (int argc, char *argv[])
       exit (EXIT_FAILURE);
     }
 
       exit (EXIT_FAILURE);
     }
 
-    strcpy (cmd, "a=`virt-inspector");
+    char *cmd;
+    size_t cmdlen;
+    FILE *fp = open_memstream (&cmd, &cmdlen);
+    if (fp == NULL) {
+      perror ("open_memstream");
+      exit (EXIT_FAILURE);
+    }
+
+    fprintf (fp, "virt-inspector");
     while (optind < argc) {
     while (optind < argc) {
-      if (strlen (cmd) + strlen (argv[optind]) + strlen (real_argv0) + 60
-          >= sizeof cmd) {
-        fprintf (stderr,
-                 _("%s: virt-inspector command too long for fixed-size buffer\n"),
-                 program_name);
-        exit (EXIT_FAILURE);
-      }
-      strcat (cmd, " '");
-      strcat (cmd, argv[optind]);
-      strcat (cmd, "'");
+      fputc (' ', fp);
+      print_shell_quote (fp, argv[optind]);
       optind++;
     }
 
     if (read_only)
       optind++;
     }
 
     if (read_only)
-      strcat (cmd, " --ro-fish");
+      fprintf (fp, " --ro-fish");
     else
     else
-      strcat (cmd, " --fish");
+      fprintf (fp, " --fish");
+
+    if (fclose (fp) == -1) {
+      perror ("fclose");
+      exit (EXIT_FAILURE);
+    }
+
+    if (verbose)
+      fprintf (stderr,
+               "%s -i: running: %s\n", program_name, cmd);
+
+    FILE *pp = popen (cmd, "r");
+    if (pp == NULL) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+
+    char *cmd2;
+    fp = open_memstream (&cmd2, &cmdlen);
+    if (fp == NULL) {
+      perror ("open_memstream");
+      exit (EXIT_FAILURE);
+    }
 
 
-    sprintf (&cmd[strlen(cmd)], "` && %s $a", real_argv0);
+    fprintf (fp, "%s", real_argv0);
 
     if (guestfs_get_verbose (g))
 
     if (guestfs_get_verbose (g))
-      strcat (cmd, " -v");
+      fprintf (fp, " -v");
     if (!guestfs_get_autosync (g))
     if (!guestfs_get_autosync (g))
-      strcat (cmd, " -n");
+      fprintf (fp, " -n");
+    if (guestfs_get_trace (g))
+      fprintf (fp, " -x");
+
+    char *insp = NULL;
+    size_t insplen;
+    if (getline (&insp, &insplen, pp) == -1) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+    fprintf (fp, " %s", insp);
+
+    if (pclose (pp) == -1) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+
+    if (fclose (fp) == -1) {
+      perror ("fclose");
+      exit (EXIT_FAILURE);
+    }
 
     if (verbose)
       fprintf (stderr,
 
     if (verbose)
       fprintf (stderr,
-               "%s -i: running virt-inspector command:\n%s\n", program_name, cmd);
+               "%s -i: running: %s\n", program_name, cmd2);
 
 
-    r = system (cmd);
+    int r = system (cmd2);
     if (r == -1) {
     if (r == -1) {
-      perror ("system");
+      perror (cmd2);
       exit (EXIT_FAILURE);
     }
       exit (EXIT_FAILURE);
     }
+
+    free (cmd);
+    free (cmd2);
+    free (insp);
+
     exit (WEXITSTATUS (r));
   }
 
     exit (WEXITSTATUS (r));
   }
 
@@ -437,7 +470,7 @@ main (int argc, char *argv[])
    * guest and mount them.
    */
   if (next_prepared_drive > 1 || mps != NULL) {
    * guest and mount them.
    */
   if (next_prepared_drive > 1 || mps != NULL) {
-    if (launch (g) == -1) exit (EXIT_FAILURE);
+    if (launch () == -1) exit (EXIT_FAILURE);
     prepare_drives (drvs);
     mount_mps (mps);
   }
     prepare_drives (drvs);
     mount_mps (mps);
   }
@@ -550,10 +583,21 @@ prepare_drives (struct drv *drv)
 {
   if (drv) {
     prepare_drives (drv->next);
 {
   if (drv) {
     prepare_drives (drv->next);
-    prepare_drive (drv->filename, drv->data, drv->device);
+    if (drv->data)
+      prepare_drive (drv->filename, drv->data, drv->device);
   }
 }
 
   }
 }
 
+static int
+launch (void)
+{
+  if (guestfs_is_config (g)) {
+    if (guestfs_launch (g) == -1)
+      return -1;
+  }
+  return 0;
+}
+
 static void
 interactive (void)
 {
 static void
 interactive (void)
 {
@@ -621,7 +665,8 @@ script (int prompt)
               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
               "editing virtual machine filesystems.\n"
               "\n"
               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
               "editing virtual machine filesystems.\n"
               "\n"
-              "Type: 'help' for help with commands\n"
+              "Type: 'help' for a list of commands\n"
+              "      'man' to read the manual\n"
               "      'quit' to quit the shell\n"
               "\n"));
 
               "      'quit' to quit the shell\n"
               "\n"));
 
@@ -855,13 +900,6 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   /* This counts the commands issued, starting at 1. */
   command_num++;
 
   /* This counts the commands issued, starting at 1. */
   command_num++;
 
-  if (echo_commands) {
-    printf ("%s", cmd);
-    for (i = 0; argv[i] != NULL; ++i)
-      printf (" %s", argv[i]);
-    printf ("\n");
-  }
-
   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
   if (pipecmd) {
     int fd[2];
   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
   if (pipecmd) {
     int fd[2];
@@ -942,6 +980,9 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
     r = do_lcd (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "glob"))
     r = do_glob (cmd, argc, argv);
     r = do_lcd (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "glob"))
     r = do_glob (cmd, argc, argv);
+  else if (STRCASEEQ (cmd, "man") ||
+           STRCASEEQ (cmd, "manual"))
+    r = do_man (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "more") ||
            STRCASEEQ (cmd, "less"))
     r = do_more (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "more") ||
            STRCASEEQ (cmd, "less"))
     r = do_more (cmd, argc, argv);
@@ -981,10 +1022,12 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
 void
 list_builtin_commands (void)
 {
 void
 list_builtin_commands (void)
 {
-  /* help and quit should appear at the top */
+  /* help, man and quit should appear at the top */
   printf ("%-20s %s\n",
           "help", _("display a list of commands or help on a command"));
   printf ("%-20s %s\n",
   printf ("%-20s %s\n",
           "help", _("display a list of commands or help on a command"));
   printf ("%-20s %s\n",
+          "man", _("read the manual"));
+  printf ("%-20s %s\n",
           "quit", _("quit guestfish"));
 
   printf ("%-20s %s\n",
           "quit", _("quit guestfish"));
 
   printf ("%-20s %s\n",
@@ -1069,6 +1112,12 @@ display_builtin_command (const char *cmd)
               "    Glob runs <command> with wildcards expanded in any\n"
               "    command args.  Note that the command is run repeatedly\n"
               "    once for each expanded argument.\n"));
               "    Glob runs <command> with wildcards expanded in any\n"
               "    command args.  Note that the command is run repeatedly\n"
               "    once for each expanded argument.\n"));
+  else if (STRCASEEQ (cmd, "man") ||
+           STRCASEEQ (cmd, "manual"))
+    printf (_("man - read the manual\n"
+              "    man\n"
+              "\n"
+              "    Opens the manual page for guestfish.\n"));
   else if (STRCASEEQ (cmd, "help"))
     printf (_("help - display a list of commands or help on a command\n"
               "     help cmd\n"
   else if (STRCASEEQ (cmd, "help"))
     printf (_("help - display a list of commands or help on a command\n"
               "     help cmd\n"
@@ -1610,3 +1659,17 @@ file_out (const char *arg)
   }
   return ret;
 }
   }
   return ret;
 }
+
+static void
+print_shell_quote (FILE *stream, const char *str)
+{
+#define SAFE(c) (c_isalnum((c)) ||                                     \
+                 (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
+  int i;
+
+  for (i = 0; str[i]; ++i) {
+    if (!SAFE(str[i]))
+      putc ('\\', stream);
+    putc (str[i], stream);
+  }
+}