Hidetaka Iwai
tyuyu****@sings*****
2004年 9月 23日 (木) 22:33:41 JST
岩井@札幌 です。 Takuro Ashie <ashie****@homa*****> wrote: Message-ID: <20040****@homa*****> > > GLibに新たに追加されたGOptionはどうでしょう? > > 全然コード見てませんが。 > > おお、言われて気がつきました。 > > でもさすがにglib-2.4にも入っていないAPIを今から使うのは... > またまたバックポート? というわけで、バックポートしてみました。かなり頭悪いコードですが(特に src/main.c の process_args() の辺り)、一応問題なく動作すると思います。 ついでに goption の方もいくつか Bug fix してあります。 # goption の Bug ってどこに報告すれば良いのかな... -- Hidetaka Iwai tyuyu****@sings***** -------------- next part -------------- diff -urN kazehakase/configure.in kazehakase-comp2/configure.in --- kazehakase/configure.in 2004-09-20 05:41:30.000000000 +0900 +++ kazehakase-comp2/configure.in 2004-09-20 05:47:24.000000000 +0900 @@ -241,6 +241,7 @@ src/sidebar/Makefile src/widget/Makefile src/gtk24backports/Makefile +src/glibbackports/Makefile src/libegg/Makefile src/libegg/dropdowntoolbutton/Makefile src/libegg/md5/Makefile diff -urN kazehakase/src/Makefile.am kazehakase-comp2/src/Makefile.am --- kazehakase/src/Makefile.am 2004-09-20 05:41:30.000000000 +0900 +++ kazehakase-comp2/src/Makefile.am 2004-09-23 13:05:40.000000000 +0900 @@ -1,7 +1,7 @@ # -*- Mode: Makefile; tab-width: 8; indent-tabs-mode: t; -*- SUBDIRS = utils net mozilla actions bookmarks dialogs \ - widget prefs_ui sidebar gtk24backports libegg + widget prefs_ui sidebar gtk24backports glibbackports libegg CPPFLAGS = \ @@ -20,6 +20,7 @@ -I$(top_srcdir)/src/actions \ -I$(top_srcdir)/src/bookmarks \ -I$(top_srcdir)/src/gtk24backports \ + -I$(top_srcdir)/src/glibbackports \ -I$(top_srcdir)/src/mozilla \ -I$(top_srcdir)/src/libegg/pixbufthumbnail \ -I$(top_srcdir)/src/net \ @@ -78,6 +79,7 @@ $(top_builddir)/src/sidebar/libkzsidebar.la \ $(top_builddir)/src/widget/libkzwidget.la \ $(top_builddir)/src/gtk24backports/libgtk24backports.la \ + $(top_builddir)/src/glibbackports/libglibbackports.la \ $(top_builddir)/src/libegg/dropdowntoolbutton/libeggdropdowntoolbutton.la \ $(top_builddir)/src/libegg/pixbufthumbnail/libeggpixbufthumbnail.la \ $(top_builddir)/src/libegg/regex/libeggregex.la diff -urN kazehakase/src/glibbackports/Makefile.am kazehakase-comp2/src/glibbackports/Makefile.am --- kazehakase/src/glibbackports/Makefile.am 1970-01-01 09:00:00.000000000 +0900 +++ kazehakase-comp2/src/glibbackports/Makefile.am 2004-09-20 05:50:32.000000000 +0900 @@ -0,0 +1,14 @@ +# -*- Mode: Makefile; tab-width: 8; indent-tabs-mode: t; -*- + +noinst_LTLIBRARIES = libglibbackports.la + +INCLUDES = \ + $(GTK_CFLAGS) \ + -I$(includedir) + +libglibbackports_la_SOURCES = \ + goption.c \ + goption.h + +libglibbackports_la_LIBADD = \ + $(GTK_LIBS) diff -urN kazehakase/src/glibbackports/goption.c kazehakase-comp2/src/glibbackports/goption.c --- kazehakase/src/glibbackports/goption.c 1970-01-01 09:00:00.000000000 +0900 +++ kazehakase-comp2/src/glibbackports/goption.c 2004-09-23 22:06:25.000000000 +0900 @@ -0,0 +1,1217 @@ +/* goption.c - Option parser + * + * Copyright (C) 1999, 2003 Red Hat Software + * Copyright (C) 2004 Anders Carlsson <ander****@gnome*****> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "goption.h" + +#include <glib.h> +#include <glib/gi18n.h> + +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#define TRANSLATE(group, str) (((group)->translate_func ? (* (group)->translate_func) ((str), (group)->translate_data) : (str))) + +typedef struct { + GOptionArg arg_type; + gpointer arg_data; + union { + gboolean bool; + gint integer; + gchar *str; + gchar **array; + } prev; + union { + gchar *str; + struct { + gint len; + gchar **data; + } array; + } allocated; +} Change; + +typedef struct +{ + gchar **ptr; + gchar *value; +} PendingNull; + +struct _GOptionContext +{ + GList *groups; + + gchar *parameter_string; + + gboolean help_enabled; + gboolean ignore_unknown; + + GOptionGroup *main_group; + + /* We keep a list of change so we can revert them */ + GList *changes; + + /* We also keep track of all argv elements that should be NULLed or + * modified. + */ + GList *pending_nulls; +}; + +struct _GOptionGroup +{ + gchar *name; + gchar *description; + gchar *help_description; + + GDestroyNotify destroy_notify; + gpointer user_data; + + GTranslateFunc translate_func; + GDestroyNotify translate_notify; + gpointer translate_data; + + GOptionEntry *entries; + gint n_entries; + + GOptionParseFunc pre_parse_func; + GOptionParseFunc post_parse_func; + GOptionErrorFunc error_func; +}; + +static void free_changes_list (GOptionContext *context, + gboolean revert); +static void free_pending_nulls (GOptionContext *context, + gboolean perform_nulls); + +GQuark +g_option_context_error_quark (void) +{ + static GQuark q = 0; + + if (q == 0) + q = g_quark_from_static_string ("g-option-context-error-quark"); + + return q; +} + +GOptionContext * +g_option_context_new (const gchar *parameter_string) + +{ + GOptionContext *context; + + context = g_new0 (GOptionContext, 1); + + context->parameter_string = g_strdup (parameter_string); + context->help_enabled = TRUE; + + return context; +} + +void +g_option_context_free (GOptionContext *context) +{ + g_return_if_fail (context != NULL); + + g_list_foreach (context->groups, (GFunc)g_option_group_free, NULL); + g_list_free (context->groups); + + if (context->main_group) + g_option_group_free (context->main_group); + + free_changes_list (context, FALSE); + free_pending_nulls (context, FALSE); + + g_free (context->parameter_string); + + g_free (context); +} + +void +g_option_context_set_help_enabled (GOptionContext *context, + gboolean help_enabled) + +{ + g_return_if_fail (context != NULL); + + context->help_enabled = help_enabled; +} + +gboolean +g_option_context_get_help_enabled (GOptionContext *context) +{ + g_return_val_if_fail (context != NULL, FALSE); + + return context->help_enabled; +} + +void +g_option_context_set_ignore_unknown_options (GOptionContext *context, + gboolean ignore_unknown) +{ + g_return_if_fail (context != NULL); + + context->ignore_unknown = ignore_unknown; +} + +gboolean +g_option_context_get_ignore_unknown_options (GOptionContext *context) +{ + g_return_val_if_fail (context != NULL, FALSE); + + return context->ignore_unknown; +} + +void +g_option_context_add_group (GOptionContext *context, + GOptionGroup *group) +{ + g_return_if_fail (context != NULL); + g_return_if_fail (group != NULL); + g_return_if_fail (group->name != NULL); + g_return_if_fail (group->description != NULL); + g_return_if_fail (group->help_description != NULL); + + context->groups = g_list_prepend (context->groups, group); +} + +void +g_option_context_set_main_group (GOptionContext *context, + GOptionGroup *group) +{ + g_return_if_fail (context != NULL); + g_return_if_fail (group != NULL); + + context->main_group = group; +} + +GOptionGroup * +g_option_context_get_main_group (GOptionContext *context) +{ + g_return_val_if_fail (context != NULL, NULL); + + return context->main_group; +} + +void +g_option_context_add_main_entries (GOptionContext *context, + const GOptionEntry *entries, + const gchar *translation_domain) +{ + g_return_if_fail (entries != NULL); + + if (!context->main_group) + context->main_group = g_option_group_new (NULL, NULL, NULL, NULL, NULL); + + g_option_group_add_entries (context->main_group, entries); + g_option_group_set_translation_domain (context->main_group, translation_domain); +} + +static void +print_entry (GOptionGroup *group, + gint max_length, + const GOptionEntry *entry) +{ + GString *str; + + if (entry->flags & G_OPTION_FLAG_HIDDEN) + return; + + str = g_string_new (NULL); + + if (entry->short_name) + g_string_append_printf (str, " -%c, --%s", entry->short_name, entry->long_name); + else + g_string_append_printf (str, " --%s", entry->long_name); + + if (entry->arg_description) + g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description)); + + g_print ("%-*s %s\n", max_length + 4, str->str, + entry->description ? TRANSLATE (group, entry->description) : ""); + g_string_free (str, TRUE); +} + +static void +print_help (GOptionContext *context, + gboolean main_help, + GOptionGroup *group) +{ + GList *list; + gint max_length, len; + gint i; + + g_print ("%s\n %s %s %s\n\n", + _("Usage:"), g_get_prgname(), _("[OPTION...]"), + context->parameter_string ? context->parameter_string : ""); + + list = context->groups; + + max_length = g_utf8_strlen ("--help, -?", -1); + + if (list) + { + len = g_utf8_strlen ("--help-all", -1); + max_length = MAX (max_length, len); + } + + while (list != NULL) + { + GOptionGroup *group = list->data; + + /* First, we check the --help-<groupname> options */ + len = g_utf8_strlen ("--help-", -1) + g_utf8_strlen (group->name, -1); + max_length = MAX (max_length, len); + + /* Then we go through the entries */ + for (i = 0; i < group->n_entries; i++) + { + len = g_utf8_strlen (group->entries[i].long_name, -1); + + if (group->entries[i].short_name) + len += 4; + + if (group->entries[i].arg != G_OPTION_ARG_NONE && + group->entries[i].arg_description) + len += 1 + g_utf8_strlen (TRANSLATE (group, group->entries[i].arg_description), -1); + + max_length = MAX (max_length, len); + } + + list = list->next; + } + + /* Add a bit of padding */ + max_length += 4; + + list = context->groups; + + g_print ("%s\n --%-*s %s\n", + _("Help Options:"), max_length, "help", _("Show help options")); + + /* We only want --help-all when there are groups */ + if (list) + g_print (" --%-*s %s\n", max_length, "help-all", _("Show all help options")); + + while (list) + { + GOptionGroup *group = list->data; + + g_print (" --help-%-*s %s\n", max_length - 5, group->name, TRANSLATE (group, group->help_description)); + + list = list->next; + } + + g_print ("\n"); + + if (group) + { + /* Print a certain group */ + + g_print ("%s\n", TRANSLATE (group, group->description)); + for (i = 0; i < group->n_entries; i++) + print_entry (group, max_length, &group->entries[i]); + g_print ("\n"); + } + else if (!main_help) + { + /* Print all groups */ + + list = context->groups; + + while (list) + { + GOptionGroup *group = list->data; + + g_print ("%s\n", group->description); + + for (i = 0; i < group->n_entries; i++) + if (!(group->entries[i].flags & G_OPTION_FLAG_IN_MAIN)) + print_entry (group, max_length, &group->entries[i]); + + g_print ("\n"); + list = list->next; + } + } + + /* Print application options if --help or --help-all has been specified */ + if (main_help || !group) + { + list = context->groups; + + g_print ("%s\n", _("Application Options:")); + + if (context->main_group) + for (i = 0; i < context->main_group->n_entries; i++) + print_entry (context->main_group, max_length, &context->main_group->entries[i]); + + while (list != NULL) + { + GOptionGroup *group = list->data; + + /* Print main entries from other groups */ + for (i = 0; i < group->n_entries; i++) + if (group->entries[i].flags & G_OPTION_FLAG_IN_MAIN) + print_entry (group, max_length, &group->entries[i]); + + list = list->next; + } + + g_print ("\n"); + } + + + exit (0); +} + +static gboolean +parse_int (const gchar *arg_name, + const gchar *arg, + gint *result, + GError **error) +{ + gchar *end; + glong tmp = strtol (arg, &end, 0); + + errno = 0; + + if (*arg == '\0' || *end != '\0') + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Cannot parse integer value '%s' for --%s"), + arg, arg_name); + return FALSE; + } + + *result = tmp; + if (*result != tmp || errno == ERANGE) + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Integer value '%s' for %s out of range"), + arg, arg_name); + return FALSE; + } + + return TRUE; +} + +static Change * +get_change (GOptionContext *context, + GOptionArg arg_type, + gpointer arg_data) +{ + GList *list; + Change *change = NULL; + + for (list = context->changes; list != NULL; list = list->next) + { + change = list->data; + + if (change->arg_data == arg_data) + break; + } + + if (!change) + { + change = g_new0 (Change, 1); + change->arg_type = arg_type; + change->arg_data = arg_data; + + context->changes = g_list_prepend (context->changes, change); + } + + return change; +} + +static void +add_pending_null (GOptionContext *context, + gchar **ptr, + gchar *value) +{ + PendingNull *n; + + n = g_new0 (PendingNull, 1); + n->ptr = ptr; + n->value = value; + + context->pending_nulls = g_list_prepend (context->pending_nulls, n); +} + +static gboolean +parse_arg (GOptionContext *context, + GOptionGroup *group, + GOptionEntry *entry, + const gchar *value, + const gchar *option_name, + GError **error) + +{ + Change *change; + + switch (entry->arg) + { + case G_OPTION_ARG_NONE: + { + change = get_change (context, G_OPTION_ARG_NONE, + entry->arg_data); + + *(gboolean *)entry->arg_data = TRUE; + break; + } + case G_OPTION_ARG_STRING: + { + gchar *data; + + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + + if (!data) + return FALSE; + + change = get_change (context, G_OPTION_ARG_STRING, + entry->arg_data); + g_free (change->allocated.str); + + change->prev.str = *(gchar **)entry->arg_data; + change->allocated.str = data; + + *(gchar **)entry->arg_data = data; + break; + } + case G_OPTION_ARG_STRING_ARRAY: + { + gchar *data; + + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + + if (!data) + return FALSE; + + change = get_change (context, G_OPTION_ARG_STRING_ARRAY, + entry->arg_data); + + if (change->allocated.array.len == 0) + { + change->prev.array = entry->arg_data; + change->allocated.array.data = g_new (gchar *, 2); + } + else + change->allocated.array.data = + g_renew (gchar *, change->allocated.array.data, + change->allocated.array.len + 2); + + change->allocated.array.data[change->allocated.array.len] = data; + change->allocated.array.data[change->allocated.array.len + 1] = NULL; + + change->allocated.array.len ++; + + *(gchar ***)entry->arg_data = change->allocated.array.data; + + break; + } + + case G_OPTION_ARG_FILENAME: + { + gchar *data; + + data = g_strdup (value); + + change = get_change (context, G_OPTION_ARG_FILENAME, + entry->arg_data); + g_free (change->allocated.str); + + change->prev.str = *(gchar **)entry->arg_data; + change->allocated.str = data; + + break; + } + + case G_OPTION_ARG_FILENAME_ARRAY: + { + gchar *data; + + data = g_strdup (value); + + change = get_change (context, G_OPTION_ARG_STRING_ARRAY, + entry->arg_data); + + if (change->allocated.array.len == 0) + { + change->prev.array = entry->arg_data; + change->allocated.array.data = g_new (gchar *, 2); + } + else + change->allocated.array.data = + g_renew (gchar *, change->allocated.array.data, + change->allocated.array.len + 2); + + change->allocated.array.data[change->allocated.array.len] = data; + change->allocated.array.data[change->allocated.array.len + 1] = NULL; + + change->allocated.array.len ++; + + *(gchar ***)entry->arg_data = change->allocated.array.data; + + break; + } + + case G_OPTION_ARG_INT: + { + gint data; + + if (!parse_int (option_name, value, + &data, + error)) + return FALSE; + + change = get_change (context, G_OPTION_ARG_INT, + entry->arg_data); + change->prev.integer = *(gint *)entry->arg_data; + *(gint *)entry->arg_data = data; + } + break; + case G_OPTION_ARG_CALLBACK: + { + gchar *tmp; + gboolean retval; + + tmp = g_locale_to_utf8 (value, -1, NULL, NULL, error); + + if (!value) + return FALSE; + + retval = (* (GOptionArgFunc) entry->arg_data) (option_name, tmp, group->user_data, error); + + g_free (tmp); + + return retval; + + break; + } + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +parse_short_option (GOptionContext *context, + GOptionGroup *group, + gint index, + gint *new_index, + gchar arg, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) +{ + gint j; + + for (j = 0; j < group->n_entries; j++) + { + if (arg == group->entries[j].short_name) + { + if (group->entries[j].arg == G_OPTION_ARG_NONE) + { + parse_arg (context, group, &group->entries[j], + NULL, NULL, error); + *parsed = TRUE; + } + else + { + gchar *value = NULL; + gchar *option_name; + + if (*new_index > index) + { + g_warning ("FIXME: figure out the correct error here"); + + return FALSE; + } + + if (index < *argc - 1) + { + value = (*argv)[index + 1]; + add_pending_null (context, &((*argv)[index + 1]), NULL); + *new_index = index + 1; + } + else + value = ""; + + + option_name = g_strdup_printf ("-%c", group->entries[j].short_name); + + if (!parse_arg (context, group, &group->entries[j], value, option_name, error)) + { + g_free (option_name); + return FALSE; + } + + g_free (option_name); + *parsed = TRUE; + } + } + } + + return TRUE; +} + +static gboolean +parse_long_option (GOptionContext *context, + GOptionGroup *group, + gint *index, + gchar *arg, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) +{ + gint j; + + for (j = 0; j < group->n_entries; j++) + { + if (*index >= *argc) + return TRUE; + + if (group->entries[j].arg == G_OPTION_ARG_NONE && + strcmp (arg, group->entries[j].long_name) == 0) + { + parse_arg (context, group, &group->entries[j], + NULL, NULL, error); + + add_pending_null (context, &((*argv)[*index]), NULL); + /* XXX: Is this correct? */ + *parsed = TRUE; + } + else + { + gint len = strlen (group->entries[j].long_name); + + if (strncmp (arg, group->entries[j].long_name, len) == 0 && + (arg[len] == '=' || arg[len] == 0)) + { + gchar *value = NULL; + gchar *option_name; + + add_pending_null (context, &((*argv)[*index]), NULL); + + if (arg[len] == '=') + value = arg + len + 1; + else if (*index < *argc - 1) + { + value = (*argv)[*index + 1]; + add_pending_null (context, &((*argv)[*index + 1]), NULL); + (*index)++; + } + else + value = ""; + + option_name = g_strconcat ("--", group->entries[j].long_name, NULL); + + if (!parse_arg (context, group, &group->entries[j], value, option_name, error)) + { + g_free (option_name); + return FALSE; + } + + g_free (option_name); + *parsed = TRUE; + } + } + } + + return TRUE; +} + +static void +free_changes_list (GOptionContext *context, + gboolean revert) +{ + GList *list; + + for (list = context->changes; list != NULL; list = list->next) + { + Change *change = list->data; + + if (revert) + { + /* Is this correct? */ + switch (change->arg_type) + { + case G_OPTION_ARG_NONE: + *(gboolean *)change->arg_data = change->prev.bool; + break; + case G_OPTION_ARG_INT: + *(gint *)change->arg_data = change->prev.integer; + break; + case G_OPTION_ARG_STRING: + case G_OPTION_ARG_FILENAME: + g_free (change->allocated.str); + *(gchar **)change->arg_data = change->prev.str; + break; + case G_OPTION_ARG_STRING_ARRAY: + case G_OPTION_ARG_FILENAME_ARRAY: + g_strfreev (change->allocated.array.data); + *(gchar ***)change->arg_data = change->prev.array; + break; + default: + g_assert_not_reached (); + } + } + + g_free (change); + } + + g_list_free (context->changes); + context->changes = NULL; +} + +static void +free_pending_nulls (GOptionContext *context, + gboolean perform_nulls) +{ + GList *list; + + for (list = context->pending_nulls; list != NULL; list = list->next) + { + PendingNull *n = list->data; + + if (perform_nulls) + { + if (n->value) + { + /* Copy back the short options */ + *(n->ptr)[0] = '-'; + strcpy (*n->ptr + 1, n->value); + } + else + *n->ptr = NULL; + } + + g_free (n->value); + g_free (n); + } + + g_list_free (context->pending_nulls); + context->pending_nulls = NULL; +} + +gboolean +g_option_context_parse (GOptionContext *context, + gint *argc, + gchar ***argv, + GError **error) +{ + gint i, j, k; + GList *list; + + /* Set program name */ + if (argc && argv) + { + gchar *prgname; + + prgname = strrchr ((*argv)[0], G_DIR_SEPARATOR); + if (prgname) + prgname++; + else + prgname = (*argv)[0]; + + g_set_prgname (prgname); + } + else + { + g_set_prgname ("<unknown>"); + } + + /* Call pre-parse hooks */ + list = context->groups; + while (list) + { + GOptionGroup *group = list->data; + + if (group->pre_parse_func) + { + if (!(* group->pre_parse_func) (context, group, + group->user_data, error)) + goto fail; + } + + list = list->next; + } + + if (context->main_group && context->main_group->pre_parse_func) + { + if (!(* context->main_group->pre_parse_func) (context, context->main_group, + context->main_group->user_data, error)) + goto fail; + } + + if (argc && argv) + { + for (i = 1; i < *argc; i++) + { + gchar *arg; + gboolean parsed = FALSE; + + if ((*argv)[i][0] == '-') + { + if ((*argv)[i][1] == '-') + { + /* -- option */ + + arg = (*argv)[i] + 2; + + /* '--' terminates list of arguments */ + if (*arg == 0) + { + add_pending_null (context, &((*argv)[i]), NULL); + break; + } + + /* Handle help options */ + if (context->help_enabled) + { + if (strcmp (arg, "help") == 0) + print_help (context, TRUE, NULL); + else if (strcmp (arg, "help-all") == 0) + print_help (context, FALSE, NULL); + else if (strncmp (arg, "help-", 5) == 0) + { + GList *list; + + list = context->groups; + + while (list) + { + GOptionGroup *group = list->data; + + if (strcmp (arg + 5, group->name) == 0) + print_help (context, FALSE, group); + + list = list->next; + } + } + } + + if (context->main_group && + !parse_long_option (context, context->main_group, &i, arg, + argc, argv, error, &parsed)) + goto fail; + + if (parsed) + continue; + + /* Try the groups */ + list = context->groups; + while (list) + { + GOptionGroup *group = list->data; + + if (!parse_long_option (context, group, &i, arg, + argc, argv, error, &parsed)) + goto fail; + + if (parsed) + break; + + list = list->next; + } + + if (context->ignore_unknown) + continue; + } + else + { + gint new_i, j; + gboolean *nulled_out = NULL; + + arg = (*argv)[i] + 1; + + new_i = i; + + if (context->ignore_unknown) + nulled_out = g_new0 (gboolean, strlen (arg)); + + for (j = 0; j < strlen (arg); j++) + { + parsed = FALSE; + + if (context->main_group && + !parse_short_option (context, context->main_group, + i, &new_i, arg[j], + argc, argv, error, &parsed)) + { + + g_free (nulled_out); + goto fail; + } + + if (!parsed) + { + /* Try the groups */ + list = context->groups; + while (list) + { + GOptionGroup *group = list->data; + + if (!parse_short_option (context, group, i, &new_i, arg[j], + argc, argv, error, &parsed)) + goto fail; + + if (parsed) + break; + + list = list->next; + } + } + + if (context->ignore_unknown) + { + if (parsed) + nulled_out[j] = TRUE; + else + continue; + } + + if (!parsed) + break; + } + + if (context->ignore_unknown) + { + gchar *new_arg = NULL; + gint arg_index = 0; + + for (j = 0; j < strlen (arg); j++) + { + if (!nulled_out[j]) + { + if (!new_arg) + new_arg = g_malloc (strlen (arg)); + new_arg[arg_index++] = arg[j]; + } + } + if (new_arg) + new_arg[arg_index] = '\0'; + + add_pending_null (context, &((*argv)[i]), new_arg); + } + else if (parsed) + { + add_pending_null (context, &((*argv)[i]), NULL); + i = new_i; + } + } + + if (!parsed && !context->ignore_unknown) + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION, + _("Unknown option %s"), (*argv)[i]); + goto fail; + } + } + } + + /* Call post-parse hooks */ + list = context->groups; + while (list) + { + GOptionGroup *group = list->data; + + if (group->post_parse_func) + { + if (!(* group->post_parse_func) (context, group, + group->user_data, error)) + goto fail; + } + + list = list->next; + } + + if (context->main_group && context->main_group->post_parse_func) + { + if (!(* context->main_group->post_parse_func) (context, context->main_group, + context->main_group->user_data, error)) + goto fail; + } + + free_pending_nulls (context, TRUE); + + for (i = 1; i < *argc; i++) + { + for (k = i; k < *argc; k++) + if ((*argv)[k] != NULL) + break; + + if (k > i) + { + k -= i; + for (j = i + k; j < *argc; j++) + (*argv)[j-k] = (*argv)[j]; + *argc -= k; + } + } + } + + return TRUE; + + fail: + + /* Call error hooks */ + list = context->groups; + while (list) + { + GOptionGroup *group = list->data; + + if (group->error_func) + (* group->error_func) (context, group, + group->user_data, error); + + list = list->next; + } + + if (context->main_group && context->main_group->error_func) + (* context->main_group->error_func) (context, context->main_group, + context->main_group->user_data, error); + + free_changes_list (context, TRUE); + free_pending_nulls (context, FALSE); + + return FALSE; +} + + +GOptionGroup * +g_option_group_new (const gchar *name, + const gchar *description, + const gchar *help_description, + gpointer user_data, + GDestroyNotify destroy) + +{ + GOptionGroup *group; + + group = g_new0 (GOptionGroup, 1); + group->name = g_strdup (name); + group->description = g_strdup (description); + group->help_description = g_strdup (help_description); + group->user_data = user_data; + group->destroy_notify = destroy; + + return group; +} + +void +g_option_group_free (GOptionGroup *group) +{ + g_return_if_fail (group != NULL); + + g_free (group->name); + g_free (group->description); + g_free (group->help_description); + + g_free (group->entries); + + if (group->destroy_notify) + (* group->destroy_notify) (group->user_data); + + if (group->translate_notify) + (* group->translate_notify) (group->translate_data); + + g_free (group); +} + + +void +g_option_group_add_entries (GOptionGroup *group, + const GOptionEntry *entries) +{ + gint n_entries; + + g_return_if_fail (entries != NULL); + + for (n_entries = 0; entries[n_entries].long_name != NULL; n_entries++); + + group->entries = g_renew (GOptionEntry, group->entries, group->n_entries + n_entries); + + memcpy (group->entries + group->n_entries, entries, sizeof (GOptionEntry) * n_entries); + + group->n_entries += n_entries; +} + +void +g_option_group_set_parse_hooks (GOptionGroup *group, + GOptionParseFunc pre_parse_func, + GOptionParseFunc post_parse_func) +{ + g_return_if_fail (group != NULL); + + group->pre_parse_func = pre_parse_func; + group->post_parse_func = post_parse_func; +} + +void +g_option_group_set_error_hook (GOptionGroup *group, + GOptionErrorFunc error_func) +{ + g_return_if_fail (group != NULL); + + group->error_func = error_func; +} + + +void +g_option_group_set_translate_func (GOptionGroup *group, + GTranslateFunc func, + gpointer data, + GDestroyNotify notify) +{ + g_return_if_fail (group != NULL); + + if (group->translate_notify) + group->translate_notify (group->translate_data); + + group->translate_func = func; + group->translate_data = data; + group->translate_notify = notify; +} + +static gchar * +dgettext_swapped (const gchar *msgid, + const gchar *domainname) +{ + return dgettext (domainname, msgid); +} + +void +g_option_group_set_translation_domain (GOptionGroup *group, + const gchar *domain) +{ + g_return_if_fail (group != NULL); + + g_option_group_set_translate_func (group, + (GTranslateFunc)dgettext_swapped, + g_strdup (domain), + g_free); +} + diff -urN kazehakase/src/glibbackports/goption.h kazehakase-comp2/src/glibbackports/goption.h --- kazehakase/src/glibbackports/goption.h 1970-01-01 09:00:00.000000000 +0900 +++ kazehakase-comp2/src/glibbackports/goption.h 2004-09-23 07:22:11.000000000 +0900 @@ -0,0 +1,143 @@ +/* goption.h - Option parser + * + * Copyright (C) 2004 Anders Carlsson <ander****@gnome*****> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_OPTION_H__ +#define __G_OPTION_H__ + +#include <glib/gerror.h> +#include <glib/gquark.h> + +typedef const gchar * (*GTranslateFunc) (const gchar *str, + gpointer data); + +G_BEGIN_DECLS + +typedef struct _GOptionContext GOptionContext; +typedef struct _GOptionGroup GOptionGroup; +typedef struct _GOptionEntry GOptionEntry; + +typedef enum +{ + G_OPTION_FLAG_HIDDEN = 1 << 0, + G_OPTION_FLAG_IN_MAIN = 1 << 1 +} GOptionFlags; + +typedef enum +{ + G_OPTION_ARG_NONE, + G_OPTION_ARG_STRING, + G_OPTION_ARG_INT, + G_OPTION_ARG_CALLBACK, + G_OPTION_ARG_FILENAME, + G_OPTION_ARG_STRING_ARRAY, + G_OPTION_ARG_FILENAME_ARRAY +} GOptionArg; + +typedef gboolean (*GOptionArgFunc) (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +typedef gboolean (*GOptionParseFunc) (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error); + +typedef void (*GOptionErrorFunc) (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error); + +#define G_OPTION_ERROR (g_option_context_error_quark ()) + +typedef enum +{ + G_OPTION_ERROR_UNKNOWN_OPTION, + G_OPTION_ERROR_BAD_VALUE, + G_OPTION_ERROR_FAILED +} GOptionError; + +GQuark g_option_context_error_quark (void) G_GNUC_CONST; + + +struct _GOptionEntry +{ + const gchar *long_name; + gchar short_name; + gint flags; + + GOptionArg arg; + gpointer arg_data; + + const gchar *description; + const gchar *arg_description; +}; + +GOptionContext *g_option_context_new (const gchar *parameter_string); +void g_option_context_free (GOptionContext *context); +void g_option_context_set_help_enabled (GOptionContext *context, + gboolean help_enabled); +gboolean g_option_context_get_help_enabled (GOptionContext *context); +void g_option_context_set_ignore_unknown_options (GOptionContext *context, + gboolean ignore_unknown); +gboolean g_option_context_get_ignore_unknown_options (GOptionContext *context); + +void g_option_context_add_main_entries (GOptionContext *context, + const GOptionEntry *entries, + const gchar *translation_domain); +gboolean g_option_context_parse (GOptionContext *context, + gint *argc, + gchar ***argv, + GError **error); + +void g_option_context_add_group (GOptionContext *context, + GOptionGroup *group); +void g_option_context_set_main_group (GOptionContext *context, + GOptionGroup *group); +GOptionGroup *g_option_context_get_main_group (GOptionContext *context); + + +GOptionGroup *g_option_group_new (const gchar *name, + const gchar *description, + const gchar *help_description, + gpointer user_data, + GDestroyNotify destroy); +void g_option_group_set_parse_hooks (GOptionGroup *group, + GOptionParseFunc pre_parse_func, + GOptionParseFunc post_parse_func); +void g_option_group_set_error_hook (GOptionGroup *group, + GOptionErrorFunc error_func); +void g_option_group_free (GOptionGroup *group); +void g_option_group_add_entries (GOptionGroup *group, + const GOptionEntry *entries); +void g_option_group_set_translate_func (GOptionGroup *group, + GTranslateFunc func, + gpointer data, + GDestroyNotify destroy_notify); +void g_option_group_set_translation_domain (GOptionGroup *group, + const gchar *domain); + + + + + +G_END_DECLS + +#endif /* __G_OPTION_H__ */ diff -urN kazehakase/src/main.c kazehakase-comp2/src/main.c --- kazehakase/src/main.c 2004-09-20 05:41:30.000000000 +0900 +++ kazehakase-comp2/src/main.c 2004-09-23 22:05:27.000000000 +0900 @@ -38,6 +38,7 @@ #include "utils.h" #include "estsearch.h" #include "mozilla.h" +#include "goption.h" #ifdef USE_GTK_2_2 #include "gtk24backports.h" @@ -50,16 +51,28 @@ #if USE_MIGEMO Migemo *migemo = NULL; #endif -static gchar *usage = -N_("Usage: kazehakase [OPTION...] [URI...]\n" -" -a, --action \"Action Name\" Execute action\n" -" -h, --help Show this help\n" -" -v, --version Show the version\n" -); + +static gchar** action_array; +static gchar* geometry_string; +static gboolean show_version; + +static GOptionEntry entries[] = { + { "action", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &action_array, "Execute action", NULL}, + { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry_string, " Position the window on the screen", NULL }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Show this version", NULL }, + { NULL } +}; + +static const gchar parameter_string[] = "URI ..."; + +static GOptionContext *context; +static GOptionGroup *main_group; gboolean exists_estindex = FALSE; gchar *estversion; +static void activate_action(GList *window_list, const gchar *text); + /****************************************************************************** * * * UI Level * @@ -148,6 +161,102 @@ return NULL; } +/****************************************************************************** + * * + * arguments parser * + * * + ******************************************************************************/ +/* split_string is taken from glib/tests/option-test.c */ + +static gchar ** +split_string (const char *str, int *argc) +{ + gchar **argv; + int len; + + argv = g_strsplit (str, "\n", -1); + + for (len = 0; argv[len] != NULL; len++); + + if (argc) + *argc = len; + + return argv; +} + +static gboolean +pre_parse_cb (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ +#if 0 + if(action_array) + { + /* free action_array before using */ + g_strfreev (action_array); + action_array = NULL; + } +#endif + return TRUE; +} + +static gboolean +post_parse_cb (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + /* XXX: Is there anything to do? */ + return TRUE; +} + +static void +parse_error_cb (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + fprintf (stderr, "%s\n", (*error)->message); + g_error_free (*error); + return; +} + +static void +free_goption() +{ + g_free (geometry_string); + g_strfreev (action_array); + g_option_context_free (context); +} + +/* XXX: This function has side effect! */ +static void +process_args(KzWindow* kz, int argc, char** argv) +{ + int i; + static int count = 0; + +#warning This function has side effect! + + if(action_array) + { + /* show actions */ + for(; action_array[count] != NULL; count++) + { + GList* list = kz_window_get_window_list(); + activate_action(list, action_array[count]); + } + } + + for(i = 1; i < argc; i++) + { + gchar *uri = complement_scheme(argv[i]); + kz_window_open_new_tab(kz, uri); + g_free(uri); + } + return; +} /****************************************************************************** * * @@ -319,31 +428,21 @@ { GList *list = kz_window_get_window_list(); KzWindow *kz; + gint argc; gchar **argv; - gint i; + gboolean retval; + GError *error = NULL; if (!list || !list->data || !KZ_IS_WINDOW(list->data)) return; kz = list->data; gdk_window_raise(GTK_WIDGET(kz)->window); /* open URL */ - argv = g_strsplit(data->data, "\n", -1); - for (i = 0; argv[i]; i++) - { - if ((!strcmp(argv[i], "--action") || - !strcmp(argv[i], "-a")) && - *argv[i+1] && - argv[i+1]) - { - activate_action(list, argv[i+1]); - i++; - continue; - } - - gchar *uri = complement_scheme(argv[i]); - kz_window_open_new_tab(kz, uri); - g_free(uri); - } + argv = split_string(data->data, &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + if(retval) + process_args(kz, argc, argv); g_strfreev(argv); } @@ -370,7 +469,7 @@ g_signal_connect (GTK_OBJECT(window), "selection-received", G_CALLBACK(cb_selection_received), window); - for (i = 1; i < argc; i++) + for (i = 0; i < argc; i++) { gint len; @@ -608,6 +707,8 @@ kz_session = NULL; kz_proxy = NULL; kz_bookmarks = NULL; + + free_goption(); } @@ -616,30 +717,43 @@ { KzWindow *kz = NULL; GtkWidget *dupl_check_server = NULL; - gint i; + gchar **argv_copy; + gint argc_copy = argc; + gboolean retval; + GError *error = NULL; - /* initialize */ + /* initialize */ setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); bind_textdomain_codeset(PACKAGE, "UTF-8"); textdomain(PACKAGE); + /* initialize goption */ + context = g_option_context_new (parameter_string); + g_option_context_add_main_entries (context, entries, NULL); + main_group = g_option_context_get_main_group (context); + + /* set hooks */ + g_option_group_set_parse_hooks (main_group, + pre_parse_cb, post_parse_cb); + g_option_group_set_error_hook (main_group, + parse_error_cb); + /* set to ignore unknown options */ + g_option_context_set_ignore_unknown_options (context, TRUE); + gtk_init(&argc, &argv); - for (i = 1; i < argc; i++) - { - if (!strncmp(argv[i], "--version", 9) || - !strncmp(argv[i], "-v", 2)) - { - g_print("%s %s\n", PACKAGE, VERSION); - return 0; - } - else if (!strncmp(argv[i], "--help", 6)|| - !strncmp(argv[i], "-h", 2)) - { - g_print(_(usage)); - return 0; - } + /* copy argv (ant argc) so as to preserve original arguments */ + argv_copy = g_strdupv(argv); + + /* g_option_context_parse changes argc_copy and argv_copy so as to + process remaining arguments */ + retval = g_option_context_parse (context, &argc_copy, &argv_copy, &error); + if(retval && show_version) + { + /* show version number and exit */ + g_print("%s %s\n", PACKAGE, VERSION); + exit(0); } #ifdef USE_GTK_2_2 @@ -663,46 +777,17 @@ * gtk_window_pargse_geometry() needs to be called * before gtk_widget_show(). */ - for (i = 1; i < argc; i++) + if(geometry_string) { - if (!strcmp(argv[i], "-geometry") && - *argv[i+1] && argv[i+1]) - { - gtk_window_parse_geometry(GTK_WINDOW(kz), - argv[i+1]); - i++; - continue; - } + gtk_window_parse_geometry(GTK_WINDOW(kz), + geometry_string); } gtk_widget_show(GTK_WIDGET(kz)); /* process arguments */ - for (i = 1; i < argc; i++) - { - gchar *uri; - if ((!strcmp(argv[i], "--action") || - !strcmp(argv[i], "-a")) && - *argv[i+1] && - argv[i+1]) - { - GList* list = kz_window_get_window_list(); - activate_action(list, argv[i+1]); - i++; - continue; - } - else if (!strcmp(argv[i], "-geometry") && - *argv[i+1] && argv[i+1]) - { - i++; - continue; - } - - /* open URL */ - uri = complement_scheme(argv[i]); - kz_window_open_new_tab(kz, uri); - g_free(uri); - } + process_args(kz, argc_copy, argv_copy); + g_strfreev (argv_copy); /* event loop */ gtk_main();