[Libguestfs] [PATCH 3/7] p2v: Use virt-v2v --colours option, support colour in the run dialog (RHBZ#1314244).

Richard W.M. Jones rjones at redhat.com
Sat Jun 18 15:09:13 UTC 2016


---
 p2v/conversion.c |   5 +-
 p2v/gui.c        | 184 +++++++++++++++++++++++++++++++++++++++++++++----------
 p2v/main.c       |   1 +
 p2v/p2v.h        |   3 +
 p2v/ssh.c        |  24 ++++++--
 5 files changed, 175 insertions(+), 42 deletions(-)

diff --git a/p2v/conversion.c b/p2v/conversion.c
index 90e1fbb..dfee0fe 100644
--- a/p2v/conversion.c
+++ b/p2v/conversion.c
@@ -333,9 +333,10 @@ start_conversion (struct config *config,
   /* Build the virt-v2v command up in pieces to make the quoting
    * slightly more sane.
    */
-  if (mexp_printf (control_h, "( %s virt-v2v%s -i libvirtxml",
+  if (mexp_printf (control_h, "( %s virt-v2v%s%s -i libvirtxml",
                    config->sudo ? "sudo -n " : "",
-                   config->verbose ? " -v -x" : "") == -1) {
+                   config->verbose ? " -v -x" : "",
+                   feature_colours_option ? " --colours" : "") == -1) {
   printf_fail:
     set_conversion_error ("mexp_printf: virt-v2v command: %m");
     goto out;
diff --git a/p2v/gui.c b/p2v/gui.c
index 4188ba6..c34bc6c 100644
--- a/p2v/gui.c
+++ b/p2v/gui.c
@@ -208,6 +208,9 @@ static GtkWidget *run_dlg,
   *v2v_output_sw, *v2v_output, *log_label, *status_label,
   *cancel_button, *reboot_button;
 
+/* Colour tags used in the v2v_output GtkTextBuffer. */
+static GtkTextTag *v2v_output_tags[16];
+
 /**
  * The entry point from the main program.
  *
@@ -1649,6 +1652,12 @@ static gboolean close_running_dialog (GtkWidget *w, GdkEvent *event, gpointer da
 static void
 create_running_dialog (void)
 {
+  size_t i;
+  static const char *tags[16] =
+    { "black", "maroon", "green", "olive", "navy", "purple", "teal", "silver",
+      "gray", "red", "lime", "yellow", "blue", "fuchsia", "cyan", "white" };
+  GtkTextBuffer *buf;
+
   run_dlg = gtk_dialog_new ();
   gtk_window_set_title (GTK_WINDOW (run_dlg), guestfs_int_program_name);
   gtk_window_set_resizable (GTK_WINDOW (run_dlg), FALSE);
@@ -1662,6 +1671,17 @@ create_running_dialog (void)
   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (v2v_output), GTK_WRAP_CHAR);
   /* XXX Gtk3 ignores this, why? */
   gtk_widget_set_size_request (v2v_output, 700, 400);
+
+  buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (v2v_output));
+  for (i = 0; i < 16; ++i) {
+    CLEANUP_FREE char *tag_name;
+
+    if (asprintf (&tag_name, "tag_%s", tags[i]) == -1)
+      error (EXIT_FAILURE, errno, "asprintf");
+    v2v_output_tags[i] =
+      gtk_text_buffer_create_tag (buf, tag_name, "foreground", tags[i], NULL);
+  }
+
   log_label = gtk_label_new (NULL);
   set_alignment (log_label, 0., 0.5);
   set_padding (log_label, 10, 10);
@@ -1767,12 +1787,12 @@ set_status (gpointer user_data)
   return FALSE;
 }
 
-static void add_v2v_output_helper (const char *msg, size_t len);
-
 /**
  * Append output from the virt-v2v process to the buffer, and scroll
  * to ensure it is visible.
  *
+ * This function is able to parse ANSI colour sequences and more.
+ *
  * If this isn't called from the main thread, then you must only
  * call it via an idle task (C<g_idle_add>).
  *
@@ -1783,44 +1803,140 @@ static gboolean
 add_v2v_output (gpointer user_data)
 {
   CLEANUP_FREE const char *msg = user_data;
+  const char *p;
   static size_t linelen = 0;
-  const char *p0, *p;
-
-  /* Gtk2 (in ~ Fedora 23) has a regression where it takes much longer
-   * to display long lines, to the point where the virt-p2v UI would
-   * still be slowly displaying kernel modules while the conversion
-   * had finished.  For this reason, arbitrarily break long lines.
-   */
-  for (p0 = p = msg; *p; ++p) {
-    linelen++;
-    if (*p == '\n' || linelen > 1024) {
-      add_v2v_output_helper (p0, p-p0+1);
-      if (*p != '\n')
-        add_v2v_output_helper ("\n", 1);
-      linelen = 0;
-      p0 = p+1;
-    }
-  }
-  add_v2v_output_helper (p0, p-p0);
-
-  return FALSE;
-}
-
-static void
-add_v2v_output_helper (const char *msg, size_t len)
-{
-  GtkTextBuffer *buf;
-  GtkTextIter iter;
-
-  /* Insert it at the end. */
-  buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (v2v_output));
-  gtk_text_buffer_get_end_iter (buf, &iter);
-  gtk_text_buffer_insert (buf, &iter, msg, len);
+  static enum {
+    state_normal,
+    state_escape1,       /* seen ESC, expecting [ */
+    state_escape2,       /* seen ESC [, expecting 0 or 1 */
+    state_escape3,       /* seen ESC [ 0/1, expecting ; or m */
+    state_escape4,       /* seen ESC [ 0/1 ;, expecting 3 */
+    state_escape5,       /* seen ESC [ 0/1 ; 3, expecting 1/2/4/5 */
+    state_escape6,       /* seen ESC [ 0/1 ; 3 1/2/5/5, expecting m */
+    state_cr,            /* seen CR */
+    state_truncating,    /* truncating line until next \n */
+  } state = state_normal;
+  static int colour = 0;
+  GtkTextTag *tag = NULL;
+  GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (v2v_output));
+  GtkTextIter iter, iter2;
+  const char *dots = " [...]";
+
+  for (p = msg; *p != '\0'; ++p) {
+    char c = *p;
+
+    switch (state) {
+    case state_normal:
+      if (c == '\r')            /* Start of possible CRLF sequence. */
+        state = state_cr;
+      else if (c == '\x1b') {   /* Start of an escape sequence. */
+        state = state_escape1;
+        colour = 0;
+      }
+      else if (c != '\n' && linelen >= 256) {
+        /* Gtk2 (in ~ Fedora 23) has a regression where it takes much
+         * longer to display long lines, to the point where the
+         * virt-p2v UI would still be slowly displaying kernel modules
+         * while the conversion had finished.  For this reason,
+         * arbitrarily truncate very long lines.
+         */
+        gtk_text_buffer_get_end_iter (buf, &iter);
+        gtk_text_buffer_insert_with_tags (buf, &iter,
+                                          dots, strlen (dots), tag, NULL);
+        state = state_truncating;
+        colour = 0;
+        tag = NULL;
+      }
+      else {             /* Treat everything else as a normal char. */
+        if (c != '\n') linelen++; else linelen = 0;
+        gtk_text_buffer_get_end_iter (buf, &iter);
+        gtk_text_buffer_insert_with_tags (buf, &iter, &c, 1, tag, NULL);
+      }
+      break;
+
+    case state_escape1:
+      if (c == '[')
+        state = state_escape2;
+      else
+        state = state_normal;
+      break;
+
+    case state_escape2:
+      if (c == '0')
+        state = state_escape3;
+      else if (c == '1') {
+        state = state_escape3;
+        colour += 8;
+      }
+      else
+        state = state_normal;
+      break;
+
+    case state_escape3:
+      if (c == ';')
+        state = state_escape4;
+      else if (c == 'm') {
+        tag = NULL;             /* restore text colour */
+        state = state_normal;
+      }
+      else
+        state = state_normal;
+      break;
+
+    case state_escape4:
+      if (c == '3')
+        state = state_escape5;
+      else
+        state = state_normal;
+      break;
+
+    case state_escape5:
+      if (c >= '0' && c <= '7') {
+        state = state_escape6;
+        colour += c - '0';
+      }
+      else
+        state = state_normal;
+      break;
+
+    case state_escape6:
+      if (c == 'm') {
+        assert (colour >= 0 && colour <= 15);
+        tag = v2v_output_tags[colour]; /* set colour tag */
+      }
+      state = state_normal;
+      break;
+
+    case state_cr:
+      if (c == '\n')
+        /* Process CRLF as single a newline character. */
+        p--;
+      else {                    /* Delete current (== last) line. */
+        linelen = 0;
+        gtk_text_buffer_get_end_iter (buf, &iter);
+        iter2 = iter;
+        gtk_text_iter_set_line_offset (&iter, 0);
+        /* Delete from iter..iter2 */
+        gtk_text_buffer_delete (buf, &iter, &iter2);
+      }
+      state = state_normal;
+      break;
+
+    case state_truncating:
+      if (c == '\n') {
+        p--;
+        state = state_normal;
+      }
+      break;
+    } /* switch (state) */
+  } /* for */
 
   /* Scroll to the end of the buffer. */
   gtk_text_buffer_get_end_iter (buf, &iter);
   gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (v2v_output), &iter,
                                 0, FALSE, 0., 1.);
+
+  return FALSE;
 }
 
 /**
diff --git a/p2v/main.c b/p2v/main.c
index c2f0860..40d8097 100644
--- a/p2v/main.c
+++ b/p2v/main.c
@@ -43,6 +43,7 @@ char **all_disks;
 char **all_removable;
 char **all_interfaces;
 int is_iso_environment = 0;
+int feature_colours_option = 0;
 
 static const char *test_disk = NULL;
 
diff --git a/p2v/p2v.h b/p2v/p2v.h
index 9ccaf0f..12dd210 100644
--- a/p2v/p2v.h
+++ b/p2v/p2v.h
@@ -58,6 +58,9 @@ extern char **all_interfaces;
  */
 extern int is_iso_environment;
 
+/* True if virt-v2v supports the --colours option. */
+extern int feature_colours_option;
+
 /* config.c */
 struct config {
   int verbose;
diff --git a/p2v/ssh.c b/p2v/ssh.c
index 4ed6f98..e916ca3 100644
--- a/p2v/ssh.c
+++ b/p2v/ssh.c
@@ -100,6 +100,7 @@ static pcre *ssh_message_re;
 static pcre *prompt_re;
 static pcre *version_re;
 static pcre *feature_libguestfs_rewrite_re;
+static pcre *feature_colours_option_re;
 static pcre *feature_input_re;
 static pcre *feature_output_re;
 static pcre *portfwd_re;
@@ -148,6 +149,7 @@ compile_regexps (void)
            "virt-v2v ([1-9].*)",
 	   0);
   COMPILE (feature_libguestfs_rewrite_re, "libguestfs-rewrite", 0);
+  COMPILE (feature_colours_option_re, "colours-option", 0);
   COMPILE (feature_input_re, "input:((?:\\w)*)", 0);
   COMPILE (feature_output_re, "output:((?:\\w)*)", 0);
   COMPILE (portfwd_re, "Allocated port ((?:\\d)+) for remote forward", 0);
@@ -161,6 +163,7 @@ free_regexps (void)
   pcre_free (prompt_re);
   pcre_free (version_re);
   pcre_free (feature_libguestfs_rewrite_re);
+  pcre_free (feature_colours_option_re);
   pcre_free (feature_input_re);
   pcre_free (feature_output_re);
   pcre_free (portfwd_re);
@@ -599,28 +602,37 @@ test_connection (struct config *config)
     switch (mexp_expect (h,
                          (mexp_regexp[]) {
                            { 100, .re = feature_libguestfs_rewrite_re },
-                           { 101, .re = feature_input_re },
-                           { 102, .re = feature_output_re },
-                           { 103, .re = prompt_re },
+                           { 101, .re = feature_colours_option_re },
+                           { 102, .re = feature_input_re },
+                           { 103, .re = feature_output_re },
+                           { 104, .re = prompt_re },
                            { 0 }
                          }, ovector, ovecsize)) {
     case 100:                   /* libguestfs-rewrite. */
       feature_libguestfs_rewrite = 1;
       break;
 
-    case 101:
+    case 101:                   /* virt-v2v supports --colours option */
+#if DEBUG_STDERR
+  fprintf (stderr, "%s: remote virt-v2v supports --colours option\n",
+           guestfs_int_program_name);
+#endif
+      feature_colours_option = 1;
+      break;
+
+    case 102:
       /* input:<driver-name> corresponds to an -i option in virt-v2v. */
       add_input_driver (&h->buffer[ovector[2]],
                         (size_t) (ovector[3]-ovector[2]));
       break;
 
-    case 102:
+    case 103:
       /* output:<driver-name> corresponds to an -o option in virt-v2v. */
       add_output_driver (&h->buffer[ovector[2]],
                          (size_t) (ovector[3]-ovector[2]));
       break;
 
-    case 103:                   /* Got prompt, so end of output. */
+    case 104:                   /* Got prompt, so end of output. */
       goto end_of_machine_readable;
 
     case MEXP_EOF:
-- 
2.7.4




More information about the Libguestfs mailing list