[util] Refactor hb-view completely

Now we can use the same code to do other utils...
diff --git a/util/options.cc b/util/options.cc
index 6fc4029..eb3ce69 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -26,14 +26,107 @@
 
 #include "options.hh"
 
+#if HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
 
-view_options_t view_opts[1];
-shape_options_t shape_opts[1];
-font_options_t font_opts[1];
 
-const char *text;
-const char *out_file = "/dev/stdout";
-hb_bool_t debug = FALSE;
+bool debug = FALSE;
+
+static gchar *
+shapers_to_string (void)
+{
+  GString *shapers = g_string_new (NULL);
+  const char **shaper_list = hb_shape_list_shapers ();
+
+  for (; *shaper_list; shaper_list++) {
+    g_string_append (shapers, *shaper_list);
+    g_string_append_c (shapers, ',');
+  }
+  g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
+
+  return g_string_free (shapers, FALSE);
+}
+
+static G_GNUC_NORETURN gboolean
+show_version (const char *name G_GNUC_UNUSED,
+	      const char *arg G_GNUC_UNUSED,
+	      gpointer    data G_GNUC_UNUSED,
+	      GError    **error G_GNUC_UNUSED)
+{
+  g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
+
+  char *shapers = shapers_to_string ();
+  g_printf ("Available shapers: %s\n", shapers);
+  g_free (shapers);
+  if (strcmp (HB_VERSION_STRING, hb_version_string ()))
+    g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
+
+  exit(0);
+}
+
+
+void
+option_parser_t::add_main_options (void)
+{
+  GOptionEntry entries[] =
+  {
+    {"version",		0, G_OPTION_FLAG_NO_ARG,
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			NULL},
+    {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	NULL},
+    {NULL}
+  };
+  g_option_context_add_main_entries (context, entries, NULL);
+}
+
+static gboolean
+pre_parse (GOptionContext *context G_GNUC_UNUSED,
+	   GOptionGroup *group G_GNUC_UNUSED,
+	   gpointer data,
+	   GError **error)
+{
+  option_group_t *option_group = (option_group_t *) data;
+  option_group->pre_parse (error);
+  return *error == NULL;
+}
+
+static gboolean
+post_parse (GOptionContext *context G_GNUC_UNUSED,
+	    GOptionGroup *group G_GNUC_UNUSED,
+	    gpointer data,
+	    GError **error)
+{
+  option_group_t *option_group = static_cast<option_group_t *>(data);
+  option_group->post_parse (error);
+  return *error == NULL;
+}
+
+void
+option_parser_t::add_group (GOptionEntry   *entries,
+			    const gchar    *name,
+			    const gchar    *description,
+			    const gchar    *help_description,
+			    option_group_t *option_group)
+{
+  GOptionGroup *group = g_option_group_new (name, description, help_description,
+					    static_cast<gpointer>(option_group), NULL);
+  g_option_group_add_entries (group, entries);
+  g_option_group_set_parse_hooks (group, pre_parse, post_parse);
+  g_option_context_add_group (context, group);
+}
+
+void
+option_parser_t::parse (int *argc, char ***argv)
+{
+  GError *parse_error = NULL;
+  if (!g_option_context_parse (context, argc, argv, &parse_error))
+  {
+    if (parse_error != NULL)
+      fail (TRUE, "%s", parse_error->message);
+    else
+      fail (TRUE, "Option parse error");
+  }
+}
 
 
 static gboolean
@@ -232,54 +325,8 @@
 }
 
 
-static gchar *
-shapers_to_string (void)
-{
-  GString *shapers = g_string_new (NULL);
-  const char **shaper_list = hb_shape_list_shapers ();
-
-  for (; *shaper_list; shaper_list++) {
-    g_string_append (shapers, *shaper_list);
-    g_string_append_c (shapers, ',');
-  }
-  g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
-
-  return g_string_free (shapers, FALSE);
-}
-
-static G_GNUC_NORETURN gboolean
-show_version (const char *name G_GNUC_UNUSED,
-	      const char *arg G_GNUC_UNUSED,
-	      gpointer    data G_GNUC_UNUSED,
-	      GError    **error G_GNUC_UNUSED)
-{
-  g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
-
-  char *shapers = shapers_to_string ();
-  g_printf ("Available shapers: %s\n", shapers);
-  g_free (shapers);
-  if (strcmp (HB_VERSION_STRING, hb_version_string ()))
-    g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
-
-  exit(0);
-}
-
-
-static void
-option_context_add_entries (GOptionContext *context,
-			    GOptionEntry   *entries,
-			    const gchar    *name,
-			    const gchar    *description,
-			    const gchar    *help_description,
-			    gpointer        user_data)
-{
-  GOptionGroup *group = g_option_group_new (name, description, help_description, user_data, NULL);
-  g_option_group_add_entries (group, entries);
-  g_option_context_add_group (context, group);
-}
-
 void
-view_options_t::add_options (GOptionContext *context)
+view_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
@@ -290,15 +337,15 @@
     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
     {NULL}
   };
-  option_context_add_entries (context, entries,
-			      "view",
-			      "View options:",
-			      "Options controlling the output rendering",
-			      this);
+  parser->add_group (entries,
+		     "view",
+		     "View options:",
+		     "Options controlling the output rendering",
+		     this);
 }
 
 void
-shape_options_t::add_options (GOptionContext *context)
+shape_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
@@ -309,65 +356,149 @@
     {"features",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_features,	"Font features to apply to text",	"TODO"},
     {NULL}
   };
-  option_context_add_entries (context, entries,
-			      "shape",
-			      "Shape options:",
-			      "Options controlling the shaping process",
-			      this);
+  parser->add_group (entries,
+		     "shape",
+		     "Shape options:",
+		     "Options controlling the shaping process",
+		     this);
 }
 
 void
-font_options_t::add_options (GOptionContext *context)
+font_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
-    {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Face index (default: 0)",				"index"},
+    {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Font file-name",					"filename"},
+    {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Face index (default: 0)",                              "index"},
     {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
     {NULL}
   };
-  option_context_add_entries (context, entries,
-			      "font",
-			      "Font options:",
-			      "Options controlling the font",
-			      NULL);
+  parser->add_group (entries,
+		     "font",
+		     "Font options:",
+		     "Options controlling the font",
+		     this);
 }
 
 void
-parse_options (int argc, char *argv[])
+text_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
-    {"version",		0, G_OPTION_FLAG_NO_ARG,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			NULL},
-    {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	NULL},
-    {"output",		0, 0, G_OPTION_ARG_STRING,	&out_file,			"Set output file name",			"filename"},
+    {"text",		0, 0, G_OPTION_ARG_STRING,	&this->text,			"Set input text",			"string"},
+    {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name",		"filename"},
     {NULL}
   };
-  GError *parse_error = NULL;
-  GOptionContext *context;
+  parser->add_group (entries,
+		     "text",
+		     "Text options:",
+		     "Options controlling the input text",
+		     this);
+}
 
-  context = g_option_context_new ("- FONT-FILE TEXT");
-
-  g_option_context_add_main_entries (context, entries, NULL);
-  view_opts->add_options (context);
-  shape_opts->add_options (context);
-  font_opts->add_options (context);
-
-  if (!g_option_context_parse (context, &argc, &argv, &parse_error))
+void
+output_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
   {
-    if (parse_error != NULL)
-      fail ("%s", parse_error->message);
-    else
-      fail ("Option parse error");
-    exit(1);
-  }
-  g_option_context_free(context);
+    {"output",		0, 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
+    {"format",		0, 0, G_OPTION_ARG_STRING,	&this->output_format,		"Set output format",			"format"},
+    {NULL}
+  };
+  parser->add_group (entries,
+		     "output",
+		     "Output options:",
+		     "Options controlling the output",
+		     this);
+}
 
-  if (argc != 3) {
-    g_printerr ("Usage: %s [OPTION...] FONT-FILE TEXT\n", g_get_prgname ());
-    exit (1);
+
+
+hb_font_t *
+font_options_t::get_font (void) const
+{
+  if (font)
+    return font;
+
+  hb_blob_t *blob = NULL;
+
+  /* Create the blob */
+  {
+    const char *font_data;
+    unsigned int len;
+    hb_destroy_func_t destroy;
+    void *user_data;
+    hb_memory_mode_t mm;
+
+    if (!font_file)
+      fail (TRUE, "No font file set");
+
+    GMappedFile *mf = g_mapped_file_new (font_file, FALSE, NULL);
+    if (!mf)
+      fail (FALSE, "Failed opening font file `%s'", font_file);
+    font_data = g_mapped_file_get_contents (mf);
+    len = g_mapped_file_get_length (mf);
+    destroy = (hb_destroy_func_t) g_mapped_file_unref;
+    user_data = (void *) mf;
+    mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+
+    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
   }
 
-  font_opts->font_file = argv[1];
-  text = argv[2];
+  /* Create the face */
+  hb_face_t *face = hb_face_create (blob, face_index);
+  hb_blob_destroy (blob);
+
+
+  font = hb_font_create (face);
+
+  unsigned int upem = hb_face_get_upem (face);
+  hb_font_set_scale (font, font_size * upem, font_size * upem);
+  hb_face_destroy (face);
+
+#if HAVE_FREETYPE
+  hb_ft_font_set_funcs (font);
+#endif
+
+  return font;
+}
+
+
+const char *
+text_options_t::get_line (unsigned int *len)
+{
+  if (!text) {
+    if (!text_file)
+      fail (TRUE, "At least one of text or text-file must be set");
+
+    GMappedFile *mf = g_mapped_file_new (text_file, FALSE, NULL);
+    if (!mf)
+      fail (FALSE, "Failed opening text file `%s'", text_file);
+    text = g_mapped_file_get_contents (mf);
+    text_len = g_mapped_file_get_length (mf);
+  }
+
+  if (text_len == (unsigned int) -1)
+    text_len = strlen (text);
+
+  if (!text_len) {
+    *len = 0;
+    return NULL;
+  }
+
+  const char *ret = text;
+  const char *p = (const char *) memchr (text, '\n', text_len);
+  unsigned int ret_len;
+  if (!p) {
+    ret_len = text_len;
+    text += ret_len;
+    text_len = 0;
+  } else {
+    ret_len = p - ret;
+    text += ret_len + 1;
+    text_len -= ret_len + 1;
+  }
+
+  *len = ret_len;
+  return ret;
 }