1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | |
20 | #include <glib.h> |
21 | #include <glib/gi18n.h> |
22 | #include <glib/gprintf.h> |
23 | |
24 | #include "command.h" |
25 | |
26 | #define command_arg_get(cmd, at)(cmd)->argv[(at) + 1] (cmd)->argv[(at) + 1] |
27 | |
28 | static void command_argument_free (void *x); |
29 | |
30 | |
31 | typedef union { |
32 | gboolean vbool; |
33 | gint vint; |
34 | gchar *vstring; |
35 | gchar **vstringv; |
36 | } command_argument_value_t; |
37 | |
38 | typedef enum { |
39 | COMMAND_ARGUMENT_TYPE_BOOLEAN = G_OPTION_ARG_NONE, |
40 | COMMAND_ARGUMENT_TYPE_INT = G_OPTION_ARG_INT, |
41 | COMMAND_ARGUMENT_TYPE_STRING = G_OPTION_ARG_STRING, |
42 | COMMAND_ARGUMENT_TYPE_STRING_ARRAY = G_OPTION_ARG_STRING_ARRAY, |
43 | } command_argument_type_t; |
44 | |
45 | typedef struct { |
46 | command_argument_type_t type; |
47 | command_argument_value_t value; |
48 | } command_argument_t; |
49 | |
50 | struct command_St { |
51 | gchar *name; |
52 | gint argc; |
53 | gchar **argv; |
54 | GHashTable *flags; |
55 | }; |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | command_t * |
67 | command_new (GOptionEntry *argdefs, gint argc, gchar **argv) |
68 | { |
69 | command_t *cmd; |
70 | GOptionContext *context; |
71 | GError *error = NULL((void*)0); |
72 | gint i; |
73 | |
74 | cmd = g_new0 (command_t, 1)((command_t *) g_malloc0_n ((1), sizeof (command_t))); |
75 | cmd->argc = argc; |
76 | cmd->argv = argv; |
77 | cmd->flags = g_hash_table_new_full (g_str_hash, g_str_equal, |
78 | g_free, command_argument_free); |
79 | |
80 | for (i = 0; argdefs && argdefs[i].long_name; ++i) { |
81 | command_argument_t *arg = g_new (command_argument_t, 1)((command_argument_t *) g_malloc_n ((1), sizeof (command_argument_t ))); |
82 | |
83 | switch (argdefs[i].arg) { |
84 | case G_OPTION_ARG_NONE: |
85 | arg->type = COMMAND_ARGUMENT_TYPE_BOOLEAN; |
86 | arg->value.vbool = FALSE(0); |
87 | argdefs[i].arg_data = &arg->value.vbool; |
88 | break; |
89 | |
90 | case G_OPTION_ARG_INT: |
91 | arg->type = COMMAND_ARGUMENT_TYPE_INT; |
92 | arg->value.vint = -1; |
93 | argdefs[i].arg_data = &arg->value.vint; |
94 | break; |
95 | |
96 | case G_OPTION_ARG_STRING: |
97 | arg->type = COMMAND_ARGUMENT_TYPE_STRING; |
98 | arg->value.vstring = NULL((void*)0); |
99 | argdefs[i].arg_data = &arg->value.vstring; |
100 | break; |
101 | |
102 | case G_OPTION_ARG_STRING_ARRAY: |
103 | arg->type = COMMAND_ARGUMENT_TYPE_STRING_ARRAY; |
104 | arg->value.vstringv = NULL((void*)0); |
105 | argdefs[i].arg_data = &arg->value.vstringv; |
106 | break; |
107 | |
108 | default: |
109 | g_printf (_("Trying to register a flag '%s' of invalid type!")gettext ("Trying to register a flag '%s' of invalid type!"), |
110 | argdefs[i].long_name); |
111 | break; |
112 | } |
113 | |
114 | g_hash_table_insert (cmd->flags, |
115 | g_strdup (argdefs[i].long_name), arg); |
116 | } |
117 | |
118 | context = g_option_context_new (NULL((void*)0)); |
119 | g_option_context_set_help_enabled (context, FALSE(0)); |
120 | g_option_context_set_ignore_unknown_options (context, TRUE(!(0))); |
121 | g_option_context_add_main_entries (context, argdefs, NULL((void*)0)); |
122 | g_option_context_parse (context, &cmd->argc, &cmd->argv, &error); |
123 | g_option_context_free (context); |
124 | |
125 | if (error) { |
126 | g_printf (_("Error: %s\n")gettext ("Error: %s\n"), error->message); |
127 | g_error_free (error); |
128 | command_free (cmd); |
129 | return NULL((void*)0); |
130 | } |
131 | |
132 | |
133 | |
134 | for (i = 0; i < cmd->argc; i++) { |
135 | if (strcmp (cmd->argv[i], "--") == 0) { |
136 | break; |
137 | } |
138 | if (cmd->argv[i][0] == '-' && cmd->argv[i][1] != '\0' && |
139 | !(cmd->argv[i][1] >= '0' && cmd->argv[i][1] <= '9')) { |
140 | |
141 | g_printf (_("Error: Unknown option '%s'\n")gettext ("Error: Unknown option '%s'\n"), cmd->argv[i]); |
142 | command_free (cmd); |
143 | return NULL((void*)0); |
144 | } |
145 | } |
146 | if (i != cmd->argc) { |
147 | for (i++; i < cmd->argc; i++) { |
148 | argv[i-1] = argv[i]; |
149 | } |
150 | cmd->argc--; |
151 | } |
152 | |
153 | |
154 | cmd->argv[cmd->argc] = NULL((void*)0); |
155 | return cmd; |
156 | } |
157 | |
158 | |
159 | static void |
160 | command_argument_free (void *x) |
161 | { |
162 | command_argument_t *arg = (command_argument_t *)x; |
163 | |
164 | if (arg->type == COMMAND_ARGUMENT_TYPE_STRING && arg->value.vstring) { |
165 | g_free (arg->value.vstring); |
166 | } |
167 | g_free (arg); |
168 | } |
169 | |
170 | void |
171 | command_free (command_t *cmd) |
172 | { |
173 | g_hash_table_destroy (cmd->flags); |
174 | g_free (cmd->name); |
175 | g_free (cmd); |
176 | } |
177 | |
178 | gboolean |
179 | command_flag_boolean_get (command_t *cmd, const gchar *name, gboolean *v) |
180 | { |
181 | command_argument_t *arg; |
182 | gboolean retval = FALSE(0); |
183 | |
184 | arg = (command_argument_t *) g_hash_table_lookup (cmd->flags, name); |
185 | if (arg && arg->type == COMMAND_ARGUMENT_TYPE_BOOLEAN) { |
186 | *v = arg->value.vbool; |
187 | retval = TRUE(!(0)); |
188 | } |
189 | |
190 | return retval; |
191 | } |
192 | |
193 | gboolean |
194 | command_flag_int_get (command_t *cmd, const gchar *name, gint *v) |
195 | { |
196 | command_argument_t *arg; |
197 | gboolean retval = FALSE(0); |
198 | |
199 | |
200 | arg = (command_argument_t *) g_hash_table_lookup (cmd->flags, name); |
201 | if (arg && arg->type == COMMAND_ARGUMENT_TYPE_INT && arg->value.vint >= 0) { |
202 | *v = arg->value.vint; |
203 | retval = TRUE(!(0)); |
204 | } |
205 | |
206 | return retval; |
207 | } |
208 | |
209 | gboolean |
210 | command_flag_string_get (command_t *cmd, const gchar *name, const gchar **v) |
211 | { |
212 | command_argument_t *arg; |
213 | gboolean retval = FALSE(0); |
214 | |
215 | arg = (command_argument_t *) g_hash_table_lookup (cmd->flags, name); |
216 | if (arg && arg->type == COMMAND_ARGUMENT_TYPE_STRING && arg->value.vstring) { |
217 | *v = arg->value.vstring; |
218 | retval = TRUE(!(0)); |
219 | } |
220 | |
221 | return retval; |
222 | } |
223 | |
224 | |
225 | |
226 | gboolean |
227 | command_flag_stringlist_get (command_t *cmd, const gchar *name, const gchar ***v) |
228 | { |
229 | const gchar *full; |
230 | gboolean retval = FALSE(0); |
231 | |
232 | if (command_flag_string_get (cmd, name, &full) && full) { |
233 | |
234 | *v = (const gchar **) g_strsplit (full, ",", MAX_STRINGLIST_TOKENS10); |
235 | retval = TRUE(!(0)); |
236 | } |
237 | |
238 | return retval; |
239 | } |
240 | |
241 | gboolean |
242 | command_flag_stringarray_get (command_t *cmd, const gchar *name, const gchar ***v) |
243 | { |
244 | command_argument_t *arg; |
245 | gboolean retval = FALSE(0); |
246 | |
247 | arg = (command_argument_t *) g_hash_table_lookup (cmd->flags, name); |
248 | if (arg && arg->type == COMMAND_ARGUMENT_TYPE_STRING_ARRAY && arg->value.vstringv) { |
249 | *v = (const gchar **) arg->value.vstringv; |
250 | retval = TRUE(!(0)); |
251 | } |
252 | |
253 | return retval; |
254 | } |
255 | |
256 | void |
257 | command_name_set (command_t *cmd, const gchar *name) |
258 | { |
259 | cmd->name = g_strdup (name); |
260 | } |
261 | |
262 | gchar * |
263 | command_name_get (command_t *cmd) |
264 | { |
265 | return cmd->name; |
266 | } |
267 | |
268 | gint |
269 | command_arg_count (command_t *cmd) |
270 | { |
271 | return cmd->argc - 1; |
272 | } |
273 | |
274 | gchar ** |
275 | command_argv_get (command_t *cmd) |
276 | { |
277 | return cmd->argv + 1; |
278 | } |
279 | |
280 | gboolean |
281 | command_arg_int_get (command_t *cmd, gint at, gint *v) |
282 | { |
283 | gboolean retval = FALSE(0); |
284 | |
285 | if (at < command_arg_count (cmd)) { |
286 | *v = strtol (command_arg_get (cmd, at)(cmd)->argv[(at) + 1], NULL((void*)0), 10); |
287 | retval = TRUE(!(0)); |
288 | } |
289 | |
290 | return retval; |
291 | } |
292 | |
293 | gboolean |
294 | command_arg_string_get (command_t *cmd, gint at, const gchar **v) |
295 | { |
296 | gboolean retval = FALSE(0); |
297 | |
298 | if (at < command_arg_count (cmd)) { |
299 | *v = command_arg_get (cmd, at)(cmd)->argv[(at) + 1]; |
300 | retval = TRUE(!(0)); |
301 | } |
302 | |
303 | return retval; |
304 | } |
305 | |
306 | |
307 | |
308 | |
309 | gboolean |
310 | command_arg_longstring_get (command_t *cmd, gint at, gchar **v) |
311 | { |
312 | gboolean retval = FALSE(0); |
313 | |
314 | if (at < command_arg_count (cmd)) { |
315 | *v = g_strjoinv (" ", &(command_arg_get (cmd, at)(cmd)->argv[(at) + 1])); |
316 | retval = TRUE(!(0)); |
317 | } |
318 | |
319 | return retval; |
320 | } |
321 | |
322 | |
323 | |
324 | static gchar * |
325 | strescape (gchar *s, const gchar *toescape, gchar escape_char) |
326 | { |
327 | gint len; |
328 | gchar *t, *r; |
329 | |
330 | t = s; |
331 | for (len = 0; *t != '\0'; len++, t++) { |
332 | if (strchr (toescape, *t)) { |
333 | len++; |
334 | } |
335 | } |
336 | r = g_new0 (gchar, len+1)((gchar *) g_malloc0_n ((len+1), sizeof (gchar))); |
337 | t = r; |
338 | while (*s) { |
339 | if (strchr (toescape, *s)) { |
340 | *t = escape_char; |
341 | t++; |
342 | } |
343 | *t = *s; |
344 | s++; |
345 | t++; |
346 | } |
347 | |
348 | return r; |
349 | } |
350 | |
351 | |
352 | |
353 | gboolean |
354 | command_arg_longstring_get_escaped (command_t *cmd, gint at, gchar **v) |
355 | { |
356 | gboolean retval = FALSE(0); |
357 | gchar **args; |
358 | gint i, len, count = command_arg_count (cmd); |
359 | |
360 | len = count-at+1; |
361 | if (at < count) { |
362 | args = g_new0 (gchar *, len)((gchar * *) g_malloc0_n ((len), sizeof (gchar *))); |
363 | args[len-1] = NULL((void*)0); |
364 | for (i = at; i < count; i++) { |
365 | args[i-at] = strescape (command_arg_get (cmd, i)(cmd)->argv[(i) + 1], " ", '\\'); |
366 | } |
367 | *v = g_strjoinv (" ", args); |
368 | |
369 | for (i = at; i < count; i++) { |
370 | g_free (args[i-at]); |
371 | } |
372 | g_free (args); |
373 | |
374 | retval = TRUE(!(0)); |
375 | } |
376 | |
377 | return retval; |
378 | } |
379 | |
380 | static guint |
381 | parse_time_sep (const gchar *s, gchar **endptr) |
382 | { |
383 | gint i; |
384 | guint v; |
385 | const gchar *t; |
386 | |
387 | v = 0; |
388 | t = s; |
389 | for (i = 0; i < 3 && *t != '\0'; i++) { |
| 10 | | Assuming the condition is false | |
|
| 11 | | Loop condition is false. Execution continues on line 394 | |
|
390 | v = v*60 + strtol (t, endptr, 10); |
391 | t = *endptr+1; |
392 | } |
393 | |
394 | return v; |
395 | } |
396 | |
397 | |
398 | |
399 | |
400 | |
401 | |
402 | |
403 | |
404 | |
405 | |
406 | |
407 | |
408 | |
409 | |
410 | static guint |
411 | parse_time (gchar *s, gchar **endptr, const gint *mul, const gchar **sep) |
412 | { |
413 | gint i; |
414 | guint n, v; |
415 | |
416 | if (strchr (s, ':') != NULL((void*)0)) { |
| 7 | | Assuming the condition is true | |
|
| |
417 | return parse_time_sep (s, endptr); |
| 9 | | Calling 'parse_time_sep' | |
|
| 12 | | Returning from 'parse_time_sep' | |
|
418 | } |
419 | |
420 | n = 0; |
421 | v = 0; |
422 | while (*s) { |
423 | if ('0' <= *s && *s <= '9') { |
424 | n = n*10 + (*s - '0'); |
425 | s++; |
426 | } else { |
427 | for (i = 0; sep[i] != NULL((void*)0); i++) { |
428 | if (g_str_has_prefix (s, sep[i])) { |
429 | v += mul[i]*n; |
430 | s += strlen (sep[i]); |
431 | n = 0; |
432 | break; |
433 | } |
434 | } |
435 | if (sep[i] == NULL((void*)0)) { |
436 | break; |
437 | } |
438 | } |
439 | } |
440 | v += n; |
441 | *endptr = s; |
442 | |
443 | return v; |
444 | } |
445 | |
446 | |
447 | gboolean |
448 | command_arg_time_get (command_t *cmd, gint at, command_arg_time_t *v) |
449 | { |
450 | gboolean retval = FALSE(0); |
451 | const gchar *s; |
452 | gchar *endptr; |
| 1 | 'endptr' declared without an initial value | |
|
453 | |
454 | const gchar *separators[] = {"hour", "h", "min", "m", "sec", "s", NULL((void*)0)}; |
455 | const gint multipliers[] = {3600, 3600, 60, 60, 1, 1}; |
456 | |
457 | if (at < command_arg_count (cmd) && command_arg_string_get (cmd, at, &s)) { |
| |
458 | gchar *time_arg = g_strdup (s); |
459 | if (*time_arg == '+' || *time_arg == '-') { |
| 3 | | Assuming the condition is false | |
|
| 4 | | Assuming the condition is false | |
|
| |
460 | v->type = COMMAND_ARG_TIME_OFFSET; |
461 | |
462 | v->value.offset = parse_time (time_arg + 1, &endptr, multipliers, separators); |
463 | if (*time_arg == '-') { |
464 | v->value.offset = -v->value.offset; |
465 | } |
466 | } else { |
467 | |
468 | v->type = COMMAND_ARG_TIME_POSITION; |
469 | |
470 | v->value.pos = parse_time (time_arg, &endptr, multipliers, separators); |
| |
| 13 | | Returning from 'parse_time' | |
|
471 | } |
472 | |
473 | |
474 | if (*endptr == '\0') { |
| 14 | | Dereference of undefined pointer value |
|
475 | retval = TRUE(!(0)); |
476 | } |
477 | |
478 | g_free (time_arg); |
479 | } |
480 | |
481 | return retval; |
482 | } |
483 | |
484 | |
485 | |
486 | |
487 | |
488 | |
489 | gboolean |
490 | command_arg_pattern_get (command_t *cmd, gint at, xmmsc_coll_t **v, |
491 | gboolean warn) |
492 | { |
493 | gchar *pattern = NULL((void*)0); |
494 | gboolean success = TRUE(!(0)); |
495 | |
496 | |
497 | |
498 | |
499 | command_arg_longstring_get_escaped (cmd, at, &pattern); |
500 | if (!pattern) { |
501 | if (warn) g_printf (_("Error: you must provide a pattern!\n")gettext ("Error: you must provide a pattern!\n")); |
502 | success = FALSE(0); |
503 | } else if (!xmmsc_coll_parsexmmsv_coll_parse (pattern, v)) { |
504 | if (warn) g_printf (_("Error: failed to parse the pattern!\n")gettext ("Error: failed to parse the pattern!\n")); |
505 | success = FALSE(0); |
506 | } |
507 | |
508 | g_free (pattern); |
509 | |
510 | return success; |
511 | } |
512 | |
513 | |
514 | |
515 | |
516 | |
517 | gboolean |
518 | command_arg_positions_get (command_t *cmd, gint at, |
519 | playlist_positions_t **p, gint currpos) |
520 | { |
521 | gchar *expression = NULL((void*)0); |
522 | gboolean success = TRUE(!(0)); |
523 | |
524 | command_arg_longstring_get_escaped (cmd, at, &expression); |
525 | if (!expression || !playlist_positions_parse (expression, p, currpos)) { |
526 | success = FALSE(0); |
527 | } |
528 | |
529 | g_free (expression); |
530 | |
531 | return success; |
532 | } |