Rename g_context_option_error_quark() to g_option_error_quark(), because
[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         break;
546       }
547
548     case G_OPTION_ARG_FILENAME_ARRAY:
549       {
550         gchar *data;
551         
552         data = g_strdup (value);
553
554         change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
555                              entry->arg_data);
556
557         if (change->allocated.array.len == 0)
558           {
559             change->prev.array = entry->arg_data;
560             change->allocated.array.data = g_new (gchar *, 2);
561           }
562         else
563           change->allocated.array.data =
564             g_renew (gchar *, change->allocated.array.data,
565                      change->allocated.array.len + 2);
566
567         change->allocated.array.data[change->allocated.array.len] = data;
568         change->allocated.array.data[change->allocated.array.len + 1] = NULL;
569
570         change->allocated.array.len ++;
571
572         *(gchar ***)entry->arg_data = change->allocated.array.data;
573
574         break;
575       }
576       
577     case G_OPTION_ARG_INT:
578       {
579         gint data;
580
581         if (!parse_int (option_name, value,
582                         &data,
583                         error))
584           return FALSE;
585
586         change = get_change (context, G_OPTION_ARG_INT,
587                              entry->arg_data);
588         change->prev.integer = *(gint *)entry->arg_data;
589         *(gint *)entry->arg_data = data;
590         break;
591       }
592     case G_OPTION_ARG_CALLBACK:
593       {
594         gchar *tmp;
595         gboolean retval;
596         
597         tmp = g_locale_to_utf8 (value, -1, NULL, NULL, error);
598
599         if (!value)
600           return FALSE;
601
602         retval = (* (GOptionArgFunc) entry->arg_data) (option_name, tmp, group->user_data, error);
603         
604         g_free (tmp);
605         
606         return retval;
607         
608         break;
609       }
610     default:
611       g_assert_not_reached ();
612     }
613
614   return TRUE;
615 }
616
617 static gboolean
618 parse_short_option (GOptionContext *context,
619                     GOptionGroup   *group,
620                     gint            index,
621                     gint           *new_index,
622                     gchar           arg,
623                     gint           *argc,
624                     gchar        ***argv,
625                     GError        **error,
626                     gboolean       *parsed)
627 {
628   gint j;
629     
630   for (j = 0; j < group->n_entries; j++)
631     {
632       if (arg == group->entries[j].short_name)
633         {
634           if (group->entries[j].arg == G_OPTION_ARG_NONE)
635             {
636               parse_arg (context, group, &group->entries[j],
637                          NULL, NULL, error);
638               *parsed = TRUE;
639             }
640           else
641             {
642               gchar *value = NULL;
643               gchar *option_name;
644               
645               if (*new_index > index)
646                 {
647                   g_warning ("FIXME: figure out the correct error here");
648
649                   return FALSE;
650                 }
651               
652               if (index < *argc - 1)
653                 {
654                   value = (*argv)[index + 1];
655                   add_pending_null (context, &((*argv)[index + 1]), NULL);
656                   *new_index = index + 1;
657                 }
658               else
659                 value = "";
660
661
662               option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
663               
664               if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
665                 {
666                   g_free (option_name);
667                   return FALSE;
668                 }
669
670               g_free (option_name);
671               *parsed = TRUE;
672             }
673         }
674     }
675
676   return TRUE;
677 }
678
679 static gboolean
680 parse_long_option (GOptionContext *context,
681                    GOptionGroup   *group,
682                    gint           *index,
683                    gchar          *arg,
684                    gint           *argc,
685                    gchar        ***argv,
686                    GError        **error,
687                    gboolean       *parsed)
688 {
689   gint j;
690
691   for (j = 0; j < group->n_entries; j++)
692     {
693       if (*index >= *argc)
694         return TRUE;
695
696       if (group->entries[j].arg == G_OPTION_ARG_NONE &&
697           strcmp (arg, group->entries[j].long_name) == 0)
698         {
699           parse_arg (context, group, &group->entries[j],
700                      NULL, NULL, error);
701           
702           add_pending_null (context, &((*argv)[*index]), NULL);
703           *parsed = TRUE;
704         }
705       else
706         {
707           gint len = strlen (group->entries[j].long_name);
708           
709           if (strncmp (arg, group->entries[j].long_name, len) == 0 &&
710               (arg[len] == '=' || arg[len] == 0))
711             {
712               gchar *value = NULL;
713               gchar *option_name;
714
715               add_pending_null (context, &((*argv)[*index]), NULL);
716               
717               if (arg[len] == '=')
718                 value = arg + len + 1;
719               else if (*index < *argc - 1)
720                 {
721                   value = (*argv)[*index + 1];
722                   add_pending_null (context, &((*argv)[*index + 1]), NULL);
723                   (*index)++;
724                 }
725               else
726                 value = "";
727
728               option_name = g_strconcat ("--", group->entries[j].long_name, NULL);
729               
730               if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
731                 {
732                   g_free (option_name);
733                   return FALSE;
734                 }
735
736               g_free (option_name);
737               *parsed = TRUE;
738             }
739         }
740     }
741   
742   return TRUE;
743 }
744
745 static void
746 free_changes_list (GOptionContext *context,
747                    gboolean        revert)
748 {
749   GList *list;
750
751   for (list = context->changes; list != NULL; list = list->next)
752     {
753       Change *change = list->data;
754
755       if (revert)
756         {
757           switch (change->arg_type)
758             {
759             case G_OPTION_ARG_NONE:
760               *(gboolean *)change->arg_data = change->prev.bool;
761               break;
762             case G_OPTION_ARG_INT:
763               *(gint *)change->arg_data = change->prev.integer;
764               break;
765             case G_OPTION_ARG_STRING:
766             case G_OPTION_ARG_FILENAME:
767               g_free (change->allocated.str);
768               *(gchar **)change->arg_data = change->prev.str;
769               break;
770             case G_OPTION_ARG_STRING_ARRAY:
771             case G_OPTION_ARG_FILENAME_ARRAY:
772               g_strfreev (change->allocated.array.data);
773               *(gchar ***)change->arg_data = change->prev.array;
774               break;
775             default:
776               g_assert_not_reached ();
777             }
778         }
779       
780       g_free (change);
781     }
782
783   g_list_free (context->changes);
784   context->changes = NULL;
785 }
786
787 static void
788 free_pending_nulls (GOptionContext *context,
789                     gboolean        perform_nulls)
790 {
791   GList *list;
792
793   for (list = context->pending_nulls; list != NULL; list = list->next)
794     {
795       PendingNull *n = list->data;
796
797       if (perform_nulls)
798         {
799           if (n->value)
800             {
801               /* Copy back the short options */
802               *(n->ptr)[0] = '-';             
803               strcpy (*n->ptr + 1, n->value);
804             }
805           else
806             *n->ptr = NULL;
807         }
808       
809       g_free (n->value);
810       g_free (n);
811     }
812
813   g_list_free (context->pending_nulls);
814   context->pending_nulls = NULL;
815 }
816
817 gboolean
818 g_option_context_parse (GOptionContext   *context,
819                         gint             *argc,
820                         gchar          ***argv,
821                         GError          **error)
822 {
823   gint i, j, k;
824   GList *list;
825
826   /* Set program name */
827   if (argc && argv)
828     {
829       gchar *prgname;
830       
831       prgname = strrchr ((*argv)[0], G_DIR_SEPARATOR);
832       if (prgname)
833         prgname++;
834       else
835         prgname = (*argv)[0];
836
837       g_set_prgname (prgname);
838     }
839   else
840     {
841       g_set_prgname ("<unknown>");
842     }
843   
844   /* Call pre-parse hooks */
845   list = context->groups;
846   while (list)
847     {
848       GOptionGroup *group = list->data;
849       
850       if (group->pre_parse_func)
851         {
852           if (!(* group->pre_parse_func) (context, group,
853                                           group->user_data, error))
854             goto fail;
855         }
856       
857       list = list->next;
858     }
859
860   if (context->main_group && context->main_group->pre_parse_func)
861     {
862       if (!(* context->main_group->pre_parse_func) (context, context->main_group,
863                                                     context->main_group->user_data, error))
864         goto fail;
865     }
866
867   if (argc && argv)
868     {
869       for (i = 1; i < *argc; i++)
870         {
871           gchar *arg;
872           gboolean parsed = FALSE;
873
874           if ((*argv)[i][0] == '-')
875             {
876               if ((*argv)[i][1] == '-')
877                 {
878                   /* -- option */
879
880                   arg = (*argv)[i] + 2;
881
882                   /* '--' terminates list of arguments */
883                   if (*arg == 0)
884                     {
885                       add_pending_null (context, &((*argv)[i]), NULL);
886                       break;
887                     }
888
889                   /* Handle help options */
890                   if (context->help_enabled)
891                     {
892                       if (strcmp (arg, "help") == 0)
893                         print_help (context, TRUE, NULL);
894                       else if (strcmp (arg, "help-all") == 0)
895                         print_help (context, FALSE, NULL);                    
896                       else if (strncmp (arg, "help-", 5) == 0)
897                         {
898                           GList *list;
899                           
900                           list = context->groups;
901                           
902                           while (list)
903                             {
904                               GOptionGroup *group = list->data;
905                               
906                               if (strcmp (arg + 5, group->name) == 0)
907                                 print_help (context, FALSE, group);                                           
908                               
909                               list = list->next;
910                             }
911                         }
912                     }
913
914                   if (context->main_group &&
915                       !parse_long_option (context, context->main_group, &i, arg,
916                                           argc, argv, error, &parsed))
917                     goto fail;
918
919                   if (parsed)
920                     continue;
921                   
922                   /* Try the groups */
923                   list = context->groups;
924                   while (list)
925                     {
926                       GOptionGroup *group = list->data;
927                       
928                       if (!parse_long_option (context, group, &i, arg,
929                                               argc, argv, error, &parsed))
930                         goto fail;
931                       
932                       if (parsed)
933                         break;
934                       
935                       list = list->next;
936                     }
937
938                   if (context->ignore_unknown)
939                     continue;
940                 }
941               else
942                 {
943                   gint new_i, j;
944                   gboolean *nulled_out = NULL;
945                   
946                   arg = (*argv)[i] + 1;
947
948                   new_i = i;
949
950                   if (context->ignore_unknown)
951                     nulled_out = g_new0 (gboolean, strlen (arg));
952                   
953                   for (j = 0; j < strlen (arg); j++)
954                     {
955                       parsed = FALSE;
956                       
957                       if (context->main_group &&
958                           !parse_short_option (context, context->main_group,
959                                                i, &new_i, arg[j],
960                                                argc, argv, error, &parsed))
961                         {
962
963                           g_free (nulled_out);
964                           goto fail;
965                         }
966
967                       if (!parsed)
968                         {
969                           /* Try the groups */
970                           list = context->groups;
971                           while (list)
972                             {
973                               GOptionGroup *group = list->data;
974                               
975                               if (!parse_short_option (context, group, i, &new_i, arg[j],
976                                                        argc, argv, error, &parsed))
977                                 goto fail;
978                               
979                               if (parsed)
980                                 break;
981                           
982                               list = list->next;
983                             }
984                         }
985
986                       if (context->ignore_unknown)
987                         {
988                           if (parsed)
989                             nulled_out[j] = TRUE;
990                           else
991                             continue;
992                         }
993
994                       if (!parsed)
995                         break;
996                     }
997
998                   if (context->ignore_unknown)
999                     {
1000                       gchar *new_arg = NULL; 
1001                       gint arg_index = 0;
1002                       
1003                       for (j = 0; j < strlen (arg); j++)
1004                         {
1005                           if (!nulled_out[j])
1006                             {
1007                               if (!new_arg)
1008                                 new_arg = g_malloc (strlen (arg));
1009                               new_arg[arg_index++] = arg[j];
1010                             }
1011                         }
1012                       if (new_arg)
1013                         new_arg[arg_index] = '\0';
1014                       
1015                       add_pending_null (context, &((*argv)[i]), new_arg);
1016                     }
1017                   else if (parsed)
1018                     {
1019                       add_pending_null (context, &((*argv)[i]), NULL);
1020                       i = new_i;
1021                     }
1022                 }
1023               
1024               if (!parsed && !context->ignore_unknown)
1025                 {
1026                   g_set_error (error,
1027                                G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION,
1028                                _("Unknown option %s"), (*argv)[i]);
1029                   goto fail;
1030                 }
1031             }
1032         }
1033
1034       /* Call post-parse hooks */
1035       list = context->groups;
1036       while (list)
1037         {
1038           GOptionGroup *group = list->data;
1039
1040           if (group->post_parse_func)
1041             {
1042               if (!(* group->post_parse_func) (context, group,
1043                                                group->user_data, error))
1044                 goto fail;
1045             }
1046           
1047           list = list->next;
1048         }
1049
1050       if (context->main_group && context->main_group->post_parse_func)
1051         {
1052           if (!(* context->main_group->post_parse_func) (context, context->main_group,
1053                                                          context->main_group->user_data, error))
1054             goto fail;
1055         }
1056
1057       free_pending_nulls (context, TRUE);
1058       
1059       for (i = 1; i < *argc; i++)
1060         {
1061           for (k = i; k < *argc; k++)
1062             if ((*argv)[k] != NULL)
1063               break;
1064           
1065           if (k > i)
1066             {
1067               k -= i;
1068               for (j = i + k; j < *argc; j++)
1069                 (*argv)[j-k] = (*argv)[j];
1070               *argc -= k;
1071             }
1072         }      
1073     }
1074
1075   return TRUE;
1076
1077  fail:
1078   
1079   /* Call error hooks */
1080   list = context->groups;
1081   while (list)
1082     {
1083       GOptionGroup *group = list->data;
1084       
1085       if (group->error_func)
1086         (* group->error_func) (context, group,
1087                                group->user_data, error);
1088       
1089       list = list->next;
1090     }
1091
1092   if (context->main_group && context->main_group->error_func)
1093     (* context->main_group->error_func) (context, context->main_group,
1094                                          context->main_group->user_data, error);
1095   
1096   free_changes_list (context, TRUE);
1097   free_pending_nulls (context, FALSE);
1098
1099   return FALSE;
1100 }
1101                                    
1102      
1103 GOptionGroup *
1104 g_option_group_new (const gchar    *name,
1105                     const gchar    *description,
1106                     const gchar    *help_description,
1107                     gpointer        user_data,
1108                     GDestroyNotify  destroy)
1109
1110 {
1111   GOptionGroup *group;
1112
1113   group = g_new0 (GOptionGroup, 1);
1114   group->name = g_strdup (name);
1115   group->description = g_strdup (description);
1116   group->help_description = g_strdup (help_description);
1117   group->user_data = user_data;
1118   group->destroy_notify = destroy;
1119   
1120   return group;
1121 }
1122
1123 void
1124 g_option_group_free (GOptionGroup *group)
1125 {
1126   g_return_if_fail (group != NULL);
1127
1128   g_free (group->name);
1129   g_free (group->description);
1130   g_free (group->help_description);
1131
1132   g_free (group->entries);
1133   
1134   if (group->destroy_notify)
1135     (* group->destroy_notify) (group->user_data);
1136
1137   if (group->translate_notify)
1138     (* group->translate_notify) (group->translate_data);
1139   
1140   g_free (group);
1141 }
1142
1143
1144 void
1145 g_option_group_add_entries (GOptionGroup       *group,
1146                             const GOptionEntry *entries)
1147 {
1148   gint n_entries;
1149   
1150   g_return_if_fail (entries != NULL);
1151
1152   for (n_entries = 0; entries[n_entries].long_name != NULL; n_entries++);
1153
1154   group->entries = g_renew (GOptionEntry, group->entries, group->n_entries + n_entries);
1155
1156   memcpy (group->entries + group->n_entries, entries, sizeof (GOptionEntry) * n_entries);
1157
1158   group->n_entries += n_entries;
1159 }
1160
1161 void
1162 g_option_group_set_parse_hooks (GOptionGroup     *group,
1163                                 GOptionParseFunc  pre_parse_func,
1164                                 GOptionParseFunc  post_parse_func)
1165 {
1166   g_return_if_fail (group != NULL);
1167
1168   group->pre_parse_func = pre_parse_func;
1169   group->post_parse_func = post_parse_func;  
1170 }
1171
1172 void
1173 g_option_group_set_error_hook (GOptionGroup     *group,
1174                                GOptionErrorFunc  error_func)
1175 {
1176   g_return_if_fail (group != NULL);
1177
1178   group->error_func = error_func;  
1179 }
1180
1181
1182 void
1183 g_option_group_set_translate_func (GOptionGroup   *group,
1184                                    GTranslateFunc  func,
1185                                    gpointer        data,
1186                                    GDestroyNotify  notify)
1187 {
1188   g_return_if_fail (group != NULL);
1189   
1190   if (group->translate_notify)
1191     group->translate_notify (group->translate_data);
1192       
1193   group->translate_func = func;
1194   group->translate_data = data;
1195   group->translate_notify = notify;
1196 }
1197
1198 static gchar *
1199 dgettext_swapped (const gchar *msgid, 
1200                   const gchar *domainname)
1201 {
1202   return dgettext (domainname, msgid);
1203 }
1204
1205 void
1206 g_option_group_set_translation_domain (GOptionGroup *group,
1207                                        const gchar  *domain)
1208 {
1209   g_return_if_fail (group != NULL);
1210
1211   g_option_group_set_translate_func (group, 
1212                                      (GTranslateFunc)dgettext_swapped,
1213                                      g_strdup (domain),
1214                                      g_free);
1215
1216