Update FSF address
[lttv.git] / lttv / lttv / option.c
CommitLineData
9c312311 1/* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 Michel Dagenais
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
b9ce0bad
YB
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16 * MA 02110-1301, USA.
9c312311 17 */
18
4e4d11b3 19#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
eccb5352 22
ffd54a90 23#include <popt.h>
24#include <glib.h>
08b1c66e 25#include <lttv/module.h>
fcdf0ec2 26#include <lttv/option.h>
eccb5352 27
dc877563 28typedef struct _LttvOption {
90e19f82
AM
29 char *long_name;
30 char char_name;
31 char *description;
32 char *arg_description;
33 LttvOptionType t;
34 gpointer p;
35 LttvOptionHook hook;
36 gpointer hook_data;
37
38 /* Keep the order of addition */
39 guint val;
dc877563 40} LttvOption;
eccb5352 41
dc877563 42GHashTable *options;
eccb5352 43
eccb5352 44
90e19f82 45static void list_options(gpointer key, gpointer value, gpointer user_data)
dc877563 46{
90e19f82
AM
47 GPtrArray *list = (GPtrArray *)user_data;
48 LttvOption *option = (LttvOption *)value;
d18abf7d 49
90e19f82
AM
50 if(list->len < option->val)
51 g_ptr_array_set_size(list, option->val);
52 list->pdata[option->val-1] = option;
dc877563 53}
eccb5352 54
eccb5352 55
90e19f82 56static void free_option(LttvOption *option)
dc877563 57{
90e19f82
AM
58 g_free(option->long_name);
59 g_free(option->description);
60 g_free(option->arg_description);
61 g_free(option);
dc877563 62}
eccb5352 63
3667f07d
YB
64static gboolean compare_short_option(gpointer key,
65 gpointer value,
66 gpointer user_data)
67{
68 LttvOption *option = value;
69 const char short_option = *(const char *)user_data;
70
71
72 if(option->char_name == short_option) {
73 return TRUE;
74 } else {
75 return FALSE;
76 }
77}
eccb5352 78
dc877563 79void lttv_option_add(const char *long_name, const char char_name,
90e19f82
AM
80 const char *description, const char *arg_description,
81 const LttvOptionType t, void *p,
82 const LttvOptionHook h, void *hook_data)
dc877563 83{
90e19f82
AM
84 LttvOption *option;
85
86 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add option %s", long_name);
87 if(g_hash_table_lookup(options, long_name) != NULL) {
3667f07d 88 g_warning("duplicate long-option: %s", long_name);
90e19f82
AM
89 return;
90 }
3667f07d
YB
91 if(char_name && g_hash_table_find(options, compare_short_option, (gpointer)&char_name) != NULL) {
92 g_warning("duplicate short-option: %c for option %s",
93 char_name,
94 long_name);
95 return;
96 }
97
90e19f82
AM
98
99 option = g_new(LttvOption, 1);
100 option->long_name = g_strdup(long_name);
101 option->char_name = char_name;
102 option->description = g_strdup(description);
103 option->arg_description = g_strdup(arg_description);
104 option->t = t;
105 option->p = p;
106 option->hook = h;
107 option->hook_data = hook_data;
108 option->val = g_hash_table_size(options) + 1;
109 g_hash_table_insert(options, option->long_name, option);
dc877563 110}
eccb5352 111
eccb5352 112
90e19f82 113void lttv_option_remove(const char *long_name)
dc877563 114{
90e19f82
AM
115 LttvOption *option = g_hash_table_lookup(options, long_name);
116
117 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Remove option %s", long_name);
118 if(option == NULL) {
119 g_warning("trying to remove unknown option %s", long_name);
120 return;
121 }
122 g_hash_table_remove(options, long_name);
123 free_option(option);
eccb5352 124}
125
eccb5352 126
dc877563 127static int poptToLTT[] = {
90e19f82 128 POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG
dc877563 129};
150ef81a 130
00e74b69 131static struct poptOption endOption = { NULL, '\0', 0, NULL, 0, NULL, NULL };
f68ad60d 132
eccb5352 133
90e19f82
AM
134static void build_popts(GPtrArray **plist, struct poptOption **ppopts,
135 poptContext *pc, int argc, char **argv)
dc877563 136{
90e19f82 137 LttvOption *option;
eccb5352 138
90e19f82 139 GPtrArray *list;
eccb5352 140
90e19f82 141 struct poptOption *popts;
eccb5352 142
90e19f82 143 poptContext c;
eccb5352 144
90e19f82 145 guint i;
eccb5352 146
90e19f82 147 list = g_ptr_array_sized_new(g_hash_table_size(options));
eccb5352 148
90e19f82 149 g_hash_table_foreach(options, list_options, list);
eccb5352 150
90e19f82 151 /* Build a popt options array from our list */
eccb5352 152
90e19f82 153 popts = g_new(struct poptOption, list->len + 1);
eccb5352 154
90e19f82
AM
155 /* add the options in the reverse order, so last additions are parsed first */
156 for(i = 0 ; i < list->len ; i++) {
157 guint reverse_i = list->len-1-i;
158 option = (LttvOption *)list->pdata[i];
159 popts[reverse_i].longName = option->long_name;
160 popts[reverse_i].shortName = option->char_name;
161 popts[reverse_i].descrip = option->description;
162 popts[reverse_i].argDescrip = option->arg_description;
163 popts[reverse_i].argInfo = poptToLTT[option->t];
164 popts[reverse_i].arg = option->p;
165 popts[reverse_i].val = option->val;
166 }
dc877563 167
90e19f82 168 /* Terminate the array for popt and create the context */
eccb5352 169
90e19f82
AM
170 popts[list->len] = endOption;
171 c = poptGetContext(argv[0], argc, (const char**)argv, popts, 0);
eccb5352 172
90e19f82
AM
173 *plist = list;
174 *ppopts = popts;
175 *pc = c;
eccb5352 176}
177
178
90e19f82
AM
179static void destroy_popts(GPtrArray **plist, struct poptOption **ppopts,
180 poptContext *pc)
dc877563 181{
90e19f82
AM
182 g_ptr_array_free(*plist, TRUE); *plist = NULL;
183 g_free(*ppopts); *ppopts = NULL;
184 poptFreeContext(*pc);
dc877563 185}
eccb5352 186
eccb5352 187
dc877563 188void lttv_option_parse(int argc, char **argv)
eccb5352 189{
90e19f82 190 GPtrArray *list;
dc877563 191
90e19f82 192 LttvOption *option;
dc877563 193
90e19f82 194 int i, rc, first_arg;
dc877563 195
90e19f82 196 struct poptOption *popts;
dc877563 197
90e19f82 198 poptContext c;
eccb5352 199
90e19f82 200 i = 0;
eccb5352 201
90e19f82 202 first_arg = 0;
eccb5352 203
90e19f82 204 guint hash_size = 0;
d18abf7d 205
90e19f82 206 build_popts(&list, &popts, &c, argc, argv);
eccb5352 207
90e19f82 208 /* Parse options while not end of options event */
dc877563 209
90e19f82 210 while((rc = poptGetNextOpt(c)) != -1) {
dc877563 211
90e19f82
AM
212 /* The option was recognized and the rc value returned is the argument
213 position in the array. Call the associated hook if present. */
d18abf7d 214
90e19f82
AM
215 if(rc > 0) {
216 option = (LttvOption *)(list->pdata[rc - 1]);
217 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Option %s encountered",
218 option->long_name);
219 hash_size = g_hash_table_size(options);
220 if(option->hook != NULL) {
221 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s hook called",
222 option->long_name);
223 option->hook(option->hook_data);
224 }
225 i++;
d18abf7d 226
90e19f82
AM
227 /* If the size of the option hash changed, add new options
228 * right now. It resolves the conflict of multiple same short
229 * option use.
230 */
231 if(hash_size != g_hash_table_size(options)) {
232 destroy_popts(&list, &popts, &c);
233 build_popts(&list, &popts, &c, argc, argv);
d18abf7d 234
90e19f82 235 /* Get back to the same argument */
dc877563 236
90e19f82
AM
237 first_arg = i;
238 for(i = 0; i < first_arg; i++) {
239 rc = poptGetNextOpt(c);
240 option = (LttvOption *)(list->pdata[rc - 1]);
241 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s rescanned, skipped",
242 option->long_name);
243 }
244 }
245 }
dc877563 246
90e19f82
AM
247 else if(rc == POPT_ERROR_BADOPT && i != first_arg) {
248 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
249 "Option %s not recognized, rescan options with new additions",
250 poptBadOption(c,0));
dc877563 251
90e19f82 252 /* Perhaps this option is newly added, restart parsing */
dc877563 253
90e19f82
AM
254 destroy_popts(&list, &popts, &c);
255 build_popts(&list, &popts, &c, argc, argv);
dc877563 256
90e19f82 257 /* Get back to the same argument */
eccb5352 258
90e19f82
AM
259 first_arg = i;
260 for(i = 0; i < first_arg; i++) {
261 rc = poptGetNextOpt(c);
262 option = (LttvOption *)(list->pdata[rc - 1]);
263 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s rescanned, skipped",
264 option->long_name);
265 }
266 }
eccb5352 267
90e19f82 268 else {
eccb5352 269
90e19f82
AM
270 /* The option has some error and it is not because this is a newly
271 added option not recognized. */
dc877563 272
90e19f82
AM
273 g_error("option %s: %s", poptBadOption(c,0), poptStrerror(rc));
274 break;
275 }
276
277 }
278
279 destroy_popts(&list, &popts, &c);
eccb5352 280}
281
08b1c66e 282/* CHECK */
d888c9c8 283static void show_help(LttvOption *option)
284{
90e19f82
AM
285 printf("--%s -%c argument: %s\n" , option->long_name,
286 option->char_name, option->arg_description);
287 printf(" %s\n" , option->description);
d888c9c8 288
289}
290
291void lttv_option_show_help(void)
292{
90e19f82 293 GPtrArray *list = g_ptr_array_new();
d888c9c8 294
90e19f82 295 guint i;
d888c9c8 296
90e19f82 297 g_hash_table_foreach(options, list_options, list);
d888c9c8 298
299 printf("Built-in commands available:\n");
300 printf("\n");
301
90e19f82
AM
302 for(i = 0 ; i < list->len ; i++) {
303 show_help((LttvOption *)list->pdata[i]);
304 }
305 g_ptr_array_free(list, TRUE);
d888c9c8 306}
b445142a 307
08b1c66e 308static void init()
309{
90e19f82
AM
310 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init option.c");
311 options = g_hash_table_new(g_str_hash, g_str_equal);
08b1c66e 312}
313
314
315static void destroy()
316{
90e19f82 317 GPtrArray *list = g_ptr_array_new();
08b1c66e 318
90e19f82 319 guint i;
08b1c66e 320
90e19f82
AM
321 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy option.c");
322 g_hash_table_foreach(options, list_options, list);
323 g_hash_table_destroy(options);
08b1c66e 324
90e19f82
AM
325 for(i = 0 ; i < list->len ; i++) {
326 free_option((LttvOption *)list->pdata[i]);
327 }
328 g_ptr_array_free(list, TRUE);
08b1c66e 329}
330
331LTTV_MODULE("option", "Command line options processing", \
90e19f82
AM
332 "Functions to add, remove and parse command line options", \
333 init, destroy)
This page took 0.086778 seconds and 4 git commands to generate.