1cab0928 |
1 | #include <popt.h> |
eccb5352 |
2 | |
3d218f2a |
3 | #include <lttv/hook.h> |
fcdf0ec2 |
4 | #include <lttv/lttv.h> |
5 | #include <lttv/option.h> |
eccb5352 |
6 | |
7 | /* Extensible array of popt command line options. Modules add options as |
8 | they are loaded and initialized. */ |
9 | |
10 | typedef struct _lttv_option { |
11 | lttv_option_hook hook; |
12 | void *hook_data; |
13 | } lttv_option; |
14 | |
eccb5352 |
15 | static GArray *lttv_options_command; |
16 | |
17 | static GArray *lttv_options_command_popt; |
18 | |
19 | // unneeded static lttv_key *key ; |
20 | |
21 | static int command_argc; |
22 | |
23 | static char **command_argv; |
24 | |
25 | /* Lists of hooks to be called at different places */ |
26 | |
27 | static lttv_hooks |
28 | *hooks_options_before, |
29 | *hooks_options_after; |
30 | |
31 | static gboolean init_done = FALSE; |
32 | |
33 | void lttv_options_command_parse(void *hook_data, void *call_data); |
34 | |
35 | |
36 | void lttv_option_init(int argc, char **argv) { |
37 | |
38 | lttv_hooks *hooks_init_after; |
39 | |
40 | if(init_done) return; |
41 | else init_done = TRUE; |
42 | |
43 | command_argc = argc; |
44 | command_argv = argv; |
45 | |
46 | hooks_options_before = lttv_hooks_new(); |
47 | hooks_options_after = lttv_hooks_new(); |
48 | |
1cab0928 |
49 | lttv_attributes_set_pointer_pathname(lttv_global_attributes(), |
eccb5352 |
50 | "hooks/options/before", hooks_options_before); |
51 | |
1cab0928 |
52 | lttv_attributes_set_pointer_pathname(lttv_global_attributes(), |
eccb5352 |
53 | "hooks/options/after", hooks_options_after); |
54 | |
55 | lttv_options_command_popt = g_array_new(0,0,sizeof(struct poptOption)); |
56 | lttv_options_command = g_array_new(0,0,sizeof(lttv_option)); |
57 | |
1cab0928 |
58 | hooks_init_after = lttv_attributes_get_pointer_pathname(lttv_global_attributes(), |
eccb5352 |
59 | "hooks/init/after"); |
60 | lttv_hooks_add(hooks_init_after, lttv_options_command_parse, NULL); |
61 | |
62 | } |
63 | |
64 | void lttv_option_destroy() { |
65 | |
f68ad60d |
66 | struct poptOption *poption; |
150ef81a |
67 | |
68 | int i; |
f68ad60d |
69 | |
150ef81a |
70 | for(i=0; i < lttv_options_command_popt->len ; i++) { |
f68ad60d |
71 | poption = &g_array_index (lttv_options_command_popt, struct poptOption, i); |
72 | |
150ef81a |
73 | g_free((gpointer)poption->longName); |
74 | g_free((gpointer)poption->descrip); |
75 | g_free((gpointer)poption->argDescrip); |
f68ad60d |
76 | } |
eccb5352 |
77 | g_array_free(lttv_options_command_popt,TRUE) ; |
78 | g_array_free(lttv_options_command,TRUE) ; |
79 | |
1cab0928 |
80 | lttv_attributes_set_pointer_pathname(lttv_global_attributes(), |
eccb5352 |
81 | "hooks/options/before", NULL); |
82 | |
1cab0928 |
83 | lttv_attributes_set_pointer_pathname(lttv_global_attributes(), |
eccb5352 |
84 | "hooks/options/after", NULL); |
85 | |
86 | lttv_hooks_destroy(hooks_options_before); |
87 | lttv_hooks_destroy(hooks_options_after); |
88 | |
89 | } |
90 | |
91 | |
92 | static int poptToLTT[] = { |
93 | POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG |
94 | }; |
95 | |
96 | |
f68ad60d |
97 | void lttv_option_add(const char *long_name, const char char_name, |
98 | const char *description, const char *argDescription, |
150ef81a |
99 | const lttv_option_type t, void *p, |
f68ad60d |
100 | const lttv_option_hook h, void *hook_data) |
eccb5352 |
101 | { |
102 | struct poptOption poption; |
103 | |
104 | lttv_option option; |
105 | |
f68ad60d |
106 | poption.longName = (char *)g_strdup(long_name); |
eccb5352 |
107 | poption.shortName = char_name; |
f68ad60d |
108 | poption.descrip = (char *)g_strdup(description); |
109 | poption.argDescrip = (char *)g_strdup(argDescription); |
eccb5352 |
110 | poption.argInfo = poptToLTT[t]; |
111 | poption.arg = p; |
112 | poption.val = lttv_options_command->len + 1; |
113 | |
114 | option.hook = h; |
115 | option.hook_data = hook_data; |
116 | |
117 | g_array_append_val(lttv_options_command_popt,poption); |
118 | g_array_append_val(lttv_options_command,option); |
119 | } |
120 | |
121 | |
122 | static struct poptOption endOption = { NULL, '\0', 0, NULL, 0}; |
123 | |
124 | /* As we may load modules in the hooks called for argument processing, |
125 | * we have to recreate the argument context each time the |
126 | * lttv_options_command_popt is modified. This way we will be able to |
127 | * parse arguments defined by the modules |
128 | */ |
129 | |
130 | void lttv_options_command_parse(void *hook_data, void *call_data) |
131 | { |
132 | int rc; |
133 | int lastrc; |
134 | poptContext c; |
135 | lttv_option *option; |
136 | |
137 | lttv_hooks_call(hooks_options_before,NULL); |
138 | /* Always add then remove the null option around the get context */ |
139 | g_array_append_val(lttv_options_command_popt, endOption); |
140 | /* Compiler warning caused by const char ** for command_argv in header */ |
141 | /* Nothing we can do about it. Header should not put it const. */ |
142 | c = poptGetContext("lttv", command_argc, (const char**)command_argv, |
143 | (struct poptOption *)(lttv_options_command_popt->data),0); |
144 | |
145 | /* We remove the null option here to be able to add options correctly */ |
146 | g_array_remove_index(lttv_options_command_popt, |
147 | lttv_options_command_popt->len - 1); |
148 | |
149 | /* There is no last good offset */ |
150 | lastrc = -1; |
151 | |
152 | /* Parse options while not end of options event */ |
153 | while((rc = poptGetNextOpt(c)) != -1) { |
154 | |
155 | if(rc == POPT_ERROR_BADOPT) { |
156 | /* We need to redo the context with information added by modules */ |
157 | g_array_append_val(lttv_options_command_popt, endOption); |
158 | poptFreeContext(c); |
159 | c = poptGetContext("lttv", command_argc, (const char**)command_argv, |
160 | (struct poptOption *)lttv_options_command_popt->data,0); |
161 | g_array_remove_index(lttv_options_command_popt, |
4fa246be |
162 | lttv_options_command_popt->len -1); |
eccb5352 |
163 | |
164 | /* Cut out the already parsed elements */ |
165 | if(lastrc != -1) |
166 | while(poptGetNextOpt(c) != lastrc) { } ; |
167 | |
168 | /* Get the same option once again */ |
169 | g_assert(rc = poptGetNextOpt(c) != -1) ; |
170 | if(rc == POPT_ERROR_BADOPT) { |
171 | /* If here again we have a parsing error with all context info ok, |
172 | * then there is a problem in the arguments themself, give up */ |
173 | g_critical("option %s: %s", poptBadOption(c,0), poptStrerror(rc)); |
174 | break ; |
175 | } |
176 | } |
177 | |
178 | /* Remember this offset as the last good option value */ |
179 | lastrc = rc; |
180 | |
181 | /* Execute the hook registered with this option */ |
182 | option = ((lttv_option *)lttv_options_command->data) + rc - 1; |
183 | if(option->hook != NULL) option->hook(option->hook_data); |
184 | |
185 | } |
186 | |
187 | poptFreeContext(c); |
188 | |
189 | lttv_hooks_call(hooks_options_after,NULL); |
190 | |
191 | } |
192 | |