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