Add check for if argc is 0.
[platform/upstream/glib.git] / glib / goption.c
1 /* goption.c - Option parser
2  *
3  *  Copyright (C) 1999, 2003 Red Hat Software
4  *  Copyright (C) 2004       Anders Carlsson <andersca@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "goption.h"
23
24 #include "galias.h"
25 #include "glib.h"
26 #include "gi18n.h"
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #define TRANSLATE(group, str) (((group)->translate_func ? (* (group)->translate_func) ((str), (group)->translate_data) : (str)))
33
34 typedef struct {
35   GOptionArg arg_type;
36   gpointer arg_data;  
37   union {
38     gboolean bool;
39     gint integer;
40     gchar *str;
41     gchar **array;
42   } prev;
43   union {
44     gchar *str;
45     struct {
46       gint len;
47       gchar **data;
48     } array;
49   } allocated;
50 } Change;
51
52 typedef struct
53 {
54   gchar **ptr;
55   gchar *value;
56 } PendingNull;
57
58 struct _GOptionContext
59 {
60   GList *groups;
61
62   gchar *parameter_string;
63
64   gboolean help_enabled;
65   gboolean ignore_unknown;
66   
67   GOptionGroup *main_group;
68
69   /* We keep a list of change so we can revert them */
70   GList *changes;
71   
72   /* We also keep track of all argv elements that should be NULLed or
73    * modified.
74    */
75   GList *pending_nulls;
76 };
77
78 struct _GOptionGroup
79 {
80   gchar *name;
81   gchar *description;
82   gchar *help_description;
83
84   GDestroyNotify  destroy_notify;
85   gpointer        user_data;
86
87   GTranslateFunc  translate_func;
88   GDestroyNotify  translate_notify;
89   gpointer        translate_data;
90
91   GOptionEntry *entries;
92   gint         n_entries;
93
94   GOptionParseFunc pre_parse_func;
95   GOptionParseFunc post_parse_func;
96   GOptionErrorFunc error_func;
97 };
98
99 static void free_changes_list (GOptionContext *context,
100                                gboolean        revert);
101 static void free_pending_nulls (GOptionContext *context,
102                                 gboolean        perform_nulls);
103
104 GQuark
105 g_option_error_quark (void)
106 {
107   static GQuark q = 0;
108   
109   if (q == 0)
110     q = g_quark_from_static_string ("g-option-context-error-quark");
111
112   return q;
113 }
114
115 GOptionContext *
116 g_option_context_new (const gchar *parameter_string)
117
118 {
119   GOptionContext *context;
120
121   context = g_new0 (GOptionContext, 1);
122
123   context->parameter_string = g_strdup (parameter_string);
124   context->help_enabled = TRUE;
125
126   return context;
127 }
128
129 void
130 g_option_context_free (GOptionContext *context)
131 {
132   g_return_if_fail (context != NULL);
133
134   g_list_foreach (context->groups, (GFunc)g_option_group_free, NULL);
135   g_list_free (context->groups);
136
137   if (context->main_group) 
138     g_option_group_free (context->main_group);
139
140   free_changes_list (context, FALSE);
141   free_pending_nulls (context, FALSE);
142   
143   g_free (context->parameter_string);
144   
145   g_free (context);
146 }
147
148 void
149 g_option_context_set_help_enabled (GOptionContext *context,
150                                    gboolean        help_enabled)
151
152 {
153   g_return_if_fail (context != NULL);
154
155   context->help_enabled = help_enabled;
156 }
157
158 gboolean
159 g_option_context_get_help_enabled (GOptionContext *context)
160 {
161   g_return_val_if_fail (context != NULL, FALSE);
162
163   return context->help_enabled;
164 }
165
166 void
167 g_option_context_set_ignore_unknown_options (GOptionContext *context,
168                                              gboolean        ignore_unknown)
169 {
170   g_return_if_fail (context != NULL);
171
172   context->ignore_unknown = ignore_unknown;
173 }
174
175 gboolean
176 g_option_context_get_ignore_unknown_options (GOptionContext *context)
177 {
178   g_return_val_if_fail (context != NULL, FALSE);
179
180   return context->ignore_unknown;
181 }
182
183 void
184 g_option_context_add_group (GOptionContext *context,
185                             GOptionGroup   *group)
186 {
187   g_return_if_fail (context != NULL);
188   g_return_if_fail (group != NULL);
189   g_return_if_fail (group->name != NULL);
190   g_return_if_fail (group->description != NULL);
191   g_return_if_fail (group->help_description != NULL);
192
193   context->groups = g_list_prepend (context->groups, group);
194 }
195
196 void
197 g_option_context_set_main_group (GOptionContext *context,
198                                  GOptionGroup   *group)
199 {
200   g_return_if_fail (context != NULL);
201   g_return_if_fail (group != NULL);
202
203   context->main_group = group;
204 }
205
206 GOptionGroup *
207 g_option_context_get_main_group (GOptionContext *context)
208 {
209   g_return_val_if_fail (context != NULL, NULL);
210
211   return context->main_group;
212 }
213
214 void
215 g_option_context_add_main_entries (GOptionContext      *context,
216                                    const GOptionEntry  *entries,
217                                    const gchar         *translation_domain)
218 {
219   g_return_if_fail (entries != NULL);
220
221   if (!context->main_group)
222     context->main_group = g_option_group_new (NULL, NULL, NULL, NULL, NULL);
223   
224   g_option_group_add_entries (context->main_group, entries);
225   g_option_group_set_translation_domain (context->main_group, translation_domain);
226 }
227
228 static void
229 print_entry (GOptionGroup       *group,
230              gint                max_length,
231              const GOptionEntry *entry)
232 {
233   GString *str;
234
235   if (entry->flags & G_OPTION_FLAG_HIDDEN)
236     return;
237           
238   str = g_string_new (NULL);
239   
240   if (entry->short_name)
241     g_string_append_printf (str, "  -%c, --%s", entry->short_name, entry->long_name);
242   else
243     g_string_append_printf (str, "  --%s", entry->long_name);
244   
245   if (entry->arg_description)
246     g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description));
247   
248   g_print ("%-*s %s\n", max_length + 4, str->str,
249            entry->description ? TRANSLATE (group, entry->description) : "");
250   g_string_free (str, TRUE);  
251 }
252
253 static void
254 print_help (GOptionContext *context,
255             gboolean        main_help,
256             GOptionGroup   *group)
257 {
258   GList *list;
259   gint max_length, len;
260   gint i;
261   
262   g_print ("%s\n  %s %s %s\n\n", 
263            _("Usage:"), g_get_prgname(), _("[OPTION...]"),
264            context->parameter_string ? context->parameter_string : "");
265
266   list = context->groups;
267
268   max_length = g_utf8_strlen ("--help, -?", -1);
269
270   if (list)
271     {
272       len = g_utf8_strlen ("--help-all", -1);
273       max_length = MAX (max_length, len);
274     }
275
276   while (list != NULL)
277     {
278       GOptionGroup *group = list->data;
279       
280       /* First, we check the --help-<groupname> options */
281       len = g_utf8_strlen ("--help-", -1) + g_utf8_strlen (group->name, -1);
282       max_length = MAX (max_length, len);
283
284       /* Then we go through the entries */
285       for (i = 0; i < group->n_entries; i++)
286         {
287           len = g_utf8_strlen (group->entries[i].long_name, -1);
288
289           if (group->entries[i].short_name)
290             len += 4;
291
292           if (group->entries[i].arg != G_OPTION_ARG_NONE &&
293               group->entries[i].arg_description)
294             len += 1 + g_utf8_strlen (TRANSLATE (group, group->entries[i].arg_description), -1);
295
296           max_length = MAX (max_length, len);
297         }
298       
299       list = list->next;
300     }
301
302   /* Add a bit of padding */
303   max_length += 4;
304   
305   list = context->groups;
306
307   g_print ("%s\n  --%-*s %s\n", 
308            _("Help Options:"), max_length, "help", _("Show help options"));
309
310   /* We only want --help-all when there are groups */
311   if (list)
312     g_print ("  --%-*s %s\n", max_length, "help-all", _("Show all help options"));
313
314   while (list)
315     {
316       GOptionGroup *group = list->data;
317
318       g_print ("  --help-%-*s %s\n", max_length - 5, group->name, TRANSLATE (group, group->help_description));
319       
320       list = list->next;
321     }
322
323   g_print ("\n");
324
325   if (group)
326     {
327       /* Print a certain group */
328       
329       g_print ("%s\n", TRANSLATE (group, group->description));
330       for (i = 0; i < group->n_entries; i++)
331         print_entry (group, max_length, &group->entries[i]);
332       g_print ("\n");
333     }
334   else if (!main_help)
335     {
336       /* Print all groups */
337
338       list = context->groups;
339
340       while (list)
341         {
342           GOptionGroup *group = list->data;
343
344           g_print ("%s\n", group->description);
345
346           for (i = 0; i < group->n_entries; i++)
347             if (!(group->entries[i].flags & G_OPTION_FLAG_IN_MAIN))
348               print_entry (group, max_length, &group->entries[i]);
349           
350           g_print ("\n");
351           list = list->next;
352         }
353     }
354   
355   /* Print application options if --help or --help-all has been specified */
356   if (main_help || !group)
357     {
358       list = context->groups;
359
360       g_print ("%s\n", _("Application Options:"));
361
362       if (context->main_group)
363         for (i = 0; i < context->main_group->n_entries; i++) 
364           print_entry (context->main_group, max_length, &context->main_group->entries[i]);
365
366       while (list != NULL)
367         {
368           GOptionGroup *group = list->data;
369
370           /* Print main entries from other groups */
371           for (i = 0; i < group->n_entries; i++)
372             if (group->entries[i].flags & G_OPTION_FLAG_IN_MAIN)
373               print_entry (group, max_length, &group->entries[i]);
374           
375           list = list->next;
376         }
377
378       g_print ("\n");
379     }
380
381   
382   exit (0);
383 }
384
385 static gboolean
386 parse_int (const gchar *arg_name,
387            const gchar *arg,
388            gint        *result,
389            GError     **error)
390 {
391   gchar *end;
392   glong tmp = strtol (arg, &end, 0);
393
394   errno = 0;
395   
396   if (*arg == '\0' || *end != '\0')
397     {
398       g_set_error (error,
399                    G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
400                    _("Cannot parse integer value '%s' for --%s"),
401                    arg, arg_name);
402       return FALSE;
403     }
404
405   *result = tmp;
406   if (*result != tmp || errno == ERANGE)
407     {
408       g_set_error (error,
409                    G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
410                    _("Integer value '%s' for %s out of range"),
411                    arg, arg_name);
412       return FALSE;
413     }
414
415   return TRUE;
416 }
417
418 static Change *
419 get_change (GOptionContext *context,
420             GOptionArg      arg_type,
421             gpointer        arg_data)
422 {
423   GList *list;
424   Change *change = NULL;
425   
426   for (list = context->changes; list != NULL; list = list->next)
427     {
428       change = list->data;
429
430       if (change->arg_data == arg_data)
431         break;
432     }
433
434   if (!change)
435     {
436       change = g_new0 (Change, 1);
437       change->arg_type = arg_type;
438       change->arg_data = arg_data;
439
440       context->changes = g_list_prepend (context->changes, change);
441     }
442   
443   return change;
444 }
445
446 static void
447 add_pending_null (GOptionContext *context,
448                   gchar         **ptr,
449                   gchar          *value)
450 {
451   PendingNull *n;
452
453   n = g_new0 (PendingNull, 1);
454   n->ptr = ptr;
455   n->value = value;
456
457   context->pending_nulls = g_list_prepend (context->pending_nulls, n);
458 }
459                   
460 static gboolean
461 parse_arg (GOptionContext *context,
462            GOptionGroup   *group,
463            GOptionEntry   *entry,
464            const gchar    *value,
465            const gchar    *option_name,
466            GError        **error)
467      
468 {
469   Change *change;
470   
471   switch (entry->arg)
472     {
473     case G_OPTION_ARG_NONE:
474       {
475         change = get_change (context, G_OPTION_ARG_NONE,
476                              entry->arg_data);
477
478         *(gboolean *)entry->arg_data = TRUE;
479         break;
480       }      
481     case G_OPTION_ARG_STRING:
482       {
483         gchar *data;
484
485         data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
486
487         if (!data)
488           return FALSE;
489
490         change = get_change (context, G_OPTION_ARG_STRING,
491                              entry->arg_data);
492         g_free (change->allocated.str);
493         
494         change->prev.str = *(gchar **)entry->arg_data;
495         change->allocated.str = data;
496         
497         *(gchar **)entry->arg_data = data;
498         break;
499       }
500     case G_OPTION_ARG_STRING_ARRAY:
501       {
502         gchar *data;
503         
504         data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
505
506         if (!data)
507           return FALSE;
508
509         change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
510                              entry->arg_data);
511
512         if (change->allocated.array.len == 0)
513           {
514             change->prev.array = entry->arg_data;
515             change->allocated.array.data = g_new (gchar *, 2);
516           }
517         else
518           change->allocated.array.data =
519             g_renew (gchar *, change->allocated.array.data,
520                      change->allocated.array.len + 2);
521
522         change->allocated.array.data[change->allocated.array.len] = data;
523         change->allocated.array.data[change->allocated.array.len + 1] = NULL;
524
525         change->allocated.array.len ++;
526
527         *(gchar ***)entry->arg_data = change->allocated.array.data;
528
529         break;
530       }
531       
532     case G_OPTION_ARG_FILENAME:
533       {
534         gchar *data;
535
536         data = g_strdup (value);
537
538         change = get_change (context, G_OPTION_ARG_FILENAME,
539                              entry->arg_data);
540         g_free (change->allocated.str);
541         
542         change->prev.str = *(gchar **)entry->arg_data;
543         change->allocated.str = data;
544
545         *(gchar **)entry->arg_data = data;
546         break;
547       }
548
549     case G_OPTION_ARG_FILENAME_ARRAY:
550       {
551         gchar *data;
552         
553         data = g_strdup (value);
554
555         change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
556                              entry->arg_data);
557
558         if (change->allocated.array.len == 0)
559           {
560             change->prev.array = entry->arg_data;
561             change->allocated.array.data = g_new (gchar *, 2);
562           }
563         else
564           change->allocated.array.data =
565             g_renew (gchar *, change->allocated.array.data,
566                      change->allocated.array.len + 2);
567
568         change->allocated.array.data[change->allocated.array.len] = data;
569         change->allocated.array.data[change->allocated.array.len + 1] = NULL;
570
571         change->allocated.array.len ++;
572
573         *(gchar ***)entry->arg_data = change->allocated.array.data;
574
575         break;
576       }
577       
578     case G_OPTION_ARG_INT:
579       {
580         gint data;
581
582         if (!parse_int (option_name, value,
583                         &data,
584                         error))
585           return FALSE;
586
587         change = get_change (context, G_OPTION_ARG_INT,
588                              entry->arg_data);
589         change->prev.integer = *(gint *)entry->arg_data;
590         *(gint *)entry->arg_data = data;
591         break;
592       }
593     case G_OPTION_ARG_CALLBACK:
594       {
595         gchar *tmp;
596         gboolean retval;
597         
598         tmp = g_locale_to_utf8 (value, -1, NULL, NULL, error);
599
600         if (!value)
601           return FALSE;
602
603         retval = (* (GOptionArgFunc) entry->arg_data) (option_name, tmp, group->user_data, error);
604         
605         g_free (tmp);
606         
607         return retval;
608         
609         break;
610       }
611     default:
612       g_assert_not_reached ();
613     }
614
615   return TRUE;
616 }
617
618 static gboolean
619 parse_short_option (GOptionContext *context,
620                     GOptionGroup   *group,
621                     gint            index,
622                     gint           *new_index,
623                     gchar           arg,
624                     gint           *argc,
625                     gchar        ***argv,
626                     GError        **error,
627                     gboolean       *parsed)
628 {
629   gint j;
630     
631   for (j = 0; j < group->n_entries; j++)
632     {
633       if (arg == group->entries[j].short_name)
634         {
635           if (group->entries[j].arg == G_OPTION_ARG_NONE)
636             {
637               parse_arg (context, group, &group->entries[j],
638                          NULL, NULL, error);
639               *parsed = TRUE;
640             }
641           else
642             {
643               gchar *value = NULL;
644               gchar *option_name;
645               
646               if (*new_index > index)
647                 {
648                   g_warning ("FIXME: figure out the correct error here");
649
650                   return FALSE;
651                 }
652               
653               if (index < *argc - 1)
654                 {
655                   value = (*argv)[index + 1];
656                   add_pending_null (context, &((*argv)[index + 1]), NULL);
657                   *new_index = index + 1;
658                 }
659               else
660                 value = "";
661
662
663               option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
664               
665               if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
666                 {
667                   g_free (option_name);
668                   return FALSE;
669                 }
670
671               g_free (option_name);
672               *parsed = TRUE;
673             }
674         }
675     }
676
677   return TRUE;
678 }
679
680 static gboolean
681 parse_long_option (GOptionContext *context,
682                    GOptionGroup   *group,
683                    gint           *index,
684                    gchar          *arg,
685                    gint           *argc,
686                    gchar        ***argv,
687                    GError        **error,
688                    gboolean       *parsed)
689 {
690   gint j;
691
692   for (j = 0; j < group->n_entries; j++)
693     {
694       if (*index >= *argc)
695         return TRUE;
696
697       if (group->entries[j].arg == G_OPTION_ARG_NONE &&
698           strcmp (arg, group->entries[j].long_name) == 0)
699         {
700           parse_arg (context, group, &group->entries[j],
701                      NULL, NULL, error);
702           
703           add_pending_null (context, &((*argv)[*index]), NULL);
704           *parsed = TRUE;
705         }
706       else
707         {
708           gint len = strlen (group->entries[j].long_name);
709           
710           if (strncmp (arg, group->entries[j].long_name, len) == 0 &&
711               (arg[len] == '=' || arg[len] == 0))
712             {
713               gchar *value = NULL;
714               gchar *option_name;
715
716               add_pending_null (context, &((*argv)[*index]), NULL);
717               
718               if (arg[len] == '=')
719                 value = arg + len + 1;
720               else if (*index < *argc - 1)
721                 {
722                   value = (*argv)[*index + 1];
723                   add_pending_null (context, &((*argv)[*index + 1]), NULL);
724                   (*index)++;
725                 }
726               else
727                 value = "";
728
729               option_name = g_strconcat ("--", group->entries[j].long_name, NULL);
730               
731               if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
732                 {
733                   g_free (option_name);
734                   return FALSE;
735                 }
736
737               g_free (option_name);
738               *parsed = TRUE;
739             }
740         }
741     }
742   
743   return TRUE;
744 }
745
746 static void
747 free_changes_list (GOptionContext *context,
748                    gboolean        revert)
749 {
750   GList *list;
751
752   for (list = context->changes; list != NULL; list = list->next)
753     {
754       Change *change = list->data;
755
756       if (revert)
757         {
758           switch (change->arg_type)
759             {
760             case G_OPTION_ARG_NONE:
761               *(gboolean *)change->arg_data = change->prev.bool;
762               break;
763             case G_OPTION_ARG_INT:
764               *(gint *)change->arg_data = change->prev.integer;
765               break;
766             case G_OPTION_ARG_STRING:
767             case G_OPTION_ARG_FILENAME:
768               g_free (change->allocated.str);
769               *(gchar **)change->arg_data = change->prev.str;
770               break;
771             case G_OPTION_ARG_STRING_ARRAY:
772             case G_OPTION_ARG_FILENAME_ARRAY:
773               g_strfreev (change->allocated.array.data);
774               *(gchar ***)change->arg_data = change->prev.array;
775               break;
776             default:
777               g_assert_not_reached ();
778             }
779         }
780       
781       g_free (change);
782     }
783
784   g_list_free (context->changes);
785   context->changes = NULL;
786 }
787
788 static void
789 free_pending_nulls (GOptionContext *context,
790                     gboolean        perform_nulls)
791 {
792   GList *list;
793
794   for (list = context->pending_nulls; list != NULL; list = list->next)
795     {
796       PendingNull *n = list->data;
797
798       if (perform_nulls)
799         {
800           if (n->value)
801             {
802               /* Copy back the short options */
803               *(n->ptr)[0] = '-';             
804               strcpy (*n->ptr + 1, n->value);
805             }
806           else
807             *n->ptr = NULL;
808         }
809       
810       g_free (n->value);
811       g_free (n);
812     }
813
814   g_list_free (context->pending_nulls);
815   context->pending_nulls = NULL;
816 }
817
818 gboolean
819 g_option_context_parse (GOptionContext   *context,
820                         gint             *argc,
821                         gchar          ***argv,
822                         GError          **error)
823 {
824   gint i, j, k;
825   GList *list;
826
827   /* Set program name */
828   if (argc && argv && *argc)
829     {
830       gchar *prgname;
831       
832       prgname = g_path_get_basename ((*argv)[0]);
833       g_set_prgname (prgname);
834       g_free (prgname);
835     }
836   else
837     {
838       g_set_prgname ("<unknown>");
839     }
840   
841   /* Call pre-parse hooks */
842   list = context->groups;
843   while (list)
844     {
845       GOptionGroup *group = list->data;
846       
847       if (group->pre_parse_func)
848         {
849           if (!(* group->pre_parse_func) (context, group,
850                                           group->user_data, error))
851             goto fail;
852         }
853       
854       list = list->next;
855     }
856
857   if (context->main_group && context->main_group->pre_parse_func)
858     {
859       if (!(* context->main_group->pre_parse_func) (context, context->main_group,
860                                                     context->main_group->user_data, error))
861         goto fail;
862     }
863
864   if (argc && argv)
865     {
866       for (i = 1; i < *argc; i++)
867         {
868           gchar *arg;
869           gboolean parsed = FALSE;
870
871           if ((*argv)[i][0] == '-')
872             {
873               if ((*argv)[i][1] == '-')
874                 {
875                   /* -- option */
876
877                   arg = (*argv)[i] + 2;
878
879                   /* '--' terminates list of arguments */
880                   if (*arg == 0)
881                     {
882                       add_pending_null (context, &((*argv)[i]), NULL);
883                       break;
884                     }
885
886                   /* Handle help options */
887                   if (context->help_enabled)
888                     {
889                       if (strcmp (arg, "help") == 0)
890                         print_help (context, TRUE, NULL);
891                       else if (strcmp (arg, "help-all") == 0)
892                         print_help (context, FALSE, NULL);                    
893                       else if (strncmp (arg, "help-", 5) == 0)
894                         {
895                           GList *list;
896                           
897                           list = context->groups;
898                           
899                           while (list)
900                             {
901                               GOptionGroup *group = list->data;
902                               
903                               if (strcmp (arg + 5, group->name) == 0)
904                                 print_help (context, FALSE, group);                                           
905                               
906                               list = list->next;
907                             }
908                         }
909                     }
910
911                   if (context->main_group &&
912                       !parse_long_option (context, context->main_group, &i, arg,
913                                           argc, argv, error, &parsed))
914                     goto fail;
915
916                   if (parsed)
917                     continue;
918                   
919                   /* Try the groups */
920                   list = context->groups;
921                   while (list)
922                     {
923                       GOptionGroup *group = list->data;
924                       
925                       if (!parse_long_option (context, group, &i, arg,
926                                               argc, argv, error, &parsed))
927                         goto fail;
928                       
929                       if (parsed)
930                         break;
931                       
932                       list = list->next;
933                     }
934
935                   if (context->ignore_unknown)
936                     continue;
937                 }
938               else
939                 {
940                   gint new_i, j;
941                   gboolean *nulled_out = NULL;
942                   
943                   arg = (*argv)[i] + 1;
944
945                   new_i = i;
946
947                   if (context->ignore_unknown)
948                     nulled_out = g_new0 (gboolean, strlen (arg));
949                   
950                   for (j = 0; j < strlen (arg); j++)
951                     {
952                       parsed = FALSE;
953                       
954                       if (context->main_group &&
955                           !parse_short_option (context, context->main_group,
956                                                i, &new_i, arg[j],
957                                                argc, argv, error, &parsed))
958                         {
959
960                           g_free (nulled_out);
961                           goto fail;
962                         }
963
964                       if (!parsed)
965                         {
966                           /* Try the groups */
967                           list = context->groups;
968                           while (list)
969                             {
970                               GOptionGroup *group = list->data;
971                               
972                               if (!parse_short_option (context, group, i, &new_i, arg[j],
973                                                        argc, argv, error, &parsed))
974                                 goto fail;
975                               
976                               if (parsed)
977                                 break;
978                           
979                               list = list->next;
980                             }
981                         }
982
983                       if (context->ignore_unknown)
984                         {
985                           if (parsed)
986                             nulled_out[j] = TRUE;
987                           else
988                             continue;
989                         }
990
991                       if (!parsed)
992                         break;
993                     }
994
995                   if (context->ignore_unknown)
996                     {
997                       gchar *new_arg = NULL; 
998                       gint arg_index = 0;
999                       
1000                       for (j = 0; j < strlen (arg); j++)
1001                         {
1002                           if (!nulled_out[j])
1003                             {
1004                               if (!new_arg)
1005                                 new_arg = g_malloc (strlen (arg));
1006                               new_arg[arg_index++] = arg[j];
1007                             }
1008                         }
1009                       if (new_arg)
1010                         new_arg[arg_index] = '\0';
1011                       
1012                       add_pending_null (context, &((*argv)[i]), new_arg);
1013                     }
1014                   else if (parsed)
1015                     {
1016                       add_pending_null (context, &((*argv)[i]), NULL);
1017                       i = new_i;
1018                     }
1019                 }
1020               
1021               if (!parsed && !context->ignore_unknown)
1022                 {
1023                   g_set_error (error,
1024                                G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION,
1025                                _("Unknown option %s"), (*argv)[i]);
1026                   goto fail;
1027                 }
1028             }
1029         }
1030
1031       /* Call post-parse hooks */
1032       list = context->groups;
1033       while (list)
1034         {
1035           GOptionGroup *group = list->data;
1036
1037           if (group->post_parse_func)
1038             {
1039               if (!(* group->post_parse_func) (context, group,
1040                                                group->user_data, error))
1041                 goto fail;
1042             }
1043           
1044           list = list->next;
1045         }
1046
1047       if (context->main_group && context->main_group->post_parse_func)
1048         {
1049           if (!(* context->main_group->post_parse_func) (context, context->main_group,
1050                                                          context->main_group->user_data, error))
1051             goto fail;
1052         }
1053
1054       free_pending_nulls (context, TRUE);
1055       
1056       for (i = 1; i < *argc; i++)
1057         {
1058           for (k = i; k < *argc; k++)
1059             if ((*argv)[k] != NULL)
1060               break;
1061           
1062           if (k > i)
1063             {
1064               k -= i;
1065               for (j = i + k; j < *argc; j++)
1066                 (*argv)[j-k] = (*argv)[j];
1067               *argc -= k;
1068             }
1069         }      
1070     }
1071
1072   return TRUE;
1073
1074  fail:
1075   
1076   /* Call error hooks */
1077   list = context->groups;
1078   while (list)
1079     {
1080       GOptionGroup *group = list->data;
1081       
1082       if (group->error_func)
1083         (* group->error_func) (context, group,
1084                                group->user_data, error);
1085       
1086       list = list->next;
1087     }
1088
1089   if (context->main_group && context->main_group->error_func)
1090     (* context->main_group->error_func) (context, context->main_group,
1091                                          context->main_group->user_data, error);
1092   
1093   free_changes_list (context, TRUE);
1094   free_pending_nulls (context, FALSE);
1095
1096   return FALSE;
1097 }
1098                                    
1099      
1100 GOptionGroup *
1101 g_option_group_new (const gchar    *name,
1102                     const gchar    *description,
1103                     const gchar    *help_description,
1104                     gpointer        user_data,
1105                     GDestroyNotify  destroy)
1106
1107 {
1108   GOptionGroup *group;
1109
1110   group = g_new0 (GOptionGroup, 1);
1111   group->name = g_strdup (name);
1112   group->description = g_strdup (description);
1113   group->help_description = g_strdup (help_description);
1114   group->user_data = user_data;
1115   group->destroy_notify = destroy;
1116   
1117   return group;
1118 }
1119
1120 void
1121 g_option_group_free (GOptionGroup *group)
1122 {
1123   g_return_if_fail (group != NULL);
1124
1125   g_free (group->name);
1126   g_free (group->description);
1127   g_free (group->help_description);
1128
1129   g_free (group->entries);
1130   
1131   if (group->destroy_notify)
1132     (* group->destroy_notify) (group->user_data);
1133
1134   if (group->translate_notify)
1135     (* group->translate_notify) (group->translate_data);
1136   
1137   g_free (group);
1138 }
1139
1140
1141 void
1142 g_option_group_add_entries (GOptionGroup       *group,
1143                             const GOptionEntry *entries)
1144 {
1145   gint n_entries;
1146   
1147   g_return_if_fail (entries != NULL);
1148
1149   for (n_entries = 0; entries[n_entries].long_name != NULL; n_entries++);
1150
1151   group->entries = g_renew (GOptionEntry, group->entries, group->n_entries + n_entries);
1152
1153   memcpy (group->entries + group->n_entries, entries, sizeof (GOptionEntry) * n_entries);
1154
1155   group->n_entries += n_entries;
1156 }
1157
1158 void
1159 g_option_group_set_parse_hooks (GOptionGroup     *group,
1160                                 GOptionParseFunc  pre_parse_func,
1161                                 GOptionParseFunc  post_parse_func)
1162 {
1163   g_return_if_fail (group != NULL);
1164
1165   group->pre_parse_func = pre_parse_func;
1166   group->post_parse_func = post_parse_func;  
1167 }
1168
1169 void
1170 g_option_group_set_error_hook (GOptionGroup     *group,
1171                                GOptionErrorFunc  error_func)
1172 {
1173   g_return_if_fail (group != NULL);
1174
1175   group->error_func = error_func;  
1176 }
1177
1178
1179 void
1180 g_option_group_set_translate_func (GOptionGroup   *group,
1181                                    GTranslateFunc  func,
1182                                    gpointer        data,
1183                                    GDestroyNotify  notify)
1184 {
1185   g_return_if_fail (group != NULL);
1186   
1187   if (group->translate_notify)
1188     group->translate_notify (group->translate_data);
1189       
1190   group->translate_func = func;
1191   group->translate_data = data;
1192   group->translate_notify = notify;
1193 }
1194
1195 static gchar *
1196 dgettext_swapped (const gchar *msgid, 
1197                   const gchar *domainname)
1198 {
1199   return dgettext (domainname, msgid);
1200 }
1201
1202 void
1203 g_option_group_set_translation_domain (GOptionGroup *group,
1204                                        const gchar  *domain)
1205 {
1206   g_return_if_fail (group != NULL);
1207
1208   g_option_group_set_translate_func (group, 
1209                                      (GTranslateFunc)dgettext_swapped,
1210                                      g_strdup (domain),
1211                                      g_free);
1212
1213