fish: Update copyright dates in usage message.
[libguestfs.git] / fish / fish.c
index bc7d96c..e8c0b60 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <signal.h>
-#include <assert.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <locale.h>
+#include <langinfo.h>
 #include <termios.h>
 
 #ifdef HAVE_LIBREADLINE
@@ -68,6 +69,7 @@ struct mp {
   char *mountpoint;
 };
 
+static void set_up_terminal (void);
 static char add_drives (struct drv *drv, char next_drive);
 static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
@@ -81,7 +83,8 @@ static void cleanup_readline (void);
 #ifdef HAVE_LIBREADLINE
 static void add_history_line (const char *);
 #endif
-static void print_shell_quote (FILE *stream, const char *str);
+
+static int override_progress_bars = -1;
 
 /* Currently open libguestfs handle. */
 guestfs_h *g;
@@ -95,6 +98,10 @@ int exit_on_error = 1;
 int command_num = 0;
 int keys_from_stdin = 0;
 const char *libvirt_uri = NULL;
+int inspector = 0;
+int utf8_mode = 0;
+int have_terminfo = 0;
+int progress_bars = 0;
 
 static void __attribute__((noreturn))
 usage (int status)
@@ -106,7 +113,7 @@ usage (int status)
     fprintf (stdout,
            _("%s: guest filesystem shell\n"
              "%s lets you edit virtual machine filesystems\n"
-             "Copyright (C) 2009 Red Hat Inc.\n"
+             "Copyright (C) 2009-2010 Red Hat Inc.\n"
              "Usage:\n"
              "  %s [--options] cmd [: cmd : cmd ...]\n"
              "  %s -i libvirt-domain\n"
@@ -126,19 +133,21 @@ usage (int status)
              "  -d|--domain guest    Add disks from libvirt guest\n"
              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
              "  -f|--file file       Read commands from file\n"
-             "  -i|--inspector       Run virt-inspector to get disk mountpoints\n"
+             "  -i|--inspector       Automatically mount filesystems\n"
              "  --keys-from-stdin    Read passphrases from stdin\n"
              "  --listen             Listen for remote commands\n"
              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
              "  -n|--no-sync         Don't autosync\n"
              "  -N|--new type        Create prepared disk (test1.img, ...)\n"
+             "  --progress-bars      Enable progress bars even when not interactive\n"
+             "  --no-progress-bars   Disable progress bars\n"
              "  --remote[=pid]       Send commands to remote %s\n"
              "  -r|--ro              Mount read-only\n"
              "  --selinux            Enable SELinux support\n"
              "  -v|--verbose         Verbose messages\n"
              "  -x                   Echo each command before executing it\n"
              "  -V|--version         Display version and exit\n"
-             "For more information,  see the manpage %s(1).\n"),
+             "For more information, see the manpage %s(1).\n"),
              program_name, program_name, program_name,
              program_name, program_name, program_name,
              program_name, program_name, program_name);
@@ -158,6 +167,8 @@ main (int argc, char *argv[])
   bindtextdomain (PACKAGE, LOCALEBASEDIR);
   textdomain (PACKAGE);
 
+  set_up_terminal ();
+
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
   static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
@@ -175,6 +186,8 @@ main (int argc, char *argv[])
     { "new", 1, 0, 'N' },
     { "no-dest-paths", 0, 0, 'D' },
     { "no-sync", 0, 0, 'n' },
+    { "progress-bars", 0, 0, 0 },
+    { "no-progress-bars", 0, 0, 0 },
     { "remote", 2, 0, 0 },
     { "ro", 0, 0, 'r' },
     { "selinux", 0, 0, 0 },
@@ -188,7 +201,6 @@ main (int argc, char *argv[])
   struct mp *mp;
   char *p, *file = NULL;
   int c;
-  int inspector = 0;
   int option_index;
   struct sigaction sa;
   int next_prepared_drive = 1;
@@ -228,7 +240,7 @@ main (int argc, char *argv[])
    * using it just above.
    *
    * getopt_long uses argv[0], so give it the sanitized name.  Save a copy
-   * of the original, in case it's needed in virt-inspector mode, below.
+   * of the original, in case it's needed below.
    */
   char *real_argv0 = argv[0];
   argv[0] = bad_cast (program_name);
@@ -261,6 +273,10 @@ main (int argc, char *argv[])
         guestfs_set_selinux (g, 1);
       } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
         keys_from_stdin = 1;
+      } else if (STREQ (long_options[option_index].name, "progress-bars")) {
+        override_progress_bars = 1;
+      } else if (STREQ (long_options[option_index].name, "no-progress-bars")) {
+        override_progress_bars = 0;
       } else {
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
@@ -301,7 +317,10 @@ main (int argc, char *argv[])
       break;
 
     case 'N':
-      if (STRCASEEQ (optarg, "list")) {
+      if (STRCASEEQ (optarg, "list") ||
+          STRCASEEQ (optarg, "help") ||
+          STRCASEEQ (optarg, "h") ||
+          STRCASEEQ (optarg, "?")) {
         list_prepared_drives ();
         exit (EXIT_SUCCESS);
       }
@@ -382,9 +401,12 @@ main (int argc, char *argv[])
       guestfs_set_verbose (g, verbose);
       break;
 
-    case 'V':
-      printf ("%s %s\n", program_name, PACKAGE_VERSION);
+    case 'V': {
+      struct guestfs_version *v = guestfs_version (g);
+      printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
+              v->major, v->minor, v->release, v->extra);
       exit (EXIT_SUCCESS);
+    }
 
     case 'x':
       guestfs_set_trace (g, 1);
@@ -398,115 +420,46 @@ main (int argc, char *argv[])
     }
   }
 
-  /* Inspector mode invalidates most of the other arguments. */
-  if (inspector) {
-    if (drvs || mps || remote_control_listen || remote_control ||
-        guestfs_get_selinux (g)) {
-      fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
-                         "--listen, --remote or --selinux\n"),
-               program_name);
-      exit (EXIT_FAILURE);
-    }
-    if (optind >= argc) {
-      fprintf (stderr,
-           _("%s: -i requires a libvirt domain or path(s) to disk image(s)\n"),
-               program_name);
-      exit (EXIT_FAILURE);
-    }
-
-    char *cmd;
-    size_t cmdlen;
-    FILE *fp = open_memstream (&cmd, &cmdlen);
-    if (fp == NULL) {
-      perror ("open_memstream");
-      exit (EXIT_FAILURE);
-    }
-
-    fprintf (fp, "virt-inspector");
+  /* Old-style -i syntax?  Since -a/-d/-N and -i was disallowed
+   * previously, if we have -i without any drives but with something
+   * on the command line, it must be old-style syntax.
+   */
+  if (inspector && drvs == NULL && optind < argc) {
     while (optind < argc) {
-      fputc (' ', fp);
-      print_shell_quote (fp, argv[optind]);
-      optind++;
-    }
-
-    if (read_only)
-      fprintf (fp, " --ro-fish");
-    else
-      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);
-    }
-
-    fprintf (fp, "%s", real_argv0);
-
-    if (guestfs_get_verbose (g))
-      fprintf (fp, " -v");
-    if (!guestfs_get_autosync (g))
-      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,
-               "%s -i: running: %s\n", program_name, cmd2);
+      if (strchr (argv[optind], '/') ||
+          access (argv[optind], F_OK) == 0) { /* simulate -a option */
+        drv = malloc (sizeof (struct drv));
+        if (!drv) {
+          perror ("malloc");
+          exit (EXIT_FAILURE);
+        }
+        drv->type = drv_a;
+        drv->a.filename = argv[optind];
+        drv->next = drvs;
+        drvs = drv;
+      } else {                  /* simulate -d option */
+        drv = malloc (sizeof (struct drv));
+        if (!drv) {
+          perror ("malloc");
+          exit (EXIT_FAILURE);
+        }
+        drv->type = drv_d;
+        drv->d.guest = argv[optind];
+        drv->next = drvs;
+        drvs = drv;
+      }
 
-    int r = system (cmd2);
-    if (r == -1) {
-      perror (cmd2);
-      exit (EXIT_FAILURE);
+      optind++;
     }
-
-    free (cmd);
-    free (cmd2);
-    free (insp);
-
-    exit (WEXITSTATUS (r));
   }
 
   /* If we've got drives to add, add them now. */
   add_drives (drvs, 'a');
 
-  /* If we've got mountpoints or prepared drives, we must launch the
-   * guest and mount them.
+  /* If we've got mountpoints or prepared drives or -i option, we must
+   * launch the guest and mount them.
    */
-  if (next_prepared_drive > 1 || mps != NULL) {
+  if (next_prepared_drive > 1 || mps != NULL || inspector) {
     /* RHBZ#612178: If --listen flag is given, then we will fork into
      * the background in rc_listen().  However you can't do this while
      * holding a libguestfs handle open because the recovery process
@@ -519,6 +472,10 @@ main (int argc, char *argv[])
       guestfs_set_recovery_proc (g, 0);
 
     if (launch () == -1) exit (EXIT_FAILURE);
+
+    if (inspector)
+      inspect_mount ();
+
     prepare_drives (drvs);
     mount_mps (mps);
   }
@@ -556,6 +513,15 @@ main (int argc, char *argv[])
     }
   }
 
+  /* Decide if we display progress bars. */
+  progress_bars =
+    override_progress_bars >= 0
+    ? override_progress_bars
+    : (optind >= argc && isatty (0));
+
+  if (progress_bars)
+    guestfs_set_progress_callback (g, progress_callback, NULL);
+
   /* Interactive, shell script, or command(s) on the command line? */
   if (optind >= argc) {
     if (isatty (0))
@@ -571,6 +537,35 @@ main (int argc, char *argv[])
   exit (EXIT_SUCCESS);
 }
 
+/* The <term.h> header file which defines this has "issues". */
+extern int tgetent (char *, const char *);
+
+static void
+set_up_terminal (void)
+{
+  /* http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate */
+  utf8_mode = STREQ (nl_langinfo (CODESET), "UTF-8");
+
+  char *term = getenv ("TERM");
+  if (term == NULL) {
+    //fprintf (stderr, _("guestfish: TERM (terminal type) not defined.\n"));
+    return;
+  }
+
+  int r = tgetent (NULL, term);
+  if (r == -1) {
+    fprintf (stderr, _("guestfish: could not access termcap or terminfo database.\n"));
+    return;
+  }
+  if (r == 0) {
+    fprintf (stderr, _("guestfish: terminal type \"%s\" not defined.\n"),
+             term);
+    return;
+  }
+
+  have_terminfo = 1;
+}
+
 void
 pod2text (const char *name, const char *shortdesc, const char *str)
 {
@@ -747,7 +742,7 @@ script (int prompt)
   int global_exit_on_error = !prompt;
   int tilde_candidate;
 
-  if (prompt)
+  if (prompt) {
     printf (_("\n"
               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
               "editing virtual machine filesystems.\n"
@@ -757,6 +752,12 @@ script (int prompt)
               "      'quit' to quit the shell\n"
               "\n"));
 
+    if (inspector) {
+      print_inspect_prompt ();
+      printf ("\n");
+    }
+  }
+
   while (!quit) {
     char *pipe = NULL;
 
@@ -984,6 +985,8 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   int pid = 0;
   int i, r;
 
+  reset_progress_bar ();
+
   /* This counts the commands issued, starting at 1. */
   command_num++;
 
@@ -1057,6 +1060,12 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   else if (STRCASEEQ (cmd, "alloc") ||
            STRCASEEQ (cmd, "allocate"))
     r = do_alloc (cmd, argc, argv);
+  else if (STRCASEEQ (cmd, "copy-in") ||
+           STRCASEEQ (cmd, "copy_in"))
+    r = do_copy_in (cmd, argc, argv);
+  else if (STRCASEEQ (cmd, "copy-out") ||
+           STRCASEEQ (cmd, "copy_out"))
+    r = do_copy_out (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "echo"))
     r = do_echo (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "edit") ||
@@ -1122,6 +1131,10 @@ list_builtin_commands (void)
   printf ("%-20s %s\n",
           "alloc", _("allocate an image"));
   printf ("%-20s %s\n",
+          "copy-in", _("copy files into an image"));
+  printf ("%-20s %s\n",
+          "copy-out", _("copy files out of an image"));
+  printf ("%-20s %s\n",
           "echo", _("display a line of text"));
   printf ("%-20s %s\n",
           "edit", _("edit a file in the image"));
@@ -1162,6 +1175,26 @@ display_builtin_command (const char *cmd)
               ));
     return 0;
   }
+  else if (STRCASEEQ (cmd, "copy-in") ||
+           STRCASEEQ (cmd, "copy_in")) {
+    printf (_("copy-in - copy files into an image\n"
+              "     copy-in <local> [<local> ...] <remotedir>\n"
+              "\n"
+              "    Copy local files or directories recursively into the\n"
+              "    image, placing them on a remote directory.\n"
+              ));
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "copy-out") ||
+           STRCASEEQ (cmd, "copy_out")) {
+    printf (_("copy-out - copy files out of an image\n"
+              "     copy-out <remote> [<remote> ...] <localdir>\n"
+              "\n"
+              "    Copy remote files or directories recursively out of the\n"
+              "    image, placing them in a local directory.\n"
+              ));
+    return 0;
+  }
   else if (STRCASEEQ (cmd, "echo")) {
     printf (_("echo - display a line of text\n"
               "     echo [<params> ...]\n"
@@ -1840,17 +1873,3 @@ read_key (const char *param)
 
   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);
-  }
-}