[util] Use post_parse to validate
diff --git a/util/face-options.hh b/util/face-options.hh
index 12cfd30..229c40e 100644
--- a/util/face-options.hh
+++ b/util/face-options.hh
@@ -31,8 +31,20 @@
 
 struct face_options_t
 {
+  ~face_options_t ()
+  {
+    g_free (font_file);
+  }
+
   void add_options (option_parser_t *parser);
 
+  void post_parse (GError **error)
+  {
+    if (!this->font_file)
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
+		   "No font file set");
+  }
+
   hb_blob_t *get_blob () const;
   hb_face_t *get_face () const;
 
@@ -40,12 +52,12 @@
   {
     ~cache_t ()
     {
-      free ((void *) font_path);
+      g_free (font_path);
       hb_blob_destroy (blob);
       hb_face_destroy (face);
     }
 
-    const char *font_path = nullptr;
+    char *font_path = nullptr;
     hb_blob_t *blob = nullptr;
     unsigned face_index = (unsigned) -1;
     hb_face_t *face = nullptr;
@@ -73,8 +85,7 @@
   if (face)
     return face;
 
-  if (!font_file)
-    fail (true, "No font file set");
+  assert (font_file);
 
   const char *font_path = font_file;
 
@@ -94,7 +105,7 @@
     cache.blob = hb_blob_create_from_file_or_fail (font_path);
 
     free ((char *) cache.font_path);
-    cache.font_path = strdup (font_path);
+    cache.font_path = g_strdup (font_path);
 
     if (!cache.blob)
       fail (false, "%s: Failed reading file", font_path);
diff --git a/util/font-options.hh b/util/font-options.hh
index 4584bfe..36bdd85 100644
--- a/util/font-options.hh
+++ b/util/font-options.hh
@@ -44,7 +44,6 @@
 {
   ~font_options_t ()
   {
-    g_free (font_file);
     free (variations);
     g_free (font_funcs);
     hb_font_destroy (font);
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index 6f18276..a2a4391 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -32,31 +32,12 @@
 /* main() body for utilities taking font and processing text.*/
 
 template <typename consumer_t, typename font_options_t, typename text_options_t>
-struct main_font_text_t : font_options_t, text_options_t, consumer_t
+struct main_font_text_t : option_parser_t, font_options_t, text_options_t, consumer_t
 {
-  void add_options (struct option_parser_t *parser)
+  int operator () (int argc, char **argv)
   {
-    font_options_t::add_options (parser);
-    text_options_t::add_options (parser);
-    consumer_t::add_options (parser);
-  }
-
-  int
-  operator () (int argc, char **argv)
-  {
-    option_parser_t options ("[FONT-FILE] [TEXT]");
-    add_options (&options);
-    options.parse (&argc, &argv);
-
-    argc--, argv++;
-    if (argc && !this->font_file) this->font_file = locale_to_utf8 (argv[0]), argc--, argv++;
-    if (argc && !this->text && !this->text_file) this->text = locale_to_utf8 (argv[0]), argc--, argv++;
-    if (argc)
-      fail (true, "Too many arguments on the command line");
-    if (!this->font_file)
-      options.usage ();
-    if (!this->text && !this->text_file)
-      this->text_file = g_strdup ("-");
+    add_options ();
+    parse (&argc, &argv);
 
     this->init (this);
 
@@ -69,6 +50,51 @@
 
     return this->failed ? 1 : 0;
   }
+
+  protected:
+
+  void add_options ()
+  {
+    font_options_t::add_options (this);
+    text_options_t::add_options (this);
+    consumer_t::add_options (this);
+
+    GOptionEntry entries[] =
+    {
+      {G_OPTION_REMAINING,	0, G_OPTION_FLAG_IN_MAIN,
+				G_OPTION_ARG_CALLBACK,	(gpointer) &collect_rest,	nullptr,	"[FONT-FILE] [TEXT]"},
+      {nullptr}
+    };
+    add_main_group (entries, this);
+    option_parser_t::add_options ();
+  }
+
+  private:
+
+  static gboolean
+  collect_rest (const char *name G_GNUC_UNUSED,
+		const char *arg,
+		gpointer    data,
+		GError    **error)
+  {
+    main_font_text_t *thiz = (main_font_text_t *) data;
+
+    if (!thiz->font_file)
+    {
+      thiz->font_file = g_strdup (arg);
+      return true;
+    }
+
+    if (!thiz->text && !thiz->text_file)
+    {
+      thiz->text = g_strdup (arg);
+      return true;
+    }
+
+    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
+		 "Too many arguments on the command line");
+    return false;
+  }
 };
 
 #endif
diff --git a/util/options.hh b/util/options.hh
index 6a72903..e9e82e7 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -70,25 +70,9 @@
   exit (1);
 }
 
-static inline char *
-locale_to_utf8 (char *s)
-{
-  char *t;
-  GError *error = nullptr;
-
-  t = g_locale_to_utf8 (s, -1, nullptr, nullptr, &error);
-  if (!t)
-  {
-     fail (true, "Failed converting text to UTF-8");
-  }
-
-  return t;
-}
-
-
 struct option_parser_t
 {
-  option_parser_t (const char *usage)
+  option_parser_t (const char *usage = nullptr)
   : usage_str (usage),
     context (g_option_context_new (usage)),
     to_free (g_ptr_array_new ())
@@ -136,6 +120,17 @@
     g_option_context_add_group (context, group);
   }
 
+  template <typename Type>
+  void add_main_group (GOptionEntry   *entries,
+		       Type           *closure)
+  {
+    GOptionGroup *group = g_option_group_new (nullptr, nullptr, nullptr,
+					      static_cast<gpointer>(closure), nullptr);
+    g_option_group_add_entries (group, entries);
+    g_option_group_set_parse_hooks (group, nullptr, post_parse<Type>);
+    g_option_context_set_main_group (context, group);
+  }
+
   void free_later (char *p) {
     g_ptr_array_add (to_free, p);
   }
@@ -147,7 +142,7 @@
     exit (1);
   }
 
-  private:
+  protected:
   const char *usage_str;
   GOptionContext *context;
   GPtrArray *to_free;
@@ -201,8 +196,6 @@
 inline void
 option_parser_t::parse (int *argc, char ***argv)
 {
-  add_options ();
-
   setlocale (LC_ALL, "");
 
   GError *parse_error = nullptr;
diff --git a/util/text-options.hh b/util/text-options.hh
index 83077ae..c9b04cb 100644
--- a/util/text-options.hh
+++ b/util/text-options.hh
@@ -17,6 +17,9 @@
 
   void post_parse (GError **error G_GNUC_UNUSED)
   {
+    if (!this->text && !this->text_file)
+      this->text_file = g_strdup ("-");
+
     if (text && text_file)
       g_set_error (error,
 		   G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,