Refine the interactions between the hooks provided by the different modules.
[lttv.git] / ltt / branches / poly / lttv / option.c
index b7e4270a049c2dcd87297edeba254ca63811218e..3a95a4af9b001c88bcbd475fa5cc8b3ea678df12 100644 (file)
 #include <popt.h>
 
-#include <lttv/hook.h>
-#include <lttv/lttv.h>
 #include <lttv/option.h>
 
-/* Extensible array of popt command line options. Modules add options as
-   they are loaded and initialized. */
+typedef struct _LttvOption {
+  const char *long_name;
+  char char_name;
+  const char *description;
+  const char *arg_description;
+  LttvOptionType t;
+  gpointer p;
+  LttvOptionHook h;
+  gpointer hook_data;
+} LttvOption;
 
-typedef struct _lttv_option {
-  lttv_option_hook hook;
-  void *hook_data;
-} lttv_option;
+GHashTable *options;
 
-static GArray *lttv_options_command;
 
-static GArray *lttv_options_command_popt;
-
-// unneeded   static lttv_key *key ;
+static void
+list_options(gpointer key, gpointer value, gpointer user_data)
+{
+  g_ptr_array_add((GPtrArray *)user_data, value);
+}
 
-static int command_argc;
 
-static char **command_argv;
+static void
+free_option(LttvOption *option)
+{
+  g_free(option->long_name);
+  g_free(option->description);
+  g_free(option->arg_description);
+  g_free(option);
+}
 
-/* Lists of hooks to be called at different places */
 
-static lttv_hooks
-  *hooks_options_before,
-  *hooks_options_after;
+void lttv_option_init(int argc, char **argv)
+{
+  options = g_hash_table_new(g_str_hash, g_str_equal);
+}
 
-static gboolean init_done = FALSE;
 
-void lttv_options_command_parse(void *hook_data, void *call_data);
+void lttv_option_destroy()
+{
+  LttvOption option;
 
+  GPtrArray list = g_ptr_array_new();
 
-void lttv_option_init(int argc, char **argv) {
+  int i;
 
-  lttv_hooks *hooks_init_after;
+  g_hash_table_foreach(options, list_options, list);
+  g_hash_table_destroy(options);
 
-  if(init_done) return;
-  else init_done = TRUE;
+  for(i = 0 ; i < list->len ; i++) {
+    free_option((LttvOption *)list->pdata[i]);
+  }
+  g_ptr_array_free(list, TRUE);
+}
 
-  command_argc = argc;
-  command_argv = argv;
 
-  hooks_options_before = lttv_hooks_new();
-  hooks_options_after = lttv_hooks_new();
+void lttv_option_add(const char *long_name, const char char_name,
+    const char *description, const char *arg_description,
+    const LttvOptionType t, void *p,
+    const LttvOptionHook h, void *hook_data)
+{
+  LttvOption *option;
 
-  lttv_attributes_set_pointer_pathname(lttv_global_attributes(), 
-      "hooks/options/before", hooks_options_before);
+  if(g_hash_table_lookup(options, long_name) != NULL) {
+    g_warning("duplicate option");
+    return;
+  }
 
-  lttv_attributes_set_pointer_pathname(lttv_global_attributes(),
-      "hooks/options/after",  hooks_options_after);
+  option = g_new(LttvOption, 1);
+  option->long_name = g_strdup(long_name);
+  option->char_name = char_name;
+  option->description = g_strdup(description);
+  option->arg_description = g_strdup(arg_description);
+  option->t = t;
+  option->p = p;
+  option->h = h;
+  option->hook_data = hook_data;
+  g_hash_table_insert(options, option->long_name, option);
+}
 
-  lttv_options_command_popt = g_array_new(0,0,sizeof(struct poptOption));
-  lttv_options_command = g_array_new(0,0,sizeof(lttv_option));
 
-  hooks_init_after = lttv_attributes_get_pointer_pathname(lttv_global_attributes(),
-                 "hooks/init/after");
-  lttv_hooks_add(hooks_init_after, lttv_options_command_parse, NULL);
+void 
+lttv_option_remove(const char *long_name) 
+{
+  LttvOption *option = g_hash_table_lookup(options, long_name);
 
+  if(option == NULL) {
+    g_warning("trying to remove unknown option %s", long_name);
+    return;
+  }
+  g_hash_table_remove(options, long_name);
+  free_option(option);
 }
 
-void lttv_option_destroy() {
 
-  struct poptOption *poption;
+static int poptToLTT[] = { 
+  POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG
+};
 
-  int i;
-  
-  for(i=0; i < lttv_options_command_popt->len ; i++) {
-    poption = &g_array_index (lttv_options_command_popt, struct poptOption, i);
+static struct poptOption endOption = { NULL, '\0', 0, NULL, 0};
 
-    g_free((gpointer)poption->longName);
-    g_free((gpointer)poption->descrip);
-    g_free((gpointer)poption->argDescrip);
-  }
-  g_array_free(lttv_options_command_popt,TRUE) ;
-  g_array_free(lttv_options_command,TRUE) ;
 
-  lttv_attributes_set_pointer_pathname(lttv_global_attributes(), 
-      "hooks/options/before", NULL);
+static void 
+build_popts(GPtrArray **plist, struct poptOption **ppopts, poptContext *pc,
+    int argv, char **argv)
+{
+  LttvOption *option;
 
-  lttv_attributes_set_pointer_pathname(lttv_global_attributes(),
-      "hooks/options/after",  NULL);
+  GPtrArray *list;
 
-  lttv_hooks_destroy(hooks_options_before);
-  lttv_hooks_destroy(hooks_options_after);
+  struct poptOption *popts;
 
-}
+  poptContext c;
 
+  guint i;
 
-static int poptToLTT[] = { 
-  POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG
-};
+  list = g_ptr_array_new();
 
+  g_hash_table_foreach(options, list_options, list);
 
-void lttv_option_add(const char *long_name, const char char_name,
-               const char *description, const char *argDescription,
-               const lttv_option_type t, void *p, 
-               const lttv_option_hook h, void *hook_data)
-{
-  struct poptOption poption;
+  /* Build a popt options array from our list */
 
-  lttv_option option;
+  popts = g_new(struct poptOption, list->len + 1);
 
-  poption.longName = (char *)g_strdup(long_name);
-  poption.shortName = char_name;
-  poption.descrip = (char *)g_strdup(description);
-  poption.argDescrip = (char *)g_strdup(argDescription);
-  poption.argInfo = poptToLTT[t];
-  poption.arg = p;
-  poption.val = lttv_options_command->len + 1;
+  for(i = 0 ; i < list->len ; i++) {
+    option = (LttvOption *)list->pdata[i];
+    popts[i].longName = option->long_name;
+    popts[i].shortName = option->char_name;
+    popts[i].descrip = option->description;
+    popts[i].argDescrip = option->arg_description;
+    popts[i].argInfo = poptToLTT[option->t];
+    popts[i].arg = option->p;
+    popts[i].val = i + 1;
+  }
+
+  /* Terminate the array for popt and create the context */
 
-  option.hook = h;
-  option.hook_data = hook_data;
+  popts[list->len] = endOption;
+  c = poptGetContext(argv[0], argc, (const char**)argv, popts, 0);
 
-  g_array_append_val(lttv_options_command_popt,poption);
-  g_array_append_val(lttv_options_command,option);
+  *plist = list;
+  *ppopts = popts;
+  *pc = c;
 }
 
 
-static struct poptOption endOption = { NULL, '\0', 0, NULL, 0};
+static void 
+destroy_popts(GPtrArray **plist, struct poptOption **ppopts, poptContext *pc)
+{
+  g_ptr_array_free(*plist, TRUE); *plist = NULL;
+  g_free(*ppopts); *ppopts = NULL;
+  poptFreeContext(*c);  
+}
 
-/* As we may load modules in the hooks called for argument processing,
- * we have to recreate the argument context each time the
- * lttv_options_command_popt is modified. This way we will be able to
- * parse arguments defined by the modules
- */
 
-void lttv_options_command_parse(void *hook_data, void *call_data) 
+void lttv_option_parse(int argc, char **argv)
 {
-  int rc;
-  int lastrc;
+  GPtrArray *list;
+
+  LttvOption *option;
+
+  int i, rc, first_arg;
+
+  struct poptOption *popts;
+
   poptContext c;
-  lttv_option *option;
 
-  lttv_hooks_call(hooks_options_before,NULL);
-  /* Always add then remove the null option around the get context */
-  g_array_append_val(lttv_options_command_popt, endOption);
-  /* Compiler warning caused by const char ** for command_argv in header */
-  /* Nothing we can do about it. Header should not put it const. */
-  c = poptGetContext("lttv", command_argc, (const char**)command_argv,
-      (struct poptOption *)(lttv_options_command_popt->data),0);
+  i = 0;
 
-  /* We remove the null option here to be able to add options correctly */
-  g_array_remove_index(lttv_options_command_popt,
-                 lttv_options_command_popt->len - 1);
+  first_arg = 0;
 
-  /* There is no last good offset */
-  lastrc = -1;
+  build_popts(&list, &popts, &c, argc, argv);
 
   /* Parse options while not end of options event */
+
   while((rc = poptGetNextOpt(c)) != -1) {
-         
-    if(rc == POPT_ERROR_BADOPT) {
-      /* We need to redo the context with information added by modules */
-      g_array_append_val(lttv_options_command_popt, endOption);
-      poptFreeContext(c);  
-      c = poptGetContext("lttv", command_argc, (const char**)command_argv,
-          (struct poptOption *)lttv_options_command_popt->data,0);
-      g_array_remove_index(lttv_options_command_popt,
-                 lttv_options_command_popt->len -1);
-
-      /* Cut out the already parsed elements */
-      if(lastrc != -1)
-        while(poptGetNextOpt(c) != lastrc) { } ;
-      
-      /* Get the same option once again */
-      g_assert(rc = poptGetNextOpt(c) != -1) ;
-      if(rc == POPT_ERROR_BADOPT) {
-        /* If here again we have a parsing error with all context info ok,
-        * then there is a problem in the arguments themself, give up */
-        g_critical("option %s: %s", poptBadOption(c,0), poptStrerror(rc));
-       break ;
-      }
+
+    /* The option was recognized and the rc value returned is the argument
+       position in the array. Call the associated hook if present. */
+  
+    if(rc > 0) {
+      option = (LttvOption *)(list->pdata[rc - 1]);
+      if(option->hook != NULL) option->hook(option->hook_data);
+      i++;
+    } 
+
+    else if(rc == POPT_ERROR_BADOPT && i != first_arg) {
+
+      /* Perhaps this option is newly added, restart parsing */
+
+      destroy_popts(&list, &popts, &c);
+      build_popts(&list, &popts, &c, argc, argv);
+
+      /* Get back to the same argument */
+
+      first_arg = i;
+      for(i = 0; i < first_arg; i++) poptGetNextOpt(c);
     }
-    
-    /* Remember this offset as the last good option value */
-    lastrc = rc;
 
-    /* Execute the hook registered with this option */
-    option = ((lttv_option *)lttv_options_command->data) + rc - 1;
-    if(option->hook != NULL) option->hook(option->hook_data);
-  }
+    else {
 
-  poptFreeContext(c);
+      /* The option has some error and it is not because this is a newly
+         added option not recognized. */
 
-  lttv_hooks_call(hooks_options_after,NULL);
-  
+      g_error("option %s: %s", poptBadOption(c,0), poptStrerror(rc));
+      break;
+    }
+    
+  }
+
+  destroy_popts(&list, &popts, &c);
 }
 
This page took 0.026183 seconds and 4 git commands to generate.