Bug Summary

File:commands.c
Location:line 115, column 16
Description:Access to field 'commands' results in a dereference of a null pointer (loaded from variable 't')

Annotated Source Code

1/*
2 * Copyright (c) 2010, 2011, 2012 Ryan Flannery <ryan.flannery@gmail.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "commands.h"
18
19bool_Bool sorts_need_saving = false0;
20
21#define swap(type, x, y){ type temp = x; x = y; y = temp;} \
22 { type temp = x; x = y; y = temp;}
23
24/*
25 * List of command-mode commands. Take note of the following: XXX
26 * 1. See 'match_cmd_name()' for the handling of abbreviations.
27 * 2. Commands that accept a '!' after their names are handled
28 * in 'match_cmd_name()'.
29 */
30const cmd CommandPath[] = {
31 { "bind", cmd_bind },
32 { "color", cmd_color },
33 { "display", cmd_display },
34 { "filter", cmd_filter },
35 { "mode", cmd_mode },
36 { "new", cmd_new },
37 { "playlist", cmd_playlist },
38 { "q", cmd_quit },
39 { "reload", cmd_reload },
40 { "set", cmd_set },
41 { "sort", cmd_sort },
42 { "unbind", cmd_unbind },
43 { "w", cmd_write },
44 { "toggle", cmd_toggle }
45};
46const int CommandPathSize = (sizeof(CommandPath) / sizeof(cmd));
47
48
49/****************************************************************************
50 * Toggleset related stuff
51 ***************************************************************************/
52
53toggle_list **toggleset;
54size_t toggleset_size;
55
56void
57toggleset_init()
58{
59 const int max_size = 52; /* since we only have registers a-z and A-Z */
60 if ((toggleset = calloc(max_size, sizeof(toggle_list*))) == NULL((void *)0))
61 err(1, "%s: calloc(3) failed", __FUNCTION__);
62
63 toggleset_size = 0;
64}
65
66void
67toggleset_free()
68{
69 size_t i;
70 for (i = 0; i < toggleset_size; i++)
71 toggle_list_free(toggleset[i]);
72
73 free(toggleset);
74 toggleset_size = 0;
75}
76
77void
78toggle_list_add_command(toggle_list *t, char *cmd)
79{
80 char **new_cmds;
81 int idx;
82
83 /* resize array */
84 if (t->size == 0) {
85 if ((t->commands = malloc(sizeof(char*))) == NULL((void *)0))
86 err(1, "%s: malloc(3) failed", __FUNCTION__);
87
88 idx = 0;
89 t->size = 1;
90 } else {
91 int new_size = (t->size + 1) * sizeof(char*);
92 if ((new_cmds = realloc(t->commands, new_size)) == NULL((void *)0))
93 err(1, "%s: realloc(3) failed", __FUNCTION__);
94
95 idx = t->size;
96 t->commands = new_cmds;
97 t->size++;
98 }
99
100 /* add command */
101 if ((t->commands[idx] = strdup(cmd)) == NULL((void *)0))
102 err(1, "%s: strdup(3) failed", __FUNCTION__);
103}
104
105toggle_list*
106toggle_list_create(int registr, int argc, char *argv[])
107{
108 toggle_list *t;
109 char *cmd = NULL((void *)0);
110 int i, j;
111
112 if ((t = malloc(sizeof(toggle_list))) == NULL((void *)0))
6
Value assigned to 't'
7
Assuming pointer value is null
8
Taking true branch
113 err(1, "%s: malloc(3) failed", __FUNCTION__);
114
115 t->commands = NULL((void *)0);
9
Access to field 'commands' results in a dereference of a null pointer (loaded from variable 't')
116 t->registr = registr;
117 t->size = 0;
118
119 /* parse the argv into the toggle list */
120 for (i = 0; i < argc; i++) {
121 if (!strcmp("/", argv[i]))
122 continue;
123
124 /* count number strings in this command and determine length */
125 for (j = i; j < argc && strcmp("/", argv[j]); j++);
126
127 /* now collapse them into a single string */
128 cmd = argv2str(j - i + 1, argv + i);
129 toggle_list_add_command(t, cmd);
130 free(cmd);
131
132 i += (j - i) - 1;
133 }
134
135 t->index = t->size - 1;
136 return t;
137}
138
139void
140toggle_list_free(toggle_list *t)
141{
142 size_t i;
143
144 for (i = 0; i < t->size; i++)
145 free(t->commands[i]);
146
147 free(t);
148}
149
150void
151toggle_add(toggle_list *t)
152{
153 if (toggle_get(t->registr) != NULL((void *)0))
154 toggle_remove(t->registr);
155
156 toggleset[toggleset_size++] = t;
157}
158
159void
160toggle_remove(int registr)
161{
162 size_t i, idx;
163 bool_Bool found;
164
165 found = false0;
166 idx = 0;
167 for (i = 0; i < toggleset_size; i++) {
168 if (toggleset[i]->registr == registr) {
169 idx = i;
170 found = true1;
171 }
172 }
173
174 if (!found) return;
175
176 for (i = idx; i < toggleset_size - 1; i++)
177 toggleset[i] = toggleset[i + 1];
178
179 toggleset_size--;
180}
181
182toggle_list*
183toggle_get(int registr)
184{
185 size_t i;
186
187 for (i = 0; i < toggleset_size; i++) {
188 if (toggleset[i]->registr == registr)
189 return toggleset[i];
190 }
191
192 return NULL((void *)0);
193}
194
195
196/****************************************************************************
197 * Misc handy functions
198 ***************************************************************************/
199
200void
201setup_viewing_playlist(playlist *p)
202{
203 viewing_playlist = p;
204
205 ui.playlist->nrows = p->nfiles;
206 ui.playlist->crow = 0;
207 ui.playlist->voffset = 0;
208 ui.playlist->hoffset = 0;
209}
210
211int
212str2bool(const char *s, bool_Bool *b)
213{
214 /* check true/t/yes/y */
215 if (strcasecmp(s, "true") == 0
216 || strcasecmp(s, "t") == 0
217 || strcasecmp(s, "yes") == 0
218 || strcasecmp(s, "y") == 0) {
219 *b = true1;
220 return 0;
221 }
222
223 /* check false/f/no/n */
224 if (strcasecmp(s, "false") == 0
225 || strcasecmp(s, "f") == 0
226 || strcasecmp(s, "no") == 0
227 || strcasecmp(s, "n") == 0) {
228 *b = false0;
229 return 0;
230 }
231
232 return -1;
233}
234
235
236/****************************************************************************
237 * Command handlers
238 ***************************************************************************/
239
240int
241cmd_quit(int argc, char *argv[])
242{
243 bool_Bool forced;
244
245 if (argc != 1) {
246 paint_error("usage: q[!]");
247 return 1;
248 }
249
250 /* is this a forced quit? */
251 forced = (strcmp(argv[0], "q!") == 0);
252
253 /* check if there are any unsaved changes if not forced */
254 if (!forced) {
255 int i;
256 for (i = 0; i < mdb.nplaylists; i++) {
257 if (mdb.playlists[i]->needs_saving) {
258 paint_error("there are playlists with unsaved changes. use \"q!\" to force.");
259 return 2;
260 }
261 }
262 }
263
264 VSIG_QUIT = 1;
265 return 0;
266}
267
268int
269cmd_write(int argc, char *argv[])
270{
271 char *filename;
272 bool_Bool forced;
273
274 if (argc > 2) {
275 paint_error("usage: w[!] [name]");
276 return 1;
277 }
278
279 forced = (strcmp(argv[0], "w!") == 0);
280
281 if (argc == 1) { /* "save" */
282
283 /* can't save library or filter results */
284 if (viewing_playlist == mdb.library
285 || viewing_playlist == mdb.filter_results) {
286 paint_error("use \"w name\" when saving pseudo-playlists like library/filter");
287 return 2;
288 }
289
290 /* can't save a new playlist that has no name */
291 if (viewing_playlist->filename == NULL((void *)0)) {
292 paint_error("use \"w name\" for new playlists");
293 return 3;
294 }
295
296 /* do the save... */
297 playlist_save(viewing_playlist);
298 viewing_playlist->needs_saving = false0;
299 paint_library();
300 paint_message("\"%s\" %d songs written",
301 viewing_playlist->filename, viewing_playlist->nfiles);
302
303 } else { /* "save as" */
304 int i, clobber_index;
305 bool_Bool will_clobber;
306
307 /* build filename for playlist */
308 if (asprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, argv[1]) == -1)
309 err(1, "cmd_write: asprintf failed");
310
311 /* check to see if playlist with that name already exists */
312 will_clobber = false0;
313 clobber_index = -1;
314 for (i = 0; i < mdb.nplaylists; i++) {
315 if (mdb.playlists[i]->filename != NULL((void *)0)
316 && strcmp(mdb.playlists[i]->filename, filename) == 0) {
317 will_clobber = true1;
318 clobber_index = i;
319 }
320 }
321
322 if (will_clobber && !forced) {
323 paint_error("playlist with that name exists (use \"w!\" to overwrite)");
324 free(filename);
325 return 4;
326 }
327
328 /* if reached here, we're going to do the save-as... */
329
330 /* duplicate playlist */
331 playlist *dup = playlist_dup(viewing_playlist, filename, argv[1]);
332 if (will_clobber)
333 medialib_playlist_remove(clobber_index);
334 else
335 ui.library->nrows++;
336
337 /*
338 * TODO If the original playlist was a new one, with no name (i.e. if
339 * name == NULL) then we should remove that playlist from the medialib
340 * and display here.
341 */
342
343 /* do the save-as... */
344 playlist_save(dup);
345 medialib_playlist_add(dup);
346
347 dup->needs_saving = false0;
348 viewing_playlist->needs_saving = false0;
349
350 paint_library();
351 paint_message("\"%s\" %d songs written",
352 filename, viewing_playlist->nfiles);
353
354 free(filename);
355 }
356
357 return 0;
358}
359
360int
361cmd_mode(int argc, char *argv[])
362{
363 if (argc != 2) {
364 paint_error("usage: mode [ linear | loop | random ]");
365 return 1;
366 }
367
368 if (strcasecmp(argv[1], "linear") == 0)
369 player_info.mode = MODE_LINEAR;
370 else if (strcasecmp(argv[1], "loop") == 0)
371 player_info.mode = MODE_LOOP;
372 else if (strcasecmp(argv[1], "random") == 0)
373 player_info.mode = MODE_RANDOM;
374 else {
375 paint_error("invalid mode \"%s\". must be one of: linear, loop, or random", argv[1]);
376 return 2;
377 }
378
379 paint_message("mode changed to: %s", argv[1]);
380 return 0;
381}
382
383int
384cmd_new(int argc, char *argv[])
385{
386 playlist *p;
387 char *name;
388 char *filename;
389
390 if (argc > 2) {
391 paint_error("usage: new [name]");
392 return 1;
393 }
394
395 /* defaults */
396 name = "untitled";
397 filename = NULL((void *)0);
398
399 /* was a name specified? */
400 if (argc == 2) {
401 /* check for existing playlist with name */
402 int i;
403 for (i = 0; i < mdb.nplaylists; i++) {
404 if (strcmp(mdb.playlists[i]->name, argv[1]) == 0) {
405 paint_error("playlist \"%s\" already exists.", argv[1]);
406 return 2;
407 }
408 }
409
410 name = argv[1];
411 if (asprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, name) == -1)
412 err(1, "cmd_new: asprintf failed");
413 }
414
415 /* create & setup playlist */
416 p = playlist_new();
417 p->needs_saving = true1;
418 p->filename = filename;
419 if ((p->name = strdup(name)) == NULL((void *)0))
420 err(1, "cmd_new: strdup(3) failed");
421
422 /* add playlist to media library and update ui */
423 medialib_playlist_add(p);
424 ui.library->nrows++;
425 if (p->filename != NULL((void *)0))
426 playlist_save(p);
427
428 /* redraw */
429 paint_library();
430 paint_message("playlist \"%s\" added", name);
431
432 return 0;
433}
434
435int
436cmd_filter(int argc, char *argv[])
437{
438 playlist *results;
439 char *search_phrase;
440 bool_Bool match;
441 int i;
442
443 if (argc == 1) {
444 paint_error("usage: filter[!] token [token2 ...]");
445 return 1;
446 }
447
448 /* determine what kind of filter we're doing */
449 match = argv[0][strlen(argv[0]) - 1] != '!';
450
451 /* set the raw query */
452 search_phrase = argv2str(argc - 1, argv + 1);
453 mi_query_setraw(search_phrase);
454 free(search_phrase);
455
456 /* clear existing global query & set new one */
457 mi_query_clear();
458 for (i = 1; i < argc; i++)
459 mi_query_add_token(argv[i]);
460
461 /* do actual filter */
462 results = playlist_filter(viewing_playlist, match);
463
464 /* swap necessary bits of results with filter playlist */
465 swap(meta_info **, results->files, mdb.filter_results->files){ meta_info ** temp = results->files; results->files = mdb
.filter_results->files; mdb.filter_results->files = temp
;}
;
466 swap(int, results->nfiles, mdb.filter_results->nfiles){ int temp = results->nfiles; results->nfiles = mdb.filter_results
->nfiles; mdb.filter_results->nfiles = temp;}
;
467 swap(int, results->capacity, mdb.filter_results->capacity){ int temp = results->capacity; results->capacity = mdb
.filter_results->capacity; mdb.filter_results->capacity
= temp;}
;
468 playlist_free(results);
469
470 /* redraw */
471 setup_viewing_playlist(mdb.filter_results);
472 paint_library();
473 paint_playlist();
474
475 return 0;
476}
477
478int
479cmd_sort(int argc, char *argv[])
480{
481 const char *errmsg;
482
483 if (argc != 2) {
484 paint_error("usage: sort <sort-description>");
485 return 1;
486 }
487
488 /* setup global sort description */
489 if (mi_sort_set(argv[1], &errmsg) != 0) {
490 paint_error("%s: bad sort description: %s", argv[0], errmsg);
491 return 2;
492 }
493
494 /* do the actual sort */
495 qsort(viewing_playlist->files, viewing_playlist->nfiles,
496 sizeof(meta_info*), mi_compare);
497
498 if (!ui_is_init())
499 return 0;
500
501 /* redraw */
502 paint_playlist();
503
504 /* if we sorted a playlist other than library, and user wants to save sorts */
505 if (viewing_playlist != mdb.library && sorts_need_saving) {
506 viewing_playlist->needs_saving = true1;
507 paint_library();
508 }
509
510 return 0;
511}
512
513int
514cmd_display(int argc, char *argv[])
515{
516 const char *errmsg;
517
518 if (argc != 2) {
519 paint_error("usage: display [ reset | show | <display-description> ]");
520 return 1;
521 }
522
523 /* show existng display? */
524 if (strcasecmp(argv[1], "show") == 0) {
525 paint_message(":display %s", mi_display_tostr());
526 return 0;
527 }
528
529 /* reset display to default? */
530 if (strcasecmp(argv[1], "reset") == 0) {
531 mi_display_reset();
532 if (ui_is_init())
533 paint_playlist();
534
535 return 0;
536 }
537
538 /* if reached here, setup global display description */
539
540 if (mi_display_set(argv[1], &errmsg) != 0) {
541 paint_error("%s: bad display description: %s", argv[0], errmsg);
542 return 1;
543 }
544
545 if(ui_is_init())
546 paint_playlist();
547
548 return 0;
549}
550
551int
552cmd_color(int argc, char *argv[])
553{
554 char *item;
555 char *fg, *bg;
556 int i_item, i_fg, i_bg, j;
557
558 if (argc != 2) {
559 paint_error("usage: %s ITEM=FG,BG", argv[0]);
560 return 1;
561 }
562
563 /* extract item and foreground/background colors */
564 item = argv[1];
565 if ((fg = strchr(item, '=')) == NULL((void *)0)) {
566 paint_error("usage: %s ITEM=FG,BG", argv[0]);
567 return 2;
568 }
569 *fg = '\0';
570 fg++;
571
572 if ((bg = strchr(fg, ',')) == NULL((void *)0)) {
573 paint_error("usage: %s ITEM=FG,BG", argv[0]);
574 return 3;
575 }
576 *bg = '\0';
577 bg++;
578
579 /* convert all */
580 if ((i_item = paint_str2item(item)) < 0) {
581 paint_error("invalid item '%s'", item);
582 return 4;
583 }
584
585 if ((i_fg = paint_str2color(fg)) == -2) {
586 paint_error("invalid foreground color '%s'", fg);
587 return 5;
588 }
589
590 if ((i_bg = paint_str2color(bg)) == -2) {
591 paint_error("invalid background color '%s'", bg);
592 return 6;
593 }
594
595 /* init color */
596 init_pair(i_item, i_fg, i_bg);
597
598 /* if this was a cinfo item, indicate that it was set */
599 for (j = 0; j < MI_NUM_CINFO8; j++) {
600 if (i_item == colors.cinfos[j])
601 colors.cinfos_set[j] = true1;
602 }
603
604 /* redraw */
605 if (ui_is_init()) {
606 ui_clear();
607 paint_all();
608 }
609
610 return 0;
611}
612
613int
614cmd_set(int argc, char *argv[])
615{
616 const char *err;
617 char *property;
618 char *value;
619 bool_Bool tf;
620 bool_Bool player_is_setup;
621
622 if (argc != 2) {
623 paint_error("usage: %s <property>=<value>", argv[0]);
624 return 1;
625 }
626
627 /* determine if the player has been setup (needed for redraws below) */
628 player_is_setup = (player.name != NULL((void *)0))
629 && (*player.name != '\0');
630
631 /* extract property and value */
632 property = argv[1];
633 if ((value = strchr(property, '=')) == NULL((void *)0)) {
634 paint_error("usage: %s <property>=<value>", argv[0]);
635 return 2;
636 }
637 *value = '\0';
638 value++;
639
640 /* handle property */
641
642 if (strcasecmp(property, "lwidth") == 0) {
643 /* get max width and height */
644 int max_w, new_width;
645 max_w = getmaxx(stdscr)((stdscr) ? ((stdscr)->_maxx + 1) : (-1));
646
647 /* validate and convert width user provided */
648 new_width = (int)strtonum(value, 1, max_w, &err);
649 if (err != NULL((void *)0)) {
650 paint_error("%s %s: bad width: '%s' %s",
651 argv[0], property, value, err);
652 return 3;
653 }
654
655 /* resize & redraw (if we're past setup & curses is running) */
656 ui.lwidth = new_width;
657 ui_resize();
658 if(player_is_setup && ui_is_init()) {
659 ui_clear();
660 paint_all();
661 }
662
663 } else if (strcasecmp(property, "lhide") == 0) {
664 if (str2bool(value, &tf) < 0) {
665 paint_error("%s %s: value must be boolean",
666 argv[0], property);
667 return 4;
668 }
669 ui.lhide = tf;
670 if (ui.lhide) {
671 if (ui.active == ui.playlist)
672 ui_hide_library();
673 if (player_is_setup && ui_is_init()) {
674 ui_clear();
675 paint_all();
676 paint_message("library window hidden");
677 }
678 } else {
679 if (ui.library->cwin == NULL((void *)0)) ui_unhide_library();
680 if (player_is_setup && ui_is_init()) paint_all();
681 paint_message("library window un-hidden");
682 }
683
684 } else if (strcasecmp(property, "match-fname") == 0) {
685 if (str2bool(value, &tf) < 0) {
686 paint_error("%s %s: value must be boolean",
687 argv[0], property);
688 return 5;
689 }
690 mi_query_match_filename = tf;
691 if (mi_query_match_filename)
692 paint_message("filenames will be matched against");
693 else
694 paint_message("filenames will NOT be matched against");
695
696 } else if (strcasecmp(property, "save-sorts") == 0) {
697 if (str2bool(value, &tf) < 0) {
698 paint_error("%s %s: value must be boolean",
699 argv[0], property);
700 return 6;
701 }
702 sorts_need_saving = tf;
703 if (sorts_need_saving)
704 paint_message("changing sort will be prompted for saving");
705 else
706 paint_message("changing sort will NOT be prompted for saving");
707
708 } else {
709 paint_error("%s: unknown property '%s'", argv[0], property);
710 return 7;
711 }
712
713 return 0;
714}
715
716int
717cmd_reload(int argc, char *argv[])
718{
719 if (argc != 2) {
720 paint_error("usage: %s [ db | conf ]", argv[0]);
721 return 1;
722 }
723
724 /* reload database or config file */
725 if (strcasecmp(argv[1], "db") == 0) {
726
727 char *db_file = strdup(mdb.db_file);
728 char *playlist_dir = strdup(mdb.playlist_dir);
729 if (db_file == NULL((void *)0) || playlist_dir == NULL((void *)0))
730 err(1, "cmd_reload: strdup(3) failed");
731
732 /* stop playback TODO investigate a nice way around this */
733 player_stop();
734
735 /* reload db */
736 medialib_destroy();
737 medialib_load(db_file, playlist_dir);
738
739 /* sort entries */
740 qsort(mdb.library->files, mdb.library->nfiles, sizeof(meta_info*), mi_compare);
741
742 free(db_file);
743 free(playlist_dir);
744
745 /* re-setup ui basics */
746 playing_playlist = NULL((void *)0);
747 setup_viewing_playlist(mdb.library);
748 ui.library->voffset = 0;
749 ui.library->nrows = mdb.nplaylists;
750 ui.library->crow = 0;
751 paint_all();
752
753 } else if (strcasecmp(argv[1], "conf") == 0) {
754 load_config();
755 paint_message("configuration reloaded");
756 } else {
757 paint_error("usage: %s [ db | conf ]", argv[0]);
758 return 2;
759 }
760
761 return 0;
762}
763
764int
765cmd_bind(int argc, char *argv[])
766{
767 KeyAction action;
768 KeyCode code;
769
770 if (argc < 3 || argc > 4) {
771 paint_error("usage: %s <action> <keycode>", argv[0]);
772 return 1;
773 }
774
775 if (!kb_str2action(argv[1], &action)) {
776 paint_error("Unknown action '%s'", argv[1]);
777 return 1;
778 }
779
780 if (argc == 3) {
781 if ((code = kb_str2keycode(argv[2])) < 0) {
782 paint_error("Invalid keycode '%s'", argv[2]);
783 return 1;
784 }
785 } else {
786 if ((code = kb_str2keycode2(argv[2], argv[3])) < 0) {
787 paint_error("Invalid keycode '%s'", argv[2]);
788 return 1;
789 }
790 }
791
792 kb_bind(action, code);
793 return 0;
794}
795
796int
797cmd_unbind(int argc, char *argv[])
798{
799 KeyAction action;
800 KeyCode key;
801
802 /* unbind all case ("unbind *") */
803 if (argc == 2 && strcmp(argv[1], "*") == 0) {
804 kb_unbind_all();
805 return 0;
806 }
807
808 /* unbind action case ("unbind action <ACTION>") */
809 if (argc == 3 && strcasecmp(argv[1], "action") == 0) {
810 if (kb_str2action(argv[2], &action)) {
811 kb_unbind_action(action);
812 return 0;
813 } else {
814 paint_error("Unknown action '%s'", argv[2]);
815 return 1;
816 }
817 }
818
819 /* unbind key case, no control ("unbind key X") */
820 if (argc == 3 && strcasecmp(argv[1], "key") == 0) {
821 if ((key = kb_str2keycode(argv[2])) < 0) {
822 paint_error("Invalid keycode '%s'", argv[2]);
823 return 1;
824 }
825
826 kb_unbind_key(key);
827 return 0;
828 }
829
830 /* unbind key case, with control ("unbind key control X") */
831 if (argc == 4 && strcasecmp(argv[1], "key") == 0) {
832 if ((key = kb_str2keycode2(argv[2], argv[3])) < 0) {
833 paint_error("Invalid keycode '%s %s'", argv[2], argv[3]);
834 return 1;
835 }
836
837 kb_unbind_key(key);
838 return 0;
839 }
840
841 paint_error("usage: unbind [* | action <ACTION> | key <KEYCODE> ]");
842 return 1;
843}
844
845int
846cmd_toggle(int argc, char *argv[])
847{
848 toggle_list *t;
849 char **cmd_argv;
850 int cmd_argc;
851 int registr;
852
853 if (argc < 3) {
1
Assuming 'argc' is >= 3
2
Taking false branch
854 paint_error("usage: %s <register> <action1> / ...", argv[0]);
855 return 1;
856 }
857
858 if (strlen(argv[1]) != 1) {
3
Taking false branch
859 paint_error("error: register name must be a single letter (in [a-zA-Z])");
860 return 1;
861 }
862
863 registr = *(argv[1]);
864
865 if (!( ('a' <= registr && registr <= 'z')
4
Taking false branch
866 || ('A' <= registr && registr <= 'Z'))) {
867 paint_error("error: invalid register name. Must be one of [a-zA-Z]");
868 return 1;
869 }
870
871 cmd_argc = argc - 2;
872 cmd_argv = argv + 2;
873 t = toggle_list_create(registr, cmd_argc, cmd_argv);
5
Calling 'toggle_list_create'
874 toggle_add(t);
875 return 0;
876}
877
878int
879cmd_playlist(int argc, char *argv[])
880{
881 int x;
882 int idx = -1;
883
884 if (argc != 2) {
885 paint_error("usage: playlist <list-name>");
886 return 1;
887 }
888
889 for(x = 0; x < mdb.nplaylists; x++) {
890 if(!strncmp(argv[1], mdb.playlists[x]->name, strlen(argv[1]))) {
891 if(idx > -1) {
892 idx = -2;
893 break;
894 }
895
896 if(idx == -1)
897 idx = x;
898 }
899 }
900
901 if(idx > -1) {
902 setup_viewing_playlist(mdb.playlists[idx]);
903 ui.active = ui.playlist;
904 paint_all();
905 paint_message("jumped to playlist: %s", mdb.playlists[idx]->name);
906 return 0;
907 }
908
909 if(idx == -1) {
910 paint_error("no match for: %s", argv[1]);
911 return 0;
912 }
913
914 if(idx == -2)
915 paint_error("no unique match for: %s", argv[1]);
916
917 return 0;
918}
919
920void
921cmd_execute(char *cmd)
922{
923 const char *errmsg = NULL((void *)0);
924 bool_Bool found;
925 char **argv;
926 int argc;
927 int found_idx = 0;
928 int num_matches;
929 int i;
930
931 if (str2argv(cmd, &argc, &argv, &errmsg) != 0) {
932 paint_error("parse error: %s in '%s'", errmsg, cmd);
933 return;
934 }
935
936 found = false0;
937 num_matches = 0;
938 for (i = 0; i < CommandPathSize; i++) {
939 if (match_command_name(argv[0], CommandPath[i].name)) {
940 found = true1;
941 found_idx = i;
942 num_matches++;
943 }
944 }
945
946 if (found && num_matches == 1)
947 (CommandPath[found_idx].func)(argc, argv);
948 else if (num_matches > 1)
949 paint_error("Ambiguous abbreviation '%s'", argv[0]);
950 else
951 paint_error("Unknown commands '%s'", argv[0]);
952
953 argv_free(&argc, &argv);
954}
955
956
957/*****************************************************************************
958 * command window input methods
959 ****************************************************************************/
960
961/*
962 * Note: Both of these return 0 if input was successfull, and something else
963 * (1) if the user cancelled the input (such as, by hitting ESCAPE)
964 */
965
966int
967user_getstr(const char *prompt, char **response)
968{
969 const int MAX_INPUT_SIZE = 1000; /* TODO remove this limit */
970 char *input;
971 int pos, ch, ret;
972
973 /* display the prompt */
974 werase(ui.command);
975 mvwprintw(ui.command, 0, 0, "%s", prompt);
976
977 /* position the cursor */
978 curs_set(1);
979 wmove(ui.command, 0, strlen(prompt));
980 wrefresh(ui.command);
981
982 /* allocate input space and clear */
983 if ((input = calloc(MAX_INPUT_SIZE, sizeof(char))) == NULL((void *)0))
984 err(1, "user_getstr: calloc(3) failed for input string");
985
986 bzero(input, MAX_INPUT_SIZE);
987
988 /* start getting input */
989 ret = 0;
990 pos = 0;
991 while ((ch = getch()wgetch(stdscr)) && !VSIG_QUIT) {
992
993 /*
994 * Handle any signals. Note that the use of curs_set, wmvoe, and
995 * wrefresh here are all necessary to ensure that the cursor does
996 * not show anywhere outside of the command window.
997 */
998 curs_set(0);
999 process_signals();
1000 curs_set(1);
1001 wmove(ui.command, 0, strlen(prompt) + pos);
1002 wrefresh(ui.command);
1003
1004 if (ch == ERR(-1))
1005 continue;
1006
1007 if (ch == '\n' || ch == 13)
1008 break;
1009
1010 /* handle 'escape' */
1011 if (ch == 27) {
1012 ret = 1;
1013 goto end;
1014 }
1015
1016 /* handle 'backspace' / left-arrow, etc. */
1017 if (ch == 127 || ch == KEY_BACKSPACE0407 || ch == KEY_LEFT0404
1018 || ch == KEY_DC0512 || ch == KEY_SDC0577) {
1019 if (pos == 0) {
1020 if (ch == KEY_BACKSPACE0407) {
1021 ret = 1;
1022 goto end;
1023 }
1024 beep();
1025 } else {
1026 mvwaddch(ui.command, 0, strlen(prompt) + pos - 1, ' ')(wmove(ui.command,0,strlen(prompt) + pos - 1) == (-1) ? (-1) :
waddch(ui.command,' '))
;
1027 wmove(ui.command, 0, strlen(prompt) + pos - 1);
1028 wrefresh(ui.command);
1029 pos--;
1030 }
1031 continue;
1032 }
1033
1034 /* got regular input. add to buffer. */
1035 input[pos] = ch;
1036 mvwaddch(ui.command, 0, strlen(prompt) + pos, ch)(wmove(ui.command,0,strlen(prompt) + pos) == (-1) ? (-1) : waddch
(ui.command,ch))
;
1037 wrefresh(ui.command);
1038 pos++;
1039
1040 /* see todo above - realloc input buffer here if position reaches max */
1041 if (pos >= MAX_INPUT_SIZE)
1042 errx(1, "user_getstr: shamefull limit reached");
1043 }
1044
1045 /* For lack of input, bail out */
1046 if (pos == 0) {
1047 ret = 1;
1048 goto end;
1049 }
1050
1051 /* NULL-terminate and trim off trailing whitespace */
1052 input[pos--] = '\0';
1053 for (; input[pos] == ' ' && pos >= 0; pos--)
1054 input[pos] = '\0';
1055
1056 /* trim the fat */
1057 if ((*response = calloc(strlen(input) + 1, sizeof(char))) == NULL((void *)0))
1058 err(1, "user_getstr: calloc(3) failed for result");
1059
1060 snprintf(*response, strlen(input) + 1, "%s", input);
1061
1062end:
1063 free(input);
1064 curs_set(0);
1065 return ret;
1066}
1067
1068int
1069user_get_yesno(const char *msg, int *response)
1070{
1071 char *answer;
1072
1073 if (user_getstr(msg, &answer) != 0)
1074 return 1;
1075
1076 if (strncasecmp(answer, "yes", 3) == 0
1077 || strncasecmp(answer, "y", 1) == 0)
1078 *response = 1;
1079 else if (strncasecmp(answer, "no", 2) == 0
1080 || strncasecmp(answer, "n", 1) == 0)
1081 *response = 0;
1082 else
1083 *response = -1;
1084
1085 free(answer);
1086 return 0;
1087}
1088