Bug Summary

File:build-analysis/../src/clients/nycli/command.c
Warning:line 474, column 7
Dereference of undefined pointer value

Annotated Source Code

1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2017 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU 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 program 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 * General Public License for more details.
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
28static void command_argument_free (void *x);
29
30
31typedef union {
32 gboolean vbool;
33 gint vint;
34 gchar *vstring;
35 gchar **vstringv;
36} command_argument_value_t;
37
38typedef 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
45typedef struct {
46 command_argument_type_t type;
47 command_argument_value_t value;
48} command_argument_t;
49
50struct command_St {
51 gchar *name;
52 gint argc;
53 gchar **argv;
54 GHashTable *flags;
55};
56
57/* Parse the argv array with GOptionContext, using the given argument
58 * definitions, and return a command context structure containing
59 * argument values.
60 * Note: The lib doesn't like argv starting with a flag, so keep a
61 * token before that to avoid problems.
62 *
63 * The passed argv should be an array of length argc+1. (So that a terminating
64 * NULL-pointer can be added in argv[argc].)
65 */
66command_t *
67command_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)); /* runs exit(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 /* strip --, check for unknown options before it */
133 /* FIXME: We do not parse options elsewhere, do we? */
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 /* Some functions rely on NULL-termination. */
154 cmd->argv[cmd->argc] = NULL((void*)0);
155 return cmd;
156}
157
158
159static void
160command_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
170void
171command_free (command_t *cmd)
172{
173 g_hash_table_destroy (cmd->flags);
174 g_free (cmd->name);
175 g_free (cmd);
176}
177
178gboolean
179command_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
193gboolean
194command_flag_int_get (command_t *cmd, const gchar *name, gint *v)
195{
196 command_argument_t *arg;
197 gboolean retval = FALSE(0);
198
199 /* A negative value means the value was not set. */
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
209gboolean
210command_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/* Extract the flag value as a list of string items.
225 * Warning: the resulting string must be freed using g_strfreev() !*/
226gboolean
227command_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 /* Force cast to suppress warning, Don't panic! */
234 *v = (const gchar **) g_strsplit (full, ",", MAX_STRINGLIST_TOKENS10);
235 retval = TRUE(!(0));
236 }
237
238 return retval;
239}
240
241gboolean
242command_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
256void
257command_name_set (command_t *cmd, const gchar *name)
258{
259 cmd->name = g_strdup (name);
260}
261
262gchar *
263command_name_get (command_t *cmd)
264{
265 return cmd->name;
266}
267
268gint
269command_arg_count (command_t *cmd)
270{
271 return cmd->argc - 1;
272}
273
274gchar **
275command_argv_get (command_t *cmd)
276{
277 return cmd->argv + 1;
278}
279
280gboolean
281command_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
293gboolean
294command_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/* Grab all the arguments after the index as a single string.
307 * Warning: the string must be freed manually afterwards!
308 */
309gboolean
310command_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/* Escape characters in toescape with escape_char.
323 */
324static gchar *
325strescape (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/* Like command_arg_longstring_get but escape spaces with '\'.
352 */
353gboolean
354command_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
380static guint
381parse_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 * Parse time expressions of the form:
399 *
400 * e0: [0-9]*:[0-9]*:[0-9]
401 * e1: ([0-9]*(hour|h|min|m|sec|s))*
402 *
403 * RFC: Be more restrictive?
404 *
405 * Actually it accepts expressions like:
406 * 1hour2min1hour1s for 2hour2min1s
407 * 1min2hour7sec for 2hour1min7sec
408 *
409 */
410static guint
411parse_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
8
Taking true branch
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/* Parse a time value, either an absolute position or an offset. */
447gboolean
448command_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)) {
2
Taking true branch
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
5
Taking false branch
460 v->type = COMMAND_ARG_TIME_OFFSET;
461 /* v->value.offset = strtol (s, &endptr, 10); */
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 /* FIXME: always signed long int anyway? */
468 v->type = COMMAND_ARG_TIME_POSITION;
469 /* v->value.pos = strtol (s, &endptr, 10); */
470 v->value.pos = parse_time (time_arg, &endptr, multipliers, separators);
6
Calling 'parse_time'
13
Returning from 'parse_time'
471 }
472
473 /* FIXME: We can have cleverer parsing for '2:17' or '3min' etc */
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/** Try to parse a collection pattern from the arguments and return
485 * success status. The resulting coll structure is saved to the v
486 * argument. In case of error, the error message is printed on
487 * stdout.
488 */
489gboolean
490command_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 /* FIXME: Need a more elegant error system. */
497 /* FIXME(g): Escape tokens? command_arg_longstring_get_escaped ? */
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/** Try to parse a positions expression from the arguments and return
514 * success status. The resulting positions structure is saved to the p
515 * argument.
516 */
517gboolean
518command_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}