diff --git a/DEVELOPERS.txt b/DEVELOPERS similarity index 100% rename from DEVELOPERS.txt rename to DEVELOPERS diff --git a/Makefile b/Makefile index 27b1946..a1e4bbd 100644 --- a/Makefile +++ b/Makefile @@ -11,19 +11,20 @@ LDEPS=$(TAGLIB_LIBS) $(GSTREAMER_LIBS) ODEPS=$(GSTREAMER_OBJS) # build variables -CC ?= /usr/bin/cc -CFLAGS += -c -std=c89 -Wall -Wextra -Wno-unused-value $(CDEBUG) $(CDEPS) -LIBS += -lm -lncurses -lutil $(LDEPS) +CC ?= /usr/bin/cc +CPPFLAGS += -I. -Icompat -Iplayers -iquote. +CFLAGS += -c -std=c89 -Wall -Wextra -Wno-unused-value $(CDEBUG) $(CDEPS) +LIBS += -lm -lncurses -lutil $(LDEPS) # object files -OBJS=commands.o compat.o ecmd.o \ +OBJS=commands.o compat.o ecmd.o ecmd_args.o \ ecmd_add.o ecmd_addurl.o ecmd_check.o \ ecmd_flush.o ecmd_help.o ecmd_init.o \ ecmd_rmfile.o ecmd_tag.o ecmd_update.o \ - keybindings.o medialib.o meta_info.o \ + error.o keybindings.o medialib.o meta_info.o \ mplayer.o paint.o player.o player_utils.o \ playlist.o socket.o str2argv.o \ - uinterface.o vitunes.o \ + uinterface.o vitunes.o xmalloc.o \ $(ODEPS) # subdirectories with code (.PATH for BSD make, VPATH for GNU make) @@ -39,10 +40,10 @@ vitunes: $(OBJS) $(CC) -o $@ $(LDFLAGS) $(OBJS) $(LIBS) .c.o: - $(CC) $(CFLAGS) $< + $(CC) $(CPPFLAGS) $(CFLAGS) $< debug: - make CDEBUG="-DDEBUG -g" + make CDEBUG="-g" clean: rm -f *.o @@ -93,4 +94,3 @@ reports: report.mandoc report.cppcheck report.scan-build @figlet "Static Checks Complete" cat report.mandoc cat report.cppcheck - diff --git a/commands.c b/commands.c index e310fca..df03fdb 100644 --- a/commands.c +++ b/commands.c @@ -14,7 +14,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include + #include "commands.h" +#include "enums.h" +#include "error.h" +#include "keybindings.h" +#include "medialib.h" +#include "paint.h" +#include "player.h" +#include "str2argv.h" +#include "uinterface.h" +#include "vitunes.h" +#include "xmalloc.h" bool sorts_need_saving = false; @@ -57,9 +70,8 @@ void toggleset_init() { const int max_size = 52; /* since we only have registers a-z and A-Z */ - if ((toggleset = calloc(max_size, sizeof(toggle_list*))) == NULL) - err(1, "%s: calloc(3) failed", __FUNCTION__); + toggleset = xcalloc(max_size, sizeof(toggle_list*)); toggleset_size = 0; } @@ -77,29 +89,20 @@ toggleset_free() void toggle_list_add_command(toggle_list *t, char *cmd) { - char **new_cmds; - int idx; + int idx; /* resize array */ if (t->size == 0) { - if ((t->commands = malloc(sizeof(char*))) == NULL) - err(1, "%s: malloc(3) failed", __FUNCTION__); - + t->commands = xmalloc(sizeof(char*)); idx = 0; t->size = 1; } else { - int new_size = (t->size + 1) * sizeof(char*); - if ((new_cmds = realloc(t->commands, new_size)) == NULL) - err(1, "%s: realloc(3) failed", __FUNCTION__); - - idx = t->size; - t->commands = new_cmds; - t->size++; + t->commands = xrealloc(t->commands, t->size + 1, sizeof(char *)); + idx = t->size++; } /* add command */ - if ((t->commands[idx] = strdup(cmd)) == NULL) - err(1, "%s: strdup(3) failed", __FUNCTION__); + t->commands[idx] = xstrdup(cmd); } toggle_list* @@ -109,9 +112,7 @@ toggle_list_create(int registr, int argc, char *argv[]) char *cmd = NULL; int i, j; - if ((t = malloc(sizeof(toggle_list))) == NULL) - err(1, "%s: malloc(3) failed", __FUNCTION__); - + t = xmalloc(sizeof(toggle_list)); t->commands = NULL; t->registr = registr; t->size = 0; @@ -243,7 +244,7 @@ cmd_quit(int argc, char *argv[]) bool forced; if (argc != 1) { - paint_error("usage: q[!]"); + fatalx("usage: q[!]"); return 1; } @@ -255,7 +256,7 @@ cmd_quit(int argc, char *argv[]) int i; for (i = 0; i < mdb.nplaylists; i++) { if (mdb.playlists[i]->needs_saving) { - paint_error("there are playlists with unsaved changes. use \"q!\" to force."); + fatalx("there are playlists with unsaved changes. use \"q!\" to force."); return 2; } } @@ -272,7 +273,7 @@ cmd_write(int argc, char *argv[]) bool forced; if (argc > 2) { - paint_error("usage: w[!] [name]"); + fatalx("usage: w[!] [name]"); return 1; } @@ -283,13 +284,13 @@ cmd_write(int argc, char *argv[]) /* can't save library or filter results */ if (viewing_playlist == mdb.library || viewing_playlist == mdb.filter_results) { - paint_error("use \"w name\" when saving pseudo-playlists like library/filter"); + fatalx("use \"w name\" when saving pseudo-playlists like library/filter"); return 2; } /* can't save a new playlist that has no name */ if (viewing_playlist->filename == NULL) { - paint_error("use \"w name\" for new playlists"); + fatalx("use \"w name\" for new playlists"); return 3; } @@ -297,7 +298,7 @@ cmd_write(int argc, char *argv[]) playlist_save(viewing_playlist); viewing_playlist->needs_saving = false; paint_library(); - paint_message("\"%s\" %d songs written", + infox("\"%s\" %d songs written", viewing_playlist->filename, viewing_playlist->nfiles); } else { /* "save as" */ @@ -305,8 +306,7 @@ cmd_write(int argc, char *argv[]) bool will_clobber; /* build filename for playlist */ - if (asprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, argv[1]) == -1) - err(1, "cmd_write: asprintf failed"); + xasprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, argv[1]); /* check to see if playlist with that name already exists */ will_clobber = false; @@ -320,7 +320,7 @@ cmd_write(int argc, char *argv[]) } if (will_clobber && !forced) { - paint_error("playlist with that name exists (use \"w!\" to overwrite)"); + fatalx("playlist with that name exists (use \"w!\" to overwrite)"); free(filename); return 4; } @@ -348,7 +348,7 @@ cmd_write(int argc, char *argv[]) viewing_playlist->needs_saving = false; paint_library(); - paint_message("\"%s\" %d songs written", + infox("\"%s\" %d songs written", filename, viewing_playlist->nfiles); free(filename); @@ -361,7 +361,7 @@ int cmd_mode(int argc, char *argv[]) { if (argc != 2) { - paint_error("usage: mode [ linear | loop | random ]"); + fatalx("usage: mode [ linear | loop | random ]"); return 1; } @@ -372,11 +372,11 @@ cmd_mode(int argc, char *argv[]) else if (strcasecmp(argv[1], "random") == 0) player_info.mode = MODE_RANDOM; else { - paint_error("invalid mode \"%s\". must be one of: linear, loop, or random", argv[1]); + fatalx("invalid mode \"%s\". must be one of: linear, loop, or random", argv[1]); return 2; } - paint_message("mode changed to: %s", argv[1]); + infox("mode changed to: %s", argv[1]); return 0; } @@ -388,7 +388,7 @@ cmd_new(int argc, char *argv[]) char *filename; if (argc > 2) { - paint_error("usage: new [name]"); + fatalx("usage: new [name]"); return 1; } @@ -402,22 +402,20 @@ cmd_new(int argc, char *argv[]) int i; for (i = 0; i < mdb.nplaylists; i++) { if (strcmp(mdb.playlists[i]->name, argv[1]) == 0) { - paint_error("playlist \"%s\" already exists.", argv[1]); + fatalx("playlist \"%s\" already exists.", argv[1]); return 2; } } name = argv[1]; - if (asprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, name) == -1) - err(1, "cmd_new: asprintf failed"); + xasprintf(&filename, "%s/%s.playlist", mdb.playlist_dir, name); } /* create & setup playlist */ p = playlist_new(); p->needs_saving = true; p->filename = filename; - if ((p->name = strdup(name)) == NULL) - err(1, "cmd_new: strdup(3) failed"); + p->name = xstrdup(name); /* add playlist to media library and update ui */ medialib_playlist_add(p); @@ -427,7 +425,7 @@ cmd_new(int argc, char *argv[]) /* redraw */ paint_library(); - paint_message("playlist \"%s\" added", name); + infox("playlist \"%s\" added", name); return 0; } @@ -441,7 +439,7 @@ cmd_filter(int argc, char *argv[]) int i; if (argc == 1) { - paint_error("usage: filter[!] token [token2 ...]"); + fatalx("usage: filter[!] token [token2 ...]"); return 1; } @@ -481,13 +479,13 @@ cmd_sort(int argc, char *argv[]) const char *errmsg; if (argc != 2) { - paint_error("usage: sort "); + fatalx("usage: sort "); return 1; } /* setup global sort description */ if (mi_sort_set(argv[1], &errmsg) != 0) { - paint_error("%s: bad sort description: %s", argv[0], errmsg); + fatalx("%s: bad sort description: %s", argv[0], errmsg); return 2; } @@ -516,13 +514,13 @@ cmd_display(int argc, char *argv[]) const char *errmsg; if (argc != 2) { - paint_error("usage: display [ reset | show | ]"); + fatalx("usage: display [ reset | show | ]"); return 1; } /* show existng display? */ if (strcasecmp(argv[1], "show") == 0) { - paint_message(":display %s", mi_display_tostr()); + infox(":display %s", mi_display_tostr()); return 0; } @@ -538,7 +536,7 @@ cmd_display(int argc, char *argv[]) /* if reached here, setup global display description */ if (mi_display_set(argv[1], &errmsg) != 0) { - paint_error("%s: bad display description: %s", argv[0], errmsg); + fatalx("%s: bad display description: %s", argv[0], errmsg); return 1; } @@ -556,21 +554,21 @@ cmd_color(int argc, char *argv[]) int i_item, i_fg, i_bg, j; if (argc != 2) { - paint_error("usage: %s ITEM=FG,BG", argv[0]); + fatalx("usage: %s ITEM=FG,BG", argv[0]); return 1; } /* extract item and foreground/background colors */ item = argv[1]; if ((fg = strchr(item, '=')) == NULL) { - paint_error("usage: %s ITEM=FG,BG", argv[0]); + fatalx("usage: %s ITEM=FG,BG", argv[0]); return 2; } *fg = '\0'; fg++; if ((bg = strchr(fg, ',')) == NULL) { - paint_error("usage: %s ITEM=FG,BG", argv[0]); + fatalx("usage: %s ITEM=FG,BG", argv[0]); return 3; } *bg = '\0'; @@ -578,17 +576,17 @@ cmd_color(int argc, char *argv[]) /* convert all */ if ((i_item = paint_str2item(item)) < 0) { - paint_error("invalid item '%s'", item); + fatalx("invalid item '%s'", item); return 4; } if ((i_fg = paint_str2color(fg)) == -2) { - paint_error("invalid foreground color '%s'", fg); + fatalx("invalid foreground color '%s'", fg); return 5; } if ((i_bg = paint_str2color(bg)) == -2) { - paint_error("invalid background color '%s'", bg); + fatalx("invalid background color '%s'", bg); return 6; } @@ -620,7 +618,7 @@ cmd_set(int argc, char *argv[]) bool player_is_setup; if (argc != 2) { - paint_error("usage: %s =", argv[0]); + fatalx("usage: %s =", argv[0]); return 1; } @@ -631,7 +629,7 @@ cmd_set(int argc, char *argv[]) /* extract property and value */ property = argv[1]; if ((value = strchr(property, '=')) == NULL) { - paint_error("usage: %s =", argv[0]); + fatalx("usage: %s =", argv[0]); return 2; } *value = '\0'; @@ -647,7 +645,7 @@ cmd_set(int argc, char *argv[]) /* validate and convert width user provided */ new_width = (int)strtonum(value, 1, max_w, &err); if (err != NULL) { - paint_error("%s %s: bad width: '%s' %s", + fatalx("%s %s: bad width: '%s' %s", argv[0], property, value, err); return 3; } @@ -662,7 +660,7 @@ cmd_set(int argc, char *argv[]) } else if (strcasecmp(property, "lhide") == 0) { if (str2bool(value, &tf) < 0) { - paint_error("%s %s: value must be boolean", + fatalx("%s %s: value must be boolean", argv[0], property); return 4; } @@ -673,40 +671,40 @@ cmd_set(int argc, char *argv[]) if (player_is_setup && ui_is_init()) { ui_clear(); paint_all(); - paint_message("library window hidden"); + infox("library window hidden"); } } else { if (ui.library->cwin == NULL) ui_unhide_library(); if (player_is_setup && ui_is_init()) paint_all(); - paint_message("library window un-hidden"); + infox("library window un-hidden"); } } else if (strcasecmp(property, "match-fname") == 0) { if (str2bool(value, &tf) < 0) { - paint_error("%s %s: value must be boolean", + fatalx("%s %s: value must be boolean", argv[0], property); return 5; } mi_query_match_filename = tf; if (mi_query_match_filename) - paint_message("filenames will be matched against"); + infox("filenames will be matched against"); else - paint_message("filenames will NOT be matched against"); + infox("filenames will NOT be matched against"); } else if (strcasecmp(property, "save-sorts") == 0) { if (str2bool(value, &tf) < 0) { - paint_error("%s %s: value must be boolean", + fatalx("%s %s: value must be boolean", argv[0], property); return 6; } sorts_need_saving = tf; if (sorts_need_saving) - paint_message("changing sort will be prompted for saving"); + infox("changing sort will be prompted for saving"); else - paint_message("changing sort will NOT be prompted for saving"); + infox("changing sort will NOT be prompted for saving"); } else { - paint_error("%s: unknown property '%s'", argv[0], property); + fatalx("%s: unknown property '%s'", argv[0], property); return 7; } @@ -717,17 +715,14 @@ int cmd_reload(int argc, char *argv[]) { if (argc != 2) { - paint_error("usage: %s [ db | conf ]", argv[0]); + fatalx("usage: %s [ db | conf ]", argv[0]); return 1; } /* reload database or config file */ if (strcasecmp(argv[1], "db") == 0) { - - char *db_file = strdup(mdb.db_file); - char *playlist_dir = strdup(mdb.playlist_dir); - if (db_file == NULL || playlist_dir == NULL) - err(1, "cmd_reload: strdup(3) failed"); + char *db_file = xstrdup(mdb.db_file); + char *playlist_dir = xstrdup(mdb.playlist_dir); /* stop playback TODO investigate a nice way around this */ player_stop(); @@ -752,9 +747,9 @@ cmd_reload(int argc, char *argv[]) } else if (strcasecmp(argv[1], "conf") == 0) { load_config(); - paint_message("configuration reloaded"); + infox("configuration reloaded"); } else { - paint_error("usage: %s [ db | conf ]", argv[0]); + fatalx("usage: %s [ db | conf ]", argv[0]); return 2; } @@ -768,23 +763,23 @@ cmd_bind(int argc, char *argv[]) KeyCode code; if (argc < 3 || argc > 4) { - paint_error("usage: %s ", argv[0]); + fatalx("usage: %s ", argv[0]); return 1; } if (!kb_str2action(argv[1], &action)) { - paint_error("Unknown action '%s'", argv[1]); + fatalx("Unknown action '%s'", argv[1]); return 1; } if (argc == 3) { if ((code = kb_str2keycode(argv[2])) < 0) { - paint_error("Invalid keycode '%s'", argv[2]); + fatalx("Invalid keycode '%s'", argv[2]); return 1; } } else { if ((code = kb_str2keycode2(argv[2], argv[3])) < 0) { - paint_error("Invalid keycode '%s'", argv[2]); + fatalx("Invalid keycode '%s'", argv[2]); return 1; } } @@ -811,7 +806,7 @@ cmd_unbind(int argc, char *argv[]) kb_unbind_action(action); return 0; } else { - paint_error("Unknown action '%s'", argv[2]); + fatalx("Unknown action '%s'", argv[2]); return 1; } } @@ -819,7 +814,7 @@ cmd_unbind(int argc, char *argv[]) /* unbind key case, no control ("unbind key X") */ if (argc == 3 && strcasecmp(argv[1], "key") == 0) { if ((key = kb_str2keycode(argv[2])) < 0) { - paint_error("Invalid keycode '%s'", argv[2]); + fatalx("Invalid keycode '%s'", argv[2]); return 1; } @@ -830,7 +825,7 @@ cmd_unbind(int argc, char *argv[]) /* unbind key case, with control ("unbind key control X") */ if (argc == 4 && strcasecmp(argv[1], "key") == 0) { if ((key = kb_str2keycode2(argv[2], argv[3])) < 0) { - paint_error("Invalid keycode '%s %s'", argv[2], argv[3]); + fatalx("Invalid keycode '%s %s'", argv[2], argv[3]); return 1; } @@ -838,7 +833,7 @@ cmd_unbind(int argc, char *argv[]) return 0; } - paint_error("usage: unbind [* | action | key ]"); + fatalx("usage: unbind [* | action | key ]"); return 1; } @@ -851,12 +846,12 @@ cmd_toggle(int argc, char *argv[]) int registr; if (argc < 3) { - paint_error("usage: %s / ...", argv[0]); + fatalx("usage: %s / ...", argv[0]); return 1; } if (strlen(argv[1]) != 1) { - paint_error("error: register name must be a single letter (in [a-zA-Z])"); + fatalx("error: register name must be a single letter (in [a-zA-Z])"); return 1; } @@ -864,7 +859,7 @@ cmd_toggle(int argc, char *argv[]) if (!( ('a' <= registr && registr <= 'z') || ('A' <= registr && registr <= 'Z'))) { - paint_error("error: invalid register name. Must be one of [a-zA-Z]"); + fatalx("error: invalid register name. Must be one of [a-zA-Z]"); return 1; } @@ -882,7 +877,7 @@ cmd_playlist(int argc, char *argv[]) int idx = -1; if (argc != 2) { - paint_error("usage: playlist "); + fatalx("usage: playlist "); return 1; } @@ -902,17 +897,17 @@ cmd_playlist(int argc, char *argv[]) setup_viewing_playlist(mdb.playlists[idx]); ui.active = ui.playlist; paint_all(); - paint_message("jumped to playlist: %s", mdb.playlists[idx]->name); + infox("jumped to playlist: %s", mdb.playlists[idx]->name); return 0; } if(idx == -1) { - paint_error("no match for: %s", argv[1]); + fatalx("no match for: %s", argv[1]); return 0; } if(idx == -2) - paint_error("no unique match for: %s", argv[1]); + fatalx("no unique match for: %s", argv[1]); return 0; } @@ -929,7 +924,7 @@ cmd_execute(char *cmd) int i; if (str2argv(cmd, &argc, &argv, &errmsg) != 0) { - paint_error("parse error: %s in '%s'", errmsg, cmd); + fatalx("parse error: %s in '%s'", errmsg, cmd); return; } @@ -946,9 +941,9 @@ cmd_execute(char *cmd) if (found && num_matches == 1) (CommandPath[found_idx].func)(argc, argv); else if (num_matches > 1) - paint_error("Ambiguous abbreviation '%s'", argv[0]); + fatalx("Ambiguous abbreviation '%s'", argv[0]); else - paint_error("Unknown commands '%s'", argv[0]); + fatalx("Unknown commands '%s'", argv[0]); argv_free(&argc, &argv); } @@ -980,10 +975,7 @@ user_getstr(const char *prompt, char **response) wrefresh(ui.command); /* allocate input space and clear */ - if ((input = calloc(MAX_INPUT_SIZE, sizeof(char))) == NULL) - err(1, "user_getstr: calloc(3) failed for input string"); - - bzero(input, MAX_INPUT_SIZE); + input = xcalloc(MAX_INPUT_SIZE, sizeof(char)); /* start getting input */ ret = 0; @@ -1039,7 +1031,7 @@ user_getstr(const char *prompt, char **response) /* see todo above - realloc input buffer here if position reaches max */ if (pos >= MAX_INPUT_SIZE) - errx(1, "user_getstr: shamefull limit reached"); + fatalx("user_getstr: shamefull limit reached"); } /* For lack of input, bail out */ @@ -1054,9 +1046,7 @@ user_getstr(const char *prompt, char **response) input[pos] = '\0'; /* trim the fat */ - if ((*response = calloc(strlen(input) + 1, sizeof(char))) == NULL) - err(1, "user_getstr: calloc(3) failed for result"); - + *response = xcalloc(strlen(input) + 1, sizeof(char)); snprintf(*response, strlen(input) + 1, "%s", input); end: diff --git a/commands.h b/commands.h index 521efe7..53b03b8 100644 --- a/commands.h +++ b/commands.h @@ -17,14 +17,9 @@ #ifndef COMMANDS_H #define COMMANDS_H -#include "compat.h" - -#include "enums.h" -#include "paint.h" -#include "str2argv.h" -#include "vitunes.h" -#include "debug.h" -#include "keybindings.h" +#include + +#include "playlist.h" /**************************************************************************** * Toggle-list handling stuff diff --git a/compat.c b/compat.c index 0e2d9f0..8cd144d 100644 --- a/compat.c +++ b/compat.c @@ -17,7 +17,7 @@ #include "compat.h" #ifdef COMPAT_NEED_FPARSELN -# include "compat/fparseln.c" +# include "fparseln.c" #endif #ifdef COMPAT_NEED_OPTRESET @@ -25,10 +25,10 @@ #endif #ifdef COMPAT_NEED_STRLCAT -# include "compat/strlcat.c" +# include "strlcat.c" #endif #ifdef COMPAT_NEED_STRTONUM -# include "compat/strtonum.c" +# include "strtonum.c" #endif diff --git a/compat.h b/compat.h index ac349c6..a8e89ab 100644 --- a/compat.h +++ b/compat.h @@ -38,6 +38,7 @@ /* OpenBSD has fparseln(3), but it must be included thusly */ #if defined(__OpenBSD__) +# include # include # include # include @@ -49,24 +50,28 @@ # include # include # include +# include "tree.h" #endif /* Mac OS X has fparseln(3), but it must be included thusly */ #if defined(__MACH__) # include # include +# include "tree.h" #endif /* Mac OS X needs strtonum(3) */ #if defined(__APPLE__) && defined(__MACH__) # include # include +# include "tree.h" # define COMPAT_NEED_STRTONUM # define COMPAT_NEED_STRTOLL #endif /* Linux needs the following.. */ #if defined(__linux) +# include "tree.h" # define _GNU_SOURCE # define COMPAT_NEED_FPARSELN # define COMPAT_NEED_OPTRESET diff --git a/compat/tree.h b/compat/tree.h new file mode 100644 index 0000000..80d0f53 --- /dev/null +++ b/compat/tree.h @@ -0,0 +1,748 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/doc/vitunes.1 b/doc/vitunes.1 index 7495bef..17e0d49 100644 --- a/doc/vitunes.1 +++ b/doc/vitunes.1 @@ -21,6 +21,7 @@ .Sh SYNOPSIS .Nm vitunes .Bk -words +.Op Fl v .Op Fl c Ar command .Op Fl d Ar database-file .Op Fl e Ar command Op argument ... @@ -138,6 +139,10 @@ will be created here. .Pp The default location is .Pa ~/.vitunes/playlists/ . +.It Fl v +Turn verbose logging on. +Log messages will be saved in the vitunes-debug.log file in the current +directory. .El .Sh GETTING STARTED .Nm diff --git a/ecmd.c b/ecmd.c index eb0fbdb..e961d1d 100644 --- a/ecmd.c +++ b/ecmd.c @@ -15,38 +15,32 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include + #include "ecmd.h" +#include "error.h" +#include "vitunes.h" -static int -ecmd_parse(const struct ecmd *ecmd, int argc, char ***argv) +static struct ecmd_args * +ecmd_parse(const struct ecmd *ecmd, int argc, char **argv) { - /* reset getopt(3) variables */ - optind = 0; - optreset = 1; + struct ecmd_args *args; + + /* generically parse command-line options */ + if ((args = ecmd_args_parse(ecmd->optstring, argc, argv)) == NULL) + return NULL; + if (ecmd->check != NULL && ecmd->check(args) == -1) + return NULL; - /* parse command line and if valid, skip parsed arguments */ - if (ecmd->parse != NULL) { - /* parse error */ - if (ecmd->parse(argc, *argv) == -1) - return -1; - if (ecmd->check != NULL && ecmd->check() == -1) - return -1; - argc -= optind; - *argv += optind; - } else { - /* no parse function; skip only its name */ - argc--; - (*argv)++; - } - /* invalid number of arguments */ - if (argc < ecmd->args_lower) - return -1; - if (ecmd->args_upper >= 0 && argc > ecmd->args_upper) - return -1; + if (args->argc < ecmd->args_lower) + return NULL; + if (ecmd->args_upper >= 0 && args->argc > ecmd->args_upper) + return NULL; - /* return updated (no name) number of arguments */ - return argc; + return args; } int @@ -64,8 +58,9 @@ ecmd_exec(const char *ecmd, int argc, char **argv) &ecmd_tag, &ecmd_update, }; - static const int ecmdtab_size = sizeof ecmdtab / sizeof ecmdtab[0]; - int i; + static const int ecmdtab_size = sizeof ecmdtab / sizeof ecmdtab[0]; + int i; + struct ecmd_args *args; /* search for e-command (first by name and then by alias) */ for (i = 0; i < ecmdtab_size; i++) { @@ -76,18 +71,18 @@ ecmd_exec(const char *ecmd, int argc, char **argv) } /* not found; bail out */ if (i == ecmdtab_size) { - warnx("Unknown e-command '%s'. See 'vitunes -e help' for list.", ecmd); + infox("Unknown e-command '%s'. See 'vitunes -e help' for list.", ecmd); return -1; } /* parse e-command arguments */ - if ((argc = ecmd_parse(ecmdtab[i], argc, &argv)) == -1) { + if ((args = ecmd_parse(ecmdtab[i], argc, argv)) == NULL) { fprintf(stderr, "usage: %s -e %s %s\n", progname, ecmdtab[i]->name, - ecmdtab[i]->usage != NULL ? ecmdtab[i]->usage : ""); + ecmdtab[i]->usage); return 1; } /* finally execute it */ - ecmdtab[i]->exec(argc, argv); + ecmdtab[i]->exec(args); return 0; } diff --git a/ecmd.h b/ecmd.h index 95c4a31..fa0ac10 100644 --- a/ecmd.h +++ b/ecmd.h @@ -18,22 +18,17 @@ #ifndef ECMD_H #define ECMD_H -#include -#include -#include -#include - -#include "vitunes.h" +#include "ecmd_args.h" struct ecmd { const char *name; - const char *alias; /* may be NULL */ - const char *usage; /* may be NULL */ - int args_lower; /* minimum number of arguments */ - int args_upper; /* negative number means no limit */ - int (*parse)(int argc, char **argv); /* may be NULL */ - int (*check)(void); /* may be NULL */ - void (*exec)(int argc, char **argv); + const char *alias; /* may be NULL */ + const char *usage; /* may be an empty string */ + const char *optstring; /* may be NULL */ + int args_lower; /* minimum number of arguments */ + int args_upper; /* negative number means no limit */ + int (*check)(struct ecmd_args *args); /* may be NULL */ + void (*exec)(struct ecmd_args *args); }; extern const struct ecmd ecmd_add; diff --git a/ecmd_add.c b/ecmd_add.c index 882d3d2..f749263 100644 --- a/ecmd_add.c +++ b/ecmd_add.c @@ -22,13 +22,13 @@ #include "vitunes.h" static void -ecmd_add_exec(UNUSED int argc, char **argv) +ecmd_add_exec(struct ecmd_args *args) { printf("Loading existing database...\n"); medialib_load(db_file, playlist_dir); printf("Scanning directories for files to add to database...\n"); - medialib_db_scan_dirs(argv); + medialib_db_scan_dirs(args->argv); medialib_destroy(); } @@ -36,8 +36,8 @@ ecmd_add_exec(UNUSED int argc, char **argv) const struct ecmd ecmd_add = { "add", NULL, "path [...]", - 1, -1, NULL, + 1, -1, NULL, ecmd_add_exec }; diff --git a/ecmd_addurl.c b/ecmd_addurl.c index bb58717..14d9fdc 100644 --- a/ecmd_addurl.c +++ b/ecmd_addurl.c @@ -15,18 +15,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include "ecmd.h" +#include "error.h" #include "medialib.h" #include "meta_info.h" #include "playlist.h" #include "vitunes.h" +#include "xmalloc.h" static void -ecmd_addurl_exec(UNUSED int argc, char **argv) +ecmd_addurl_exec(struct ecmd_args *args) { meta_info *m; bool found; @@ -37,15 +38,14 @@ ecmd_addurl_exec(UNUSED int argc, char **argv) /* start new record, set filename */ m = mi_new(); m->is_url = true; - if ((m->filename = strdup(argv[0])) == NULL) - err(1, "%s: strdup failed (filename)", __FUNCTION__); + m->filename = xstrdup(args->argv[0]); /* get fields from user */ for (field = 0; field < MI_NUM_CINFO; field++) { printf("%10.10s: ", MI_CINFO_NAMES[field]); if (fgets(input, sizeof(input), stdin) == NULL) { - warnx("Operation canceled. Database unchanged."); + infox("Operation canceled. Database unchanged."); mi_free(m); return; } @@ -53,8 +53,7 @@ ecmd_addurl_exec(UNUSED int argc, char **argv) if (input[strlen(input) - 1] == '\n') input[strlen(input) - 1] = '\0'; - if ((m->cinfo[field] = strdup(input)) == NULL) - err(1, "%s: strdup failed (field)", __FUNCTION__); + m->cinfo[field] = xstrdup(input); } /* load existing database and see if file/URL already exists */ @@ -71,12 +70,13 @@ ecmd_addurl_exec(UNUSED int argc, char **argv) } if (found) { - printf("Warning: file/URL '%s' already in the database.\n", argv[0]); + printf("Warning: file/URL '%s' already in the database.\n", + args->argv[0]); printf("Do you want to replace the existing record? [y/n] "); if (fgets(input, sizeof(input), stdin) == NULL || (strcasecmp(input, "yes\n") != 0 && strcasecmp(input, "y\n") != 0)) { - warnx("Operation Canceled. Database unchanged."); + infox("Operation Canceled. Database unchanged."); mi_free(m); medialib_destroy(); return; @@ -96,8 +96,8 @@ ecmd_addurl_exec(UNUSED int argc, char **argv) const struct ecmd ecmd_addurl = { "addurl", NULL, "URL|path", - 1, 1, NULL, + 1, 1, NULL, ecmd_addurl_exec }; diff --git a/ecmd_args.c b/ecmd_args.c new file mode 100644 index 0000000..462cf2e --- /dev/null +++ b/ecmd_args.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "ecmd_args.h" +#include "error.h" +#include "str2argv.h" +#include "xmalloc.h" + +/* E-command arguments compare function. */ +static int +ecmd_args_cmp(struct ecmd_args_entry *entry1, struct ecmd_args_entry *entry2) +{ + return entry1->flag - entry2->flag; +} + +/* Tree prototypes and functions. */ +RB_PROTOTYPE_STATIC(ecmd_args_tree, ecmd_args_entry, entry, ecmd_args_cmp); +RB_GENERATE_STATIC(ecmd_args_tree, ecmd_args_entry, entry, ecmd_args_cmp); + +/* Find flag in the arguments tree. */ +static struct ecmd_args_entry * +ecmd_args_find(struct ecmd_args *args, int flag) +{ + struct ecmd_args_entry entry; + + entry.flag = flag; + return RB_FIND(ecmd_args_tree, &args->tree, &entry); +} + +/* Add a flag and value to the arguments tree. */ +void +ecmd_args_add(struct ecmd_args *args, int flag, const char *value) +{ + struct ecmd_args_entry *entry; + + /* Remove existing value. */ + if ((entry = ecmd_args_find(args, flag)) != NULL) { + free(entry->value); + if (value != NULL) + entry->value = xstrdup(value); + else + entry->value = NULL; + return; + } + + /* New value. */ + entry = xcalloc(1, sizeof *entry); + entry->flag = flag; + if (value != NULL) + entry->value = xstrdup(value); + RB_INSERT(ecmd_args_tree, &args->tree, entry); +} + +/* Checks if the given flag was set an either return true or false. */ +bool +ecmd_args_bool(struct ecmd_args *args, int flag) +{ + if (ecmd_args_find(args, flag) == NULL) + return false; + return true; +} + +/* + * Checks if the arguments tree is empty. Useful for those e-commands that need + * at least one flag. + */ +int +ecmd_args_empty(struct ecmd_args *args) +{ + return RB_EMPTY(&args->tree); +} + +/* Get e-command argument value. */ +const char * +ecmd_args_get(struct ecmd_args *args, int flag) +{ + struct ecmd_args_entry *entry; + + if ((entry = ecmd_args_find(args, flag)) == NULL) + return NULL; + return entry->value; +} + +/* + * Generically parse function that adds the flags and, if applicable, its + * values to the arguments tree. It initialises the arguments structure, which + * isn't freed on purpose, because the program always quits on error. + */ +struct ecmd_args * +ecmd_args_parse(const char *optstring, int argc, char **argv) +{ + int ch; + struct ecmd_args *args; + + /* Initialise e-command arguments structure. */ + args = xcalloc(1, sizeof *args); + + /* Reset getopt(3) variables (skip e-command name). */ + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, optstring)) != -1) { + if (ch == '?') + return NULL; + ecmd_args_add(args, ch, optarg); + } + argc -= optind; + argv += optind; + + args->argc = argc; + args->argv = argv_copy(argc, argv); + + return args; +} + +/* + * Convert the argument value to an integer. Since, no e-command (currently) + * expects a negative value, returns -1 if the flag wasn't set. + */ +int +ecmd_args_strtonum(struct ecmd_args *args, int flag, int minval, int maxval) +{ + const char *errstr, *value; + int n; + + if ((value = ecmd_args_get(args, flag)) == NULL) + return -1; + + n = strtonum(value, minval, maxval, &errstr); + if (errstr != NULL) + fatalx("number %s", errstr); + + return n; +} diff --git a/ecmd_args.h b/ecmd_args.h new file mode 100644 index 0000000..9d0a90d --- /dev/null +++ b/ecmd_args.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ECMD_ARGS +#define ECMD_ARGS + +#include + +#include "compat.h" + +/* E-command arguments structure. */ +struct ecmd_args_entry { + int flag; /* Argument flag. */ + char *value; /* Argument value. */ + RB_ENTRY(ecmd_args_entry) entry; +}; +RB_HEAD(ecmd_args_tree, ecmd_args_entry); + +struct ecmd_args { + struct ecmd_args_tree tree; /* Tree of arguments. */ + int argc; + char **argv; +}; + +struct ecmd_args; +void ecmd_args_add(struct ecmd_args *, int, const char *); +bool ecmd_args_bool(struct ecmd_args *, int); +int ecmd_args_empty(struct ecmd_args *); +const char *ecmd_args_get(struct ecmd_args *, int); +struct ecmd_args *ecmd_args_parse(const char *, int, char **); +int ecmd_args_strtonum(struct ecmd_args *, int, int, int); + +#endif diff --git a/ecmd_check.c b/ecmd_check.c index dabfb49..b011eae 100644 --- a/ecmd_check.c +++ b/ecmd_check.c @@ -15,31 +15,28 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include +#include #include -#include #include "ecmd.h" +#include "error.h" #include "medialib.h" #include "vitunes.h" -static bool show_raw; -static bool show_sanitized; -static bool show_database; - static void -ecmd_check_show_db(const char *path) +ecmd_check_show_db(struct ecmd_args *args, const char *path) { bool found; char realfile[PATH_MAX]; int i; meta_info *mi; - if (show_database == false) + if (ecmd_args_bool(args, 'd') == false) return; if (realpath(path, realfile) == NULL) { - warn("realpath failed for %s: skipping", path); + info("realpath failed for %s: skipping", path); return; } @@ -55,7 +52,7 @@ ecmd_check_show_db(const char *path) } if (!found) - warnx("File '%s' does NOT exist in the database", path); + infox("File '%s' does NOT exist in the database", path); else { printf("\tThe meta-information in the DATABASE is:\n"); for (i = 0; i < MI_NUM_CINFO; i++) @@ -66,15 +63,15 @@ ecmd_check_show_db(const char *path) } static void -ecmd_check_show_raw(const char *path) +ecmd_check_show_raw(struct ecmd_args *args, const char *path) { int i; meta_info *mi; - if (show_raw == false) + if (ecmd_args_bool(args, 'r') == false) return; if ((mi = mi_extract(path)) == NULL) { - warnx("Failed to extract any meta-information from '%s'", path); + infox("Failed to extract any meta-information from '%s'", path); return; } @@ -84,15 +81,15 @@ ecmd_check_show_raw(const char *path) } static void -ecmd_check_show_sanitized(const char *path) +ecmd_check_show_sanitized(struct ecmd_args *args, const char *path) { int i; meta_info *mi; - if (show_sanitized == false) + if (ecmd_args_bool(args, 's') == false) return; if ((mi = mi_extract(path)) == NULL) { - warnx("Failed to extract any meta-information from '%s'", path); + infox("Failed to extract any meta-information from '%s'", path); return; } @@ -103,58 +100,30 @@ ecmd_check_show_sanitized(const char *path) } static int -ecmd_check_parse(int argc, char **argv) -{ - int ch; - - while ((ch = getopt(argc, argv, "drs")) != -1) { - switch (ch) { - case 'd': - show_database = true; - break; - case 'r': - show_raw = true; - break; - case 's': - show_sanitized = true; - break; - case 'h': - case '?': - default: - return -1; - } - } - - return 0; -} - -static int -ecmd_check_check(void) +ecmd_check_check(struct ecmd_args *args) { - if (!show_raw && !show_sanitized && !show_database) - return -1; - return 0; + return ecmd_args_empty(args) == 0 ? 0 : -1; } static void -ecmd_check_exec(int argc, char **argv) +ecmd_check_exec(struct ecmd_args *args) { int i; /* scan through files... */ - for (i = 0; i < argc; i++) { - printf("Checking: '%s'\n", argv[i]); - ecmd_check_show_raw(argv[i]); - ecmd_check_show_sanitized(argv[i]); - ecmd_check_show_db(argv[i]); + for (i = 0; i < args->argc; i++) { + printf("Checking: '%s'\n", args->argv[i]); + ecmd_check_show_raw(args, args->argv[i]); + ecmd_check_show_sanitized(args, args->argv[i]); + ecmd_check_show_db(args, args->argv[i]); } } const struct ecmd ecmd_check = { "check", NULL, "-d | -r | -s path [...]", + "drs", 1, -1, - ecmd_check_parse, ecmd_check_check, ecmd_check_exec }; diff --git a/ecmd_flush.c b/ecmd_flush.c index 6eeb158..4eb6914 100644 --- a/ecmd_flush.c +++ b/ecmd_flush.c @@ -15,50 +15,29 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include - #include "ecmd.h" #include "medialib.h" #include "vitunes.h" +#include "xmalloc.h" -static char *time_format = "%Y %m %d %H:%M:%S"; - -static int -ecmd_flush_parse(int argc, char **argv) +static void +ecmd_flush_exec(struct ecmd_args *args) { - int ch; + const char *timefmt; - while ((ch = getopt(argc, argv, "t:")) != -1) { - switch (ch) { - case 't': - if ((time_format = strdup(optarg)) == NULL) - err(1, "%s: strdup of time_format failed", argv[0]); - break; - case '?': - case 'h': - default: - return -1; - } - } + if ((timefmt = ecmd_args_get(args, 't')) == NULL) + timefmt = "%Y %m %d %H:%M:%S"; - return 0; -} - -static void -ecmd_flush_exec(UNUSED int argc, UNUSED char **argv) -{ medialib_load(db_file, playlist_dir); - medialib_db_flush(stdout, time_format); + medialib_db_flush(stdout, timefmt); medialib_destroy(); } const struct ecmd ecmd_flush = { "flush", NULL, "[-t format]", + "t:", 0, 0, - ecmd_flush_parse, NULL, ecmd_flush_exec }; diff --git a/ecmd_help.c b/ecmd_help.c index ad85528..65b1f16 100644 --- a/ecmd_help.c +++ b/ecmd_help.c @@ -15,21 +15,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include #include "ecmd.h" +#include "error.h" #include "vitunes.h" +#include "xmalloc.h" static void -ecmd_help_exec(UNUSED int argc, char **argv) +ecmd_help_exec(struct ecmd_args *args) { char *man_args[3]; /* no help requested for a specific command, give a list of all e-cmds */ - if (argc == 0) { + if (args->argc == 0) { printf("\ The following is a list of e-commands supported by vitunes.\n\ Each command is executed by doing:\n\ @@ -54,25 +55,24 @@ The list of available commands are:\n\n\ } /* if reach here, help fora specific command was requested */ - if (strcmp(argv[0], "help") == 0) { + if (strcmp(args->argv[0], "help") == 0) { printf("You're a damn fool if you need help with help.\n"); return; } man_args[0] = "man"; - if (asprintf(&man_args[1], "vitunes-%s", argv[0]) == -1) - err(1, "ecmd_help: asprintf failed"); + xasprintf(&man_args[1], "vitunes-%s", args->argv[0]); man_args[2] = NULL; execvp("man", man_args); - err(1, "ecmd_help: execvp failed"); + fatal("ecmd_help: execvp failed"); } const struct ecmd ecmd_help = { "help", NULL, "[command]", - 0, 1, NULL, + 0, 1, NULL, ecmd_help_exec }; diff --git a/ecmd_init.c b/ecmd_init.c index b0857dc..96592d5 100644 --- a/ecmd_init.c +++ b/ecmd_init.c @@ -22,7 +22,7 @@ #include "vitunes.h" static void -ecmd_init_exec(UNUSED int argc, UNUSED char **argv) +ecmd_init_exec(UNUSED struct ecmd_args *args) { printf("Creating all necessary files and directories for vitunes...\n"); medialib_setup_files(vitunes_dir, db_file, playlist_dir); @@ -32,9 +32,9 @@ ecmd_init_exec(UNUSED int argc, UNUSED char **argv) const struct ecmd ecmd_init = { "init", NULL, + "", NULL, 0, 0, NULL, - NULL, ecmd_init_exec }; diff --git a/ecmd_rmfile.c b/ecmd_rmfile.c index 39af3cf..56a9f2f 100644 --- a/ecmd_rmfile.c +++ b/ecmd_rmfile.c @@ -15,69 +15,48 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include +#include #include -#include #include "ecmd.h" +#include "error.h" #include "medialib.h" #include "playlist.h" #include "vitunes.h" -static bool forced; - -static int -ecmd_rmfile_parse(int argc, char **argv) -{ - int ch; - - while ((ch = getopt(argc, argv, "f")) != -1) { - switch (ch) { - case 'f': - forced = true; - break; - case 'h': - case '?': - default: - return -1; - } - } - - return 0; -} - static void -ecmd_rmfile_exec(UNUSED int argc, char **argv) +ecmd_rmfile_exec(struct ecmd_args *args) { char input[255]; - bool found; - int found_idx; - int i; + bool forced, found; + int found_idx, i; /* load database and search for record */ medialib_load(db_file, playlist_dir); found = false; found_idx = -1; for (i = 0; i < mdb.library->nfiles && !found; i++) { - if (strcmp(argv[0], mdb.library->files[i]->filename) == 0) { + if (strcmp(args->argv[0], mdb.library->files[i]->filename) == 0) { found = true; found_idx = i; } } /* if not found then error */ + forced = ecmd_args_bool(args, 'f'); if (!found) { i = (forced ? 0 : 1); - errx(i, "%s: %s: No such file or URL", argv[0], argv[0]); + infox("%s: No such file or URL", args->argv[0]); + exit(i); } /* if not forced, prompt user if they are sure */ if (!forced) { - printf("Are you sure you want to delete '%s'? [y/n] ", argv[0]); + printf("Are you sure you want to delete '%s'? [y/n] ", args->argv[0]); if (fgets(input, sizeof(input), stdin) == NULL || (strcasecmp(input, "yes\n") != 0 && strcasecmp(input, "y\n") != 0)) - errx(1, "Operation canceled. Database unchanged."); + fatalx("Operation canceled. Database unchanged."); } playlist_files_remove(mdb.library, found_idx, 1, false); @@ -88,8 +67,8 @@ ecmd_rmfile_exec(UNUSED int argc, char **argv) const struct ecmd ecmd_rmfile = { "rmfile", "rm", "[-f] URL|path", + "f", 1, 1, - ecmd_rmfile_parse, NULL, ecmd_rmfile_exec }; diff --git a/ecmd_tag.c b/ecmd_tag.c index d17c7cd..d49b3b7 100644 --- a/ecmd_tag.c +++ b/ecmd_tag.c @@ -15,112 +15,55 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include -#include -#include -#include +#include #include "ecmd.h" +#include "error.h" #include "meta_info.h" - -static char *artist; -static char *album; -static char *title; -static char *genre; -static char *comment; -static unsigned int track; -static unsigned int year; - -static int -ecmd_tag_parse(int argc, char **argv) -{ - const char *errstr; - int ch; - - while ((ch = getopt(argc, argv, "A:T:a:c:g:t:y:")) != -1) { - switch (ch) { - case 'A': - free(album); - if ((album = strdup(optarg)) == NULL) - err(1, "%s: strdup ALBUM failed", argv[0]); - break; - case 'T': - track = (unsigned int) strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) - errx(1, "invalid track '%s': %s", optarg, errstr); - break; - case 'a': - free(artist); - if ((artist = strdup(optarg)) == NULL) - err(1, "%s: strdup ARTIST failed", argv[0]); - break; - case 'c': - free(comment); - if ((comment = strdup(optarg)) == NULL) - err(1, "%s: strdup COMMENT failed", argv[0]); - break; - case 'g': - free(genre); - if ((genre = strdup(optarg)) == NULL) - err(1, "%s: strdup GENRE failed", argv[0]); - break; - case 't': - free(title); - if ((title = strdup(optarg)) == NULL) - err(1, "%s: strdup TITLE failed", argv[0]); - break; - case 'y': - year = (unsigned int) strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) - errx(1, "invalid year '%s': %s", optarg, errstr); - break; - case 'h': - case '?': - default: - return -1; - } - } - - return 0; -} +#include "xmalloc.h" static int -ecmd_tag_check(void) +ecmd_tag_check(struct ecmd_args *args) { - if (artist == NULL && album == NULL && title == NULL && genre == NULL - && track == 0 && year == 0 && comment == NULL) - return -1; - - return 0; + return ecmd_args_empty(args) == 0 ? 0 : -1; } static void -ecmd_tag_exec(int argc, char **argv) +ecmd_tag_exec(struct ecmd_args *args) { TagLib_File *tag_file; TagLib_Tag *tag; - int i; + const char *album, *artist, *comment, *genre, *title; + int i, track, year; /* be verbose, indicate what we're setting... */ printf("Setting the following tags to all files:\n"); - if (artist != NULL) printf("%10.10s => '%s'\n", "artist", artist); - if (album != NULL) printf("%10.10s => '%s'\n", "album", album); - if (title != NULL) printf("%10.10s => '%s'\n", "title", title); - if (genre != NULL ) printf("%10.10s => '%s'\n", "genre", genre); - if (track) printf("%10.10s => %u\n", "track", track); - if (year) printf("%10.10s => %u\n", "year", year); - if (comment != NULL) printf("%10.10s => '%s'\n", "comment", comment); + if ((artist = ecmd_args_get(args, 'a')) != NULL) + printf("%10.10s => '%s'\n", "artist", artist); + if ((album = ecmd_args_get(args, 'A')) != NULL) + printf("%10.10s => '%s'\n", "album", album); + if ((title = ecmd_args_get(args, 't')) != NULL) + printf("%10.10s => '%s'\n", "title", title); + if ((genre = ecmd_args_get(args, 'g')) != NULL) + printf("%10.10s => '%s'\n", "genre", genre); + if ((track = ecmd_args_strtonum(args, 'T', 0, INT_MAX)) != -1) + printf("%10.10s => %u\n", "track", track); + if ((year = ecmd_args_strtonum(args, 'y', 0, INT_MAX)) != -1) + printf("%10.10s => %u\n", "year", year); + if ((comment = ecmd_args_get(args, 'c')) != NULL) + printf("%10.10s => '%s'\n", "comment", comment); /* tag files ... */ taglib_set_strings_unicode(false); - for (i = 0; i < argc; i++) { - printf("tagging: '%s'\n", argv[i]); + for (i = 0; i < args->argc; i++) { + printf("tagging: '%s'\n", args->argv[i]); /* extract taglib stuff */ - if ((tag_file = taglib_file_new(argv[i])) == NULL) { - warnx("TagLib: failed to open file '%s': skipping.", argv[i]); - warnx(" => Causes: format not supported by TagLib or format doesn't support tags"); + if ((tag_file = taglib_file_new(args->argv[i])) == NULL) { + infox("TagLib: failed to open file '%s': skipping.", args->argv[i]); + infox(" => Causes: format not supported by TagLib or format doesn't support tags"); continue; } @@ -131,8 +74,8 @@ ecmd_tag_exec(int argc, char **argv) if (album != NULL) taglib_tag_set_album(tag, album); if (title != NULL) taglib_tag_set_title(tag, title); if (genre != NULL) taglib_tag_set_genre(tag, genre); - if (track) taglib_tag_set_track(tag, track); - if (year) taglib_tag_set_year(tag, year); + if (track != -1) taglib_tag_set_track(tag, track); + if (year != -1) taglib_tag_set_year(tag, year); if (comment != NULL) taglib_tag_set_comment(tag, comment); @@ -147,8 +90,8 @@ const struct ecmd ecmd_tag = { "tag", NULL, "[-A album] [-T track] [-a artist] [-c comment] [-g genre] [-t title]\n\ \t[-y year] path [...]", + "A:T:a:c:g:t:y:", 1, -1, - ecmd_tag_parse, ecmd_tag_check, ecmd_tag_exec }; diff --git a/ecmd_update.c b/ecmd_update.c index 877b1e6..20ca9f0 100644 --- a/ecmd_update.c +++ b/ecmd_update.c @@ -17,44 +17,21 @@ #include #include -#include #include "ecmd.h" #include "medialib.h" #include "vitunes.h" -static bool force_update; -static bool show_skipped; - -static int -ecmd_update_parse(int argc, char **argv) -{ - int ch; - - while ((ch = getopt(argc, argv, "fs")) != -1) { - switch (ch) { - case 'f': - force_update = true; - break; - case 's': - show_skipped = true; - break; - case 'h': - case '?': - default: - return -1; - } - } - - return 0; -} - static void -ecmd_update_exec(UNUSED int argc, UNUSED char **argv) +ecmd_update_exec(struct ecmd_args *args) { + bool force_update, show_skipped; + printf("Loading existing database...\n"); medialib_load(db_file, playlist_dir); + force_update = ecmd_args_bool(args, 'f'); + show_skipped = ecmd_args_bool(args, 's'); printf("Updating existing database...\n"); medialib_db_update(show_skipped, force_update); @@ -64,8 +41,8 @@ ecmd_update_exec(UNUSED int argc, UNUSED char **argv) const struct ecmd ecmd_update = { "update", NULL, "[-fs]", + "fs", 0, 0, - ecmd_update_parse, NULL, ecmd_update_exec }; diff --git a/error.c b/error.c new file mode 100644 index 0000000..0b7d4c9 --- /dev/null +++ b/error.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2010, 2011, 2012 Ryan Flannery + * Copyright (c) 2013 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "paint.h" +#include "uinterface.h" +#include "vitunes.h" + +static FILE *error_fp; /* error debug file */ +static int error_type; /* error type which depends on the context */ + +/* + * Starts by fetching the needed attributes that depend on the type of the + * message. It, then, paints it to the command/status window and, if wanted + * appends the errno message string. A beep is also issued if it's an error + * message. + */ +static void +error_paint(bool errnoflag, bool iserr, const char *fmt, va_list ap) +{ + int attrs, which; + + which = iserr ? colors.errors : colors.messages; + attrs = COLOR_PAIR(which); + + werase(ui.command); + wmove(ui.command, 0, 0); + wattron(ui.command, attrs); + vwprintw(ui.command, fmt, ap); + + if (errnoflag) + wprintw(ui.command, ": %s", strerror(errno)); + if (iserr) + beep(); + + wattroff(ui.command, attrs); + wrefresh(ui.command); +} + +/* + * Prints the provided error message to standard error and, if wanted, appends + * the errno message string. + */ +static void +error_file(FILE *fp, bool errnoflag, const char *fmt, va_list ap) +{ + fprintf(fp, "%s:", progname); + if (error_type == ERROR_CFG) + fprintf(fp, " %s line %zd:", conf_file, conf_linenum); + + fputs(" ", fp); + vfprintf(fp, fmt, ap); + + if (errnoflag) + fprintf(fp, ": %s", strerror(errno)); + fputs("\n", fp); +} + +/* + * Prints a fatal message (informational messages are pointless in this context) + * and terminates the process. + */ +static void +error_cfg(bool errnoflag, bool iserr, const char *fmt, va_list ap) +{ + if (!iserr) + return; + + endwin(); + error_file(stderr, errnoflag, fmt, ap); + exit(1); +} + +/* + * Prints a message to standard error and terminates the process if it's an + * error. + */ +static void +error_stderr(bool errnoflag, bool iserr, const char *fmt, va_list ap) +{ + error_file(stderr, errnoflag, fmt, ap); + if (iserr) + exit(1); +} + +/* + * Check which context we are in and call the function responsible to output the + * error message. + */ +static void +error_doit(bool errnoflag, bool iserr, const char *fmt, va_list ap) +{ + switch (error_type) { + case ERROR_CFG: + error_cfg(errnoflag, iserr, fmt, ap); + break; + case ERROR_PAINT: + error_paint(errnoflag, iserr, fmt, ap); + break; + default: + error_stderr(errnoflag, iserr, fmt, ap); + break; + } +} + +/* Outputs a message if debugging is turned on. */ +void +debug(const char *fmt, ...) +{ + va_list ap; + + if (error_fp == NULL) + return; + + va_start(ap, fmt); + error_file(error_fp, false, fmt, ap); + va_end(ap); +} + +void +error_init(int type) +{ + error_type = type; +} + +void +error_open(void) +{ + if (error_fp != NULL) + return; + if ((error_fp = fopen(ERROR_LOG_PATH, "w")) == NULL) + error_file(stderr, true, "%s: fopen", ERROR_LOG_PATH); +} + +/* + * Outputs a fatal message with the errno message string appended and terminates + * the process. + */ +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_file(stderr, true, fmt, ap); + va_end(ap); + exit(1); +} + +/* Outputs a fatal message and terminates the process. */ +void +diex(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_file(stderr, false, fmt, ap); + va_end(ap); + exit(1); +} + +/* Outputs a fatal message with the errno message string appended. */ +void +fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_doit(true, true, fmt, ap); + va_end(ap); +} + +/* Outputs a fatal message. */ +void +fatalx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_doit(false, true, fmt, ap); + va_end(ap); +} + +/* Outputs an informational message with the errno message string appended. */ +void +info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_doit(true, false, fmt, ap); + va_end(ap); +} + +/* Outputs an informational message. */ +void +infox(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_doit(false, false, fmt, ap); + va_end(ap); +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..27396b2 --- /dev/null +++ b/error.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ERROR_H +#define ERROR_H + +#define ERROR_LOG_PATH "vitunes-debug.log" + +#define ERROR_STDERR 0 +#define ERROR_CFG 1 +#define ERROR_PAINT 2 + +void debug(const char *, ...); +void die(const char *, ...); +void diex(const char *, ...); +void error_init(int); +void error_open(void); +void fatal(const char *, ...); +void fatalx(const char *, ...); +void info(const char *, ...); +void infox(const char *, ...); + +#endif diff --git a/add_urls.sh b/examples/add_urls.sh similarity index 100% rename from add_urls.sh rename to examples/add_urls.sh diff --git a/keybindings.c b/keybindings.c index 242e868..9744894 100644 --- a/keybindings.c +++ b/keybindings.c @@ -14,8 +14,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include + +#include "commands.h" +#include "compat.h" +#include "enums.h" +#include "error.h" #include "keybindings.h" - +#include "medialib.h" +#include "paint.h" +#include "player.h" +#include "str2argv.h" +#include "uinterface.h" +#include "vitunes.h" +#include "xmalloc.h" /* This table maps KeyActions to their string representations */ typedef struct { @@ -255,15 +269,8 @@ size_t KeyBindingsCapacity; void kb_increase_capacity() { - KeyBinding *new_buffer; - size_t nbytes; - KeyBindingsCapacity += KEYBINDINGS_CHUNK_SIZE; - nbytes = KeyBindingsCapacity * sizeof(KeyBinding); - if ((new_buffer = realloc(KeyBindings, nbytes)) == NULL) - err(1, "%s: failed to realloc(3) keybindings", __FUNCTION__); - - KeyBindings = new_buffer; + KeyBindings = xrealloc(KeyBindings, KeyBindingsCapacity, sizeof(KeyBinding)); } @@ -487,7 +494,7 @@ kba_scroll_row(KbaArgs a) ui.active->crow -= n; break; default: - errx(1, "%s: invalid direction", __FUNCTION__); + fatalx("%s: invalid direction", __FUNCTION__); } /* handle off-the-edge cases */ @@ -551,7 +558,8 @@ kba_scroll_page(KbaArgs a) maintain_row_idx = false; break; default: - errx(1, "scroll_page: invalid amount"); + fatalx("scroll_page: invalid amount"); + return; } swindow_scroll(ui.active, a.direction, diff); @@ -647,11 +655,11 @@ kba_scroll_col(KbaArgs a) ui.active->hoffset = maxhoff; break; default: - errx(1, "scroll_col: invalid direction"); + fatalx("scroll_col: invalid direction"); } break; default: - errx(1, "scroll_col: invalid amount"); + fatalx("scroll_col: invalid amount"); } /* redraw */ @@ -690,7 +698,7 @@ kba_jumpto_screen(KbaArgs a) ui.active->crow = max_row - n + 1; break; default: - errx(1, "jumpto_page: invalid location"); + fatalx("jumpto_page: invalid location"); } /* sanitize current row */ @@ -753,7 +761,8 @@ kba_jumpto_file(KbaArgs a) break; default: - errx(1, "jumpto_file: NUMBER type with no num!"); + fatalx("jumpto_file: NUMBER type with no num!"); + return; } break; @@ -767,7 +776,8 @@ kba_jumpto_file(KbaArgs a) break; default: - errx(1, "jumpto_file: invalid scale"); + fatalx("jumpto_file: invalid scale"); + return; } /* jump */ @@ -821,7 +831,7 @@ kba_search(KbaArgs a) prompt = "?"; break; default: - errx(1, "search: invalid direction"); + fatalx("search: invalid direction"); } /* get search phrase from user */ @@ -832,7 +842,7 @@ kba_search(KbaArgs a) /* set the global query description and the search direction */ if (str2argv(search_phrase, &argc, &argv, &errmsg) != 0) { - paint_error("parse error: %s in '%s'", errmsg, search_phrase); + fatalx("parse error: %s in '%s'", errmsg, search_phrase); free(search_phrase); return; } @@ -878,7 +888,8 @@ kba_search_find(KbaArgs a) break; default: - errx(1, "search_find: invalid direction"); + fatalx("search_find: invalid direction"); + return; } /* start looking from current row */ @@ -911,7 +922,7 @@ kba_search_find(KbaArgs a) /* found one, jump to it */ if (matches) { if (msg != NULL) - paint_message(msg); + infox("%s", msg); gnum_set(idx + 1); foo = get_dummy_args(); @@ -922,7 +933,7 @@ kba_search_find(KbaArgs a) } } - paint_error("Pattern not found: %s", mi_query_getraw()); + fatalx("Pattern not found: %s", mi_query_getraw()); } @@ -930,7 +941,7 @@ void kba_visual(KbaArgs a UNUSED) { if (ui.active == ui.library) { - paint_message("No visual mode in library window. Sorry."); + infox("No visual mode in library window. Sorry."); return; } @@ -1005,7 +1016,7 @@ kba_cut(KbaArgs a UNUSED) } if (start >= ui.active->nrows) { - paint_message("nothing to delete here!"); + infox("nothing to delete here!"); return; } @@ -1020,25 +1031,24 @@ kba_cut(KbaArgs a UNUSED) * while drunk. */ if (end != start + 1) { - paint_error("cannot delete multiple playlists"); + fatalx("cannot delete multiple playlists"); return; } if (p == mdb.library || p == mdb.filter_results) { - paint_error("cannot delete pseudo-playlists like LIBRARY or FILTER"); + fatalx("cannot delete pseudo-playlists like LIBRARY or FILTER"); return; } - if (asprintf(&warning, "Are you sure you want to delete '%s'?", p->name) == -1) - err(1, "cut: asprintf failed"); + xasprintf(&warning, "Are you sure you want to delete '%s'?", p->name); /* make sure user wants this */ if (user_get_yesno(warning, &response) != 0) { - paint_message("delete of '%s' cancelled", p->name); + infox("delete of '%s' cancelled", p->name); free(warning); return; } if (response != 1) { - paint_message("playlist '%s' not deleted", p->name); + infox("playlist '%s' not deleted", p->name); free(warning); return; } @@ -1066,7 +1076,7 @@ kba_cut(KbaArgs a UNUSED) /* can't delete from library */ if (viewing_playlist == mdb.library) { - paint_error("cannot delete from library"); + fatalx("cannot delete from library"); return; } @@ -1092,7 +1102,7 @@ kba_cut(KbaArgs a UNUSED) /* redraw */ paint_playlist(); paint_library(); - paint_message("%d fewer files.", end - start); + infox("%d fewer files.", end - start); } void @@ -1104,12 +1114,12 @@ kba_yank(KbaArgs a UNUSED) int n; if (ui.active == ui.library) { - paint_error("cannot yank in library window"); + fatalx("cannot yank in library window"); return; } if (viewing_playlist->nfiles == 0) { - paint_error("nothing to yank!"); + fatalx("nothing to yank!"); return; } @@ -1175,7 +1185,7 @@ kba_yank(KbaArgs a UNUSED) paint_playlist(); /* notify user # of rows yanked */ - paint_message("Yanked %d files.", end - start); + infox("Yanked %d files.", end - start); } void @@ -1185,7 +1195,7 @@ kba_paste(KbaArgs a) int start = 0; if (_yank_buffer.nfiles == 0) { - paint_error("nothing to paste"); + fatalx("nothing to paste"); return; } @@ -1199,7 +1209,7 @@ kba_paste(KbaArgs a) /* can't alter library */ if (p == mdb.library) { - paint_error("Cannot alter %s pseudo-playlist", mdb.library->name); + fatalx("Cannot alter %s pseudo-playlist", mdb.library->name); return; } @@ -1213,7 +1223,7 @@ kba_paste(KbaArgs a) start = p->nfiles; break; default: - errx(1, "paste: invalid placement [if]"); + fatalx("paste: invalid placement [if]"); } } else { @@ -1227,7 +1237,7 @@ kba_paste(KbaArgs a) if (start > p->nfiles) start = p->nfiles; break; default: - errx(1, "paste: invalid placement [else]"); + fatalx("paste: invalid placement [else]"); } } @@ -1243,9 +1253,9 @@ kba_paste(KbaArgs a) paint_library(); paint_playlist(); if (ui.active == ui.library) - paint_message("Pasted %d files to '%s'", _yank_buffer.nfiles, p->name); + infox("Pasted %d files to '%s'", _yank_buffer.nfiles, p->name); else - paint_message("Pasted %d files.", _yank_buffer.nfiles); + infox("Pasted %d files.", _yank_buffer.nfiles); } @@ -1253,14 +1263,14 @@ void kba_undo(KbaArgs a UNUSED) { if (ui.active == ui.library) { - paint_message("Cannot undo in library window."); + infox("Cannot undo in library window."); return; } if (playlist_undo(viewing_playlist) != 0) - paint_message("Nothing to undo."); + infox("Nothing to undo."); else - paint_message("Undo successfull."); + infox("Undo successfull."); /* TODO more informative message like in vim */ @@ -1275,14 +1285,14 @@ void kba_redo(KbaArgs a UNUSED) { if (ui.active == ui.library) { - paint_message("Cannot redo in library window."); + infox("Cannot redo in library window."); return; } if (playlist_redo(viewing_playlist) != 0) - paint_message("Nothing to redo."); + infox("Nothing to redo."); else - paint_message("Redo successfull."); + infox("Redo successfull."); /* TODO */ @@ -1368,7 +1378,7 @@ kba_show_file_info(KbaArgs a UNUSED) return; if (ui.active->crow >= ui.active->nrows) { - paint_message("no file here"); + infox("no file here"); return; } @@ -1398,7 +1408,7 @@ kba_load_playlist(KbaArgs a UNUSED) } else { /* play song */ if (ui.active->crow >= ui.active->nrows) { - paint_message("no file here"); + infox("no file here"); return; } player_set_queue(viewing_playlist, ui.active->voffset + ui.active->crow); @@ -1424,7 +1434,7 @@ kba_play(KbaArgs a UNUSED) } else { /* play song */ if (ui.active->crow >= ui.active->nrows) { - paint_message("no file here"); + infox("no file here"); return; } player_set_queue(viewing_playlist, ui.active->voffset + ui.active->crow); @@ -1491,7 +1501,7 @@ kba_volume(KbaArgs a) pcnt *= -1; break; default: - errx(1, "kba_volume: invalid direction"); + fatalx("kba_volume: invalid direction"); } player_volume_step(pcnt); @@ -1511,7 +1521,8 @@ kba_seek(KbaArgs a) secs = a.num * 60; break; default: - errx(1, "seek_playback: invalid scale"); + fatalx("seek_playback: invalid scale"); + return; } /* adjust for direction */ @@ -1523,7 +1534,8 @@ kba_seek(KbaArgs a) secs *= -1; break; default: - errx(1, "seek_playback: invalid direction"); + fatalx("seek_playback: invalid direction"); + return; } /* is there a multiplier? */ @@ -1568,7 +1580,7 @@ kba_toggle(KbaArgs a) /* get the command to execute */ if ((t = toggle_get(registr)) == NULL) { - paint_error("No toggle list in register %c (%i).", registr, registr); + fatalx("No toggle list in register %c (%i).", registr, registr); return; } @@ -1588,7 +1600,7 @@ kba_toggle(KbaArgs a) } break; default: - errx(1, "%s: invalid direction", __FUNCTION__); + fatalx("%s: invalid direction", __FUNCTION__); } /* execute */ @@ -1657,10 +1669,7 @@ yank_buffer _yank_buffer; void ybuffer_init() { - _yank_buffer.files = calloc(YANK_BUFFER_CHUNK_SIZE, sizeof(meta_info*)); - if (_yank_buffer.files == NULL) - err(1, "ybuffer_init: calloc(3) failed"); - + _yank_buffer.files = xcalloc(YANK_BUFFER_CHUNK_SIZE, sizeof(meta_info*)); _yank_buffer.capacity = YANK_BUFFER_CHUNK_SIZE; _yank_buffer.nfiles = 0; } @@ -1682,16 +1691,11 @@ ybuffer_free() void ybuffer_add(meta_info *f) { - meta_info **new_buff; - /* do we need to realloc()? */ if (_yank_buffer.nfiles == _yank_buffer.capacity) { _yank_buffer.capacity += YANK_BUFFER_CHUNK_SIZE; - int new_capacity = _yank_buffer.capacity * sizeof(meta_info*); - if ((new_buff = realloc(_yank_buffer.files, new_capacity)) == NULL) - err(1, "ybuffer_add: realloc(3) failed [%i]", new_capacity); - - _yank_buffer.files = new_buff; + _yank_buffer.files = xrealloc(_yank_buffer.files, _yank_buffer.capacity, + sizeof(meta_info *)); } /* add the file */ @@ -1733,8 +1737,7 @@ match_command_name(const char *input, const char *cmd) /* check for '!' weirdness and abbreviations */ - if ((icopy = strdup(input)) == NULL) - err(1, "match_command_name: strdup(3) failed"); + icopy = xstrdup(input); /* remove '!' from input, if present */ if (strstr(icopy, "!") != NULL) diff --git a/keybindings.h b/keybindings.h index 4ad5ccb..5df52d7 100644 --- a/keybindings.h +++ b/keybindings.h @@ -17,12 +17,9 @@ #ifndef KEYBINDINGS_H #define KEYBINDINGS_H -#include "compat.h" +#include -#include "debug.h" -#include "enums.h" -#include "paint.h" -#include "vitunes.h" +#include "meta_info.h" /* * List of all actions that can be bound by keybindings. diff --git a/medialib.c b/medialib.c index 35cfd2e..f577743 100644 --- a/medialib.c +++ b/medialib.c @@ -14,7 +14,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "error.h" #include "medialib.h" +#include "meta_info.h" +#include "playlist.h" +#include "xmalloc.h" /* The global media library struct */ medialib mdb; @@ -32,22 +47,17 @@ medialib_load(const char *db_file, const char *playlist_dir) int i; /* copy file/directory names */ - mdb.db_file = strdup(db_file); - mdb.playlist_dir = strdup(playlist_dir); - if (mdb.db_file == NULL || mdb.playlist_dir == NULL) - err(1, "failed to strdup db file and playlist dir in medialib_init"); + mdb.db_file = xstrdup(db_file); + mdb.playlist_dir = xstrdup(playlist_dir); /* setup pseudo-playlists */ mdb.library = playlist_new(); mdb.library->filename = NULL; - mdb.library->name = strdup("--LIBRARY--"); + mdb.library->name = xstrdup("--LIBRARY--"); mdb.filter_results = playlist_new(); mdb.filter_results->filename = NULL; - mdb.filter_results->name = strdup("--FILTER--"); - - if (mdb.library->name == NULL || mdb.filter_results->name == NULL) - err(1, "failed to strdup pseudo-names in medialib_load"); + mdb.filter_results->name = xstrdup("--FILTER--"); /* load the actual database */ medialib_db_load(db_file); @@ -55,9 +65,7 @@ medialib_load(const char *db_file, const char *playlist_dir) /* setup initial record keeping for playlists */ mdb.nplaylists = 0; mdb.playlists_capacity = 2; - mdb.playlists = calloc(2, sizeof(playlist*)); - if (mdb.playlists == NULL) - err(1, "medialib_load: failed to allocate initial playlists"); + mdb.playlists = xcalloc(2, sizeof(playlist*)); /* add library/filter pseudo-playlists */ medialib_playlist_add(mdb.library); @@ -65,17 +73,18 @@ medialib_load(const char *db_file, const char *playlist_dir) /* load the rest */ npfiles = retrieve_playlist_filenames(mdb.playlist_dir, &pfiles); - for (i = 0; i < npfiles; i++) { - p = playlist_load(pfiles[i], mdb.library->files, mdb.library->nfiles); - medialib_playlist_add(p); - free(pfiles[i]); + if (npfiles) { + for (i = 0; i < npfiles; i++) { + p = playlist_load(pfiles[i], mdb.library->files, mdb.library->nfiles); + medialib_playlist_add(p); + free(pfiles[i]); + } + free(pfiles); } /* set all playlists as saved initially */ for (i = 0; i < mdb.nplaylists; i++) mdb.playlists[i]->needs_saving = false; - - free(pfiles); } /* free() all memory associated with global media library */ @@ -106,16 +115,11 @@ medialib_destroy() void medialib_playlist_add(playlist *p) { - playlist **new_playlists; - /* check to see if we need to resize the array */ if (mdb.nplaylists == mdb.playlists_capacity) { mdb.playlists_capacity += MEDIALIB_PLAYLISTS_CHUNK_SIZE; - int size = mdb.playlists_capacity * sizeof(playlist*); - if ((new_playlists = realloc(mdb.playlists, size)) == NULL) - err(1, "medialib_playlist_add: realloc failed"); - - mdb.playlists = new_playlists; + mdb.playlists = xrealloc(mdb.playlists, mdb.playlists_capacity, + sizeof(playlist *)); } mdb.playlists[mdb.nplaylists++] = p; @@ -131,7 +135,7 @@ medialib_playlist_remove(int pindex) int i; if (pindex < 0 || pindex >= mdb.nplaylists) - errx(1, "medialib_playlist_remove: index %d out of range", pindex); + fatalx("medialib_playlist_remove: index %d out of range", pindex); playlist_delete(mdb.playlists[pindex]); @@ -155,20 +159,20 @@ medialib_setup_files(const char *vitunes_dir, const char *db_file, /* create vitunes directory */ if (mkdir(vitunes_dir, S_IRWXU) == -1) { if (errno == EEXIST) - warnx("vitunes directory '%s' already exists (OK)", vitunes_dir); + infox("vitunes directory '%s' already exists (OK)", vitunes_dir); else - err(1, "unable to create vitunes directory '%s'", vitunes_dir); + fatal("unable to create vitunes directory '%s'", vitunes_dir); } else - warnx("vitunes directory '%s' created", vitunes_dir); + infox("vitunes directory '%s' created", vitunes_dir); /* create playlists directory */ if (mkdir(playlist_dir, S_IRWXU) == -1) { if (errno == EEXIST) - warnx("playlists directory '%s' already exists (OK)", playlist_dir); + infox("playlists directory '%s' already exists (OK)", playlist_dir); else - err(1, "unable to create playlists directory '%s'", playlist_dir); + fatal("unable to create playlists directory '%s'", playlist_dir); } else - warnx("playlists directory '%s' created", playlist_dir); + infox("playlists directory '%s' created", playlist_dir); /* create database file */ if (stat(db_file, &sb) < 0) { @@ -179,18 +183,18 @@ medialib_setup_files(const char *vitunes_dir, const char *db_file, /* open for writing */ if ((f = fopen(db_file, "w")) == NULL) - err(1, "failed to create database file '%s'", db_file); + fatal("failed to create database file '%s'", db_file); /* save header & version */ fwrite("vitunes", strlen("vitunes"), 1, f); fwrite(version, sizeof(version), 1, f); - warnx("empty database at '%s' created", db_file); + infox("empty database at '%s' created", db_file); fclose(f); } else - err(1, "database file '%s' exists, but cannot access it", db_file); + fatal("database file '%s' exists, but cannot access it", db_file); } else - warnx("database file '%s' already exists (OK)", db_file); + infox("database file '%s' already exists (OK)", db_file); } /* used to sort media db by filenames. */ @@ -214,12 +218,12 @@ medialib_db_load(const char *db_file) int version[3]; if ((fin = fopen(db_file, "r")) == NULL) - err(1, "Failed to open database file '%s'", db_file); + fatal("Failed to open database file '%s'", db_file); /* read and check header & version */ fread(header, strlen("vitunes"), 1, fin); if (strncmp(header, "vitunes", strlen("vitunes")) != 0) - errx(1, "Database file '%s' NOT a vitunes database", db_file); + fatalx("Database file '%s' NOT a vitunes database", db_file); fread(version, sizeof(version), 1, fin); if (version[0] != DB_VERSION_MAJOR || version[1] != DB_VERSION_MINOR @@ -244,7 +248,7 @@ medialib_db_load(const char *db_file) if (feof(fin)) mi_free(mi); else if (ferror(fin)) - err(1, "Error loading database file '%s'", db_file); + fatal("Error loading database file '%s'", db_file); else playlist_files_append(mdb.library, &mi, 1, false); } @@ -264,7 +268,7 @@ medialib_db_save(const char *db_file) int i; if ((fout = fopen(db_file, "w")) == NULL) - err(1, "medialib_db_save: failed to open database file '%s'", db_file); + fatal("medialib_db_save: failed to open database file '%s'", db_file); /* save header & version */ fwrite("vitunes", strlen("vitunes"), 1, fout); @@ -274,7 +278,7 @@ medialib_db_save(const char *db_file) for (i = 0; i < mdb.library->nfiles; i++) { mi_fwrite(mdb.library->files[i], fout); if (ferror(fout)) - err(1, "medialib_db_save: error saving database"); + fatal("medialib_db_save: error saving database"); } fclose(fout); @@ -449,7 +453,7 @@ medialib_db_scan_dirs(char *dirlist[]) fts = fts_open(dirlist, FTS_LOGICAL | FTS_NOCHDIR, NULL); if (fts == NULL) - err(1, "medialib_db_scan_dirs: fts_open failed"); + fatal("medialib_db_scan_dirs: fts_open failed"); while ((ftsent = fts_read(fts)) != NULL) { @@ -476,7 +480,7 @@ medialib_db_scan_dirs(char *dirlist[]) /* get the full name for the file */ if (realpath(ftsent->fts_accpath, fullname) == NULL) { - err(1, "medialib_db_scan_dirs: realpath failed for '%s'", + fatal("medialib_db_scan_dirs: realpath failed for '%s'", ftsent->fts_accpath); } @@ -536,7 +540,7 @@ medialib_db_scan_dirs(char *dirlist[]) } if (fts_close(fts) == -1) - err(1, "medialib_db_scan_dirs: failed to close file heirarchy"); + fatal("medialib_db_scan_dirs: failed to close file heirarchy"); /* save to file */ medialib_db_save(mdb.db_file); diff --git a/medialib.h b/medialib.h index 5f6e3af..20e707d 100644 --- a/medialib.h +++ b/medialib.h @@ -25,19 +25,6 @@ #ifndef MEDIALIB_H #define MEDIALIB_H -#include "compat.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "debug.h" -#include "meta_info.h" #include "playlist.h" #define MEDIALIB_PLAYLISTS_CHUNK_SIZE 100 diff --git a/meta_info.c b/meta_info.c index b2c81d2..be446c8 100644 --- a/meta_info.c +++ b/meta_info.c @@ -14,7 +14,25 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* non-baes includes (just TagLib) */ +#include + +#include "compat.h" +#include "enums.h" +#include "error.h" #include "meta_info.h" +#include "xmalloc.h" /* human-readable names of all of the string-type meta information values */ const char *MI_CINFO_NAMES[] = { @@ -38,9 +56,7 @@ mi_new(void) meta_info *mi; int i; - if ((mi = malloc(sizeof(meta_info))) == NULL) - err(1, "mi_new: meta_info malloc failed"); - + mi = xmalloc(sizeof(meta_info)); mi->filename = NULL; mi->length = 0; mi->last_updated = 0; @@ -111,18 +127,11 @@ mi_fread(meta_info *mi, FILE *fin) fread(lengths, sizeof(lengths), 1, fin); /* allocate all needed space in the meta_info struct, and zero */ - if ((mi->filename = calloc(lengths[0] + 1, sizeof(char))) == NULL) - err(1, "mi_fread: calloc filename failed"); - - bzero(mi->filename, sizeof(char) * (lengths[0] + 1)); + mi->filename = xcalloc(lengths[0] + 1, sizeof(char)); for (i = 0; i < MI_NUM_CINFO; i++) { - if (lengths[i+1] > 0) { - if ((mi->cinfo[i] = calloc(lengths[i+1] + 1, sizeof(char))) == NULL) - err(1, "mi_fread: failed to calloc cinfo"); - - bzero(mi->cinfo[i], sizeof(char) * (lengths[i+1] + 1)); - } + if (lengths[i+1] > 0) + mi->cinfo[i] = xcalloc(lengths[i+1] + 1, sizeof(char)); } /* read */ @@ -181,10 +190,9 @@ mi_extract(const char *filename) /* store full filename in meta_info struct */ bzero(fullname, sizeof(fullname)); if (realpath(filename, fullname) == NULL) - err(1, "mi_extract: realpath failed to resolve '%s'", filename); + fatal("mi_extract: realpath failed to resolve '%s'", filename); - if ((mi->filename = strdup(fullname)) == NULL) - errx(1, "mi_extract: strdup failed for '%s'", fullname); + mi->filename = xstrdup(fullname); /* start extracting fields using TagLib... */ @@ -202,45 +210,32 @@ mi_extract(const char *filename) /* artist/album/title/genre */ if ((str = taglib_tag_artist(tag)) != NULL) - mi->cinfo[MI_CINFO_ARTIST] = strdup(str); + mi->cinfo[MI_CINFO_ARTIST] = xstrdup(str); if ((str = taglib_tag_album(tag)) != NULL) - mi->cinfo[MI_CINFO_ALBUM] = strdup(str); + mi->cinfo[MI_CINFO_ALBUM] = xstrdup(str); if ((str = taglib_tag_title(tag)) != NULL) - mi->cinfo[MI_CINFO_TITLE] = strdup(str); + mi->cinfo[MI_CINFO_TITLE] = xstrdup(str); if ((str = taglib_tag_genre(tag)) != NULL) - mi->cinfo[MI_CINFO_GENRE] = strdup(str); + mi->cinfo[MI_CINFO_GENRE] = xstrdup(str); if ((str = taglib_tag_comment(tag)) != NULL) - mi->cinfo[MI_CINFO_COMMENT] = strdup(str); - - if (mi->cinfo[MI_CINFO_ARTIST] == NULL - || mi->cinfo[MI_CINFO_ALBUM] == NULL - || mi->cinfo[MI_CINFO_TITLE] == NULL - || mi->cinfo[MI_CINFO_GENRE] == NULL - || mi->cinfo[MI_CINFO_COMMENT] == NULL) - err(1, "mi_extract: strdup for CINFO failed"); + mi->cinfo[MI_CINFO_COMMENT] = xstrdup(str); /* track number */ - if (taglib_tag_track(tag) > 0) { - if (asprintf(&(mi->cinfo[MI_CINFO_TRACK]), "%3i", taglib_tag_track(tag)) == -1) - err(1, "mi_extract: asprintf failed for CINFO_TRACK"); - } + if (taglib_tag_track(tag) > 0) + xasprintf(&(mi->cinfo[MI_CINFO_TRACK]), "%3i", taglib_tag_track(tag)); /* year */ - if (taglib_tag_year(tag) > 0) { - if (asprintf(&(mi->cinfo[MI_CINFO_YEAR]), "%i", taglib_tag_year(tag)) == -1) - err(1, "mi_extract: asprintf failed for CINFO_YEAR"); - } + if (taglib_tag_year(tag) > 0) + xasprintf(&(mi->cinfo[MI_CINFO_YEAR]), "%i", taglib_tag_year(tag)); /* playlength in seconds (will be 0 if unavailable) */ mi->length = taglib_audioproperties_length(properties); - if (mi->length > 0) { - if ((mi->cinfo[MI_CINFO_LENGTH] = strdup(time2str(mi->length))) == NULL) - err(1, "mi_extract: strdup failed for CINO_LENGTH"); - } + if (mi->length > 0) + mi->cinfo[MI_CINFO_LENGTH] = xstrdup(time2str(mi->length)); /* record the time we extracted this info */ time(&mi->last_updated); @@ -336,7 +331,7 @@ void mi_query_add_token(const char *token) { if (_mi_query.ntokens == MI_MAX_QUERY_TOKENS) - errx(1, "mi_query_add_token: reached shamefull limit"); + fatalx("mi_query_add_token: reached shamefull limit"); /* match or no? */ if (token[0] == '!') { @@ -346,8 +341,7 @@ mi_query_add_token(const char *token) _mi_query.match[_mi_query.ntokens] = true; /* copy token */ - if ((_mi_query.tokens[_mi_query.ntokens++] = strdup(token)) == NULL) - err(1, "mi_query_add_token: strdup failed"); + _mi_query.tokens[_mi_query.ntokens++] = xstrdup(token); } void @@ -356,8 +350,7 @@ mi_query_setraw(const char *query) if (_mi_query.raw != NULL) free(_mi_query.raw); - if ((_mi_query.raw = strdup(query)) == NULL) - err(1, "mi_query_setraw: query strdup failed"); + _mi_query.raw = xstrdup(query); } const char * @@ -491,9 +484,7 @@ mi_sort_set(const char *s, const char **errmsg) }; *errmsg = NULL; - if ((line = strdup(s)) == NULL) - err(1, "mi_sort_set: sort strdup failed"); - + line = xstrdup(s); idx = 0; copy = line; @@ -699,9 +690,7 @@ mi_display_set(const char *display, const char **errmsg) *errmsg = NULL; new_display.nfields = 0; - if ((s = strdup(display)) == NULL) - err(1, "mi_display_set: display strdup failed"); - + s = xstrdup(display); copy = s; idx = 0; diff --git a/meta_info.h b/meta_info.h index 19c04d7..24fead9 100644 --- a/meta_info.h +++ b/meta_info.h @@ -17,24 +17,9 @@ #ifndef META_INFO_H #define META_INFO_H -#include "compat.h" - -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -/* non-baes includes (just TagLib) */ -#include - -#include "debug.h" #include "enums.h" /* the character-info fields. used for all meta-info that's shown */ diff --git a/paint.c b/paint.c index e0b4a8f..aa2737d 100644 --- a/paint.c +++ b/paint.c @@ -14,7 +14,25 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "enums.h" +#include "error.h" +#include "medialib.h" +#include "meta_info.h" #include "paint.h" +#include "player.h" +#include "playlist.h" +#include "uinterface.h" +#include "vitunes.h" +#include "xmalloc.h" /* globals */ _colors colors; @@ -67,7 +85,7 @@ num2fmt(int n, Direction d) if (n <= 0) { endwin(); - errx(1, "num2sfmt: invalid number %d provided", n); + fatalx("num2sfmt: invalid number %d provided", n); } if (d == LEFT) @@ -547,49 +565,6 @@ paint_all() paint_playlist(); } -/* - * Paints an error message to the command/status window. The usage is - * identical to that of printf(3) (vwprintw(3) actually). - */ -void -paint_error(char *fmt, ...) -{ - va_list ap; - - werase(ui.command); - wmove(ui.command, 0, 0); - wattron(ui.command, COLOR_PAIR(colors.errors)); - - va_start(ap, fmt); - vwprintw(ui.command, fmt, ap); - va_end(ap); - - beep(); - wattroff(ui.command, COLOR_PAIR(colors.errors)); - wrefresh(ui.command); -} - -/* - * Paints an informational message to the command/status window. The usage - * is identical to that of printf(3) (vwprintw(3) actually). - */ -void -paint_message(char *fmt, ...) -{ - va_list ap; - - werase(ui.command); - wmove(ui.command, 0, 0); - wattron(ui.command, COLOR_PAIR(colors.messages)); - - va_start(ap, fmt); - vwprintw(ui.command, fmt, ap); - va_end(ap); - - wattroff(ui.command, COLOR_PAIR(colors.messages)); - wrefresh(ui.command); -} - /* * Each of these members of the global color object will be * run through init_pair(3) @@ -687,8 +662,7 @@ paint_str2color(const char *str) char *numberstr; int number; - if ((color = strdup(str)) == NULL) - err(1, "%s: strdup of '%s' failed.", __FUNCTION__, str); + color = xstrdup(str); if ((numberstr = strtok(color, "color")) == NULL) { free(color); diff --git a/paint.h b/paint.h index b8c8643..d5078a1 100644 --- a/paint.h +++ b/paint.h @@ -17,21 +17,7 @@ #ifndef PAINT_H #define PAINT_H -#include "compat.h" - -#include -#include -#include -#include -#include -#include - -#include "enums.h" #include "meta_info.h" -#include "player.h" -#include "playlist.h" -#include "uinterface.h" -#include "vitunes.h" /* colors used by paint - each of these will be a number for a COLOR_PAIR */ typedef struct { @@ -80,10 +66,6 @@ void paint_all(); extern bool showing_file_info; void paint_playlist_file_info(const meta_info *m); -/* routines for painting errors/messages in the command/status window */ -void paint_error(char *fmt, ...); -void paint_message(char *fmt, ...); - /* for setting up and working with the colors */ void paint_setup_colors(); int paint_str2item(const char *str); diff --git a/player.c b/player.c index 37bf637..c1d5ffe 100644 --- a/player.c +++ b/player.c @@ -14,7 +14,23 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include + +#include "compat.h" +#include "error.h" +#include "paint.h" #include "player.h" +#include "playlist.h" +#include "uinterface.h" +#include "vitunes.h" + +/* "static" backends (those that aren't dynamically loaded) */ +#include "mplayer.h" +#if defined(ENABLE_GSTREAMER) +# include "gstplayer.h" +#endif /* globals */ player_backend_t player; @@ -25,7 +41,7 @@ player_info_t player_info; static void callback_playnext() { player_skip_song(1); } static void -callback_fatal(char *fmt, ...) +callback_fatal(const char *fmt, ...) { va_list ap; @@ -119,16 +135,14 @@ player_init(const char *backend) } if (!found) - errx(1, "media backend '%s' is unknown", backend); + fatalx("media backend '%s' is unknown", backend); - if (player.dynamic) { - ui_destroy(); - errx(1, "dynamically loaded backends not yet supported"); - } + if (player.dynamic) + fatalx("dynamically loaded backends not yet supported"); player.set_callback_playnext(callback_playnext); - player.set_callback_notice(paint_message); - player.set_callback_error(paint_error); + player.set_callback_notice(infox); + player.set_callback_error(fatalx); player.set_callback_fatal(callback_fatal); player.start(); } @@ -150,10 +164,10 @@ void player_play() { if (player_info.queue == NULL) - errx(1, "player_play: bad queue/qidx"); + fatalx("player_play: bad queue/qidx"); if (player_info.qidx < 0 || player_info.qidx > player_info.queue->nfiles) - errx(1, "player_play: qidx %i out-of-range", player_info.qidx); + fatalx("player_play: qidx %i out-of-range", player_info.qidx); player.play(player_info.queue->files[player_info.qidx]->filename); diff --git a/player.h b/player.h index 029fb43..e05a40b 100644 --- a/player.h +++ b/player.h @@ -17,19 +17,7 @@ #ifndef PLAYER_H #define PLAYER_H -#include "compat.h" - -#include - #include "playlist.h" -#include "paint.h" -#include "debug.h" - -/* "static" backends (those that aren't dynamically loaded) */ -#include "players/mplayer.h" -#if defined(ENABLE_GSTREAMER) -# include "players/gstplayer.h" -#endif /* * Available play-modes. @@ -98,9 +86,9 @@ typedef struct { /* callback functions */ void (*set_callback_playnext)(void (*f)(void)); - void (*set_callback_notice)(void (*f)(char *, ...)); - void (*set_callback_error)(void (*f)(char *, ...)); - void (*set_callback_fatal)(void (*f)(char *, ...)); + void (*set_callback_notice)(void (*f)(const char *, ...)); + void (*set_callback_error)(void (*f)(const char *, ...)); + void (*set_callback_fatal)(void (*f)(const char *, ...)); /* monitor function */ void (*monitor)(void); diff --git a/players/gstplayer.c b/players/gstplayer.c index 01f77a7..4d60c42 100644 --- a/players/gstplayer.c +++ b/players/gstplayer.c @@ -22,7 +22,8 @@ */ #include "gstplayer.h" -#include "../player.h" +#include "player.h" +#include "vitunes.h" /* player data */ static gst_player gplayer; @@ -269,17 +270,17 @@ gstplayer_set_callback_playnext(void (*f)(void)) gplayer.playnext_cb = f; } void -gstplayer_set_callback_notice(void (*f)(char *, ...)) +gstplayer_set_callback_notice(void (*f)(const char *, ...)) { gplayer.notice_cb = f; } void -gstplayer_set_callback_error(void (*f)(char *, ...)) +gstplayer_set_callback_error(void (*f)(const char *, ...)) { gplayer.error_cb = f; } void -gstplayer_set_callback_fatal(void (*f)(char *, ...)) +gstplayer_set_callback_fatal(void (*f)(const char *, ...)) { gplayer.fatal_cb = f; } diff --git a/players/gstplayer.h b/players/gstplayer.h index b23c8f7..0972644 100644 --- a/players/gstplayer.h +++ b/players/gstplayer.h @@ -34,10 +34,10 @@ typedef struct { bool paused; bool about_to_finish; /* callback functions */ - void (*playnext_cb)(void); - void (*notice_cb)(char *, ...); - void (*error_cb)(char *, ...); - void (*fatal_cb)(char *, ...); + void (*playnext_cb)(void); + void (*notice_cb)(const char *, ...); + void (*error_cb)(const char *, ...); + void (*fatal_cb)(const char *, ...); /* backend data */ GstElement *player; @@ -60,9 +60,9 @@ bool gstplayer_is_playing(); bool gstplayer_is_paused(); void gstplayer_set_callback_playnext(void (*f)(void)); -void gstplayer_set_callback_notice(void (*f)(char *, ...)); -void gstplayer_set_callback_error(void (*f)(char *, ...)); -void gstplayer_set_callback_fatal(void (*f)(char *, ...)); +void gstplayer_set_callback_notice(void (*f)(const char *, ...)); +void gstplayer_set_callback_error(void (*f)(const char *, ...)); +void gstplayer_set_callback_fatal(void (*f)(const char *, ...)); void gstplayer_monitor(); diff --git a/players/mplayer.c b/players/mplayer.c index fcc8f71..4b468bb 100644 --- a/players/mplayer.c +++ b/players/mplayer.c @@ -14,14 +14,32 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" #include "mplayer.h" #include "mplayer_conf.h" +#include "player_utils.h" +#include "xmalloc.h" /* callback functions */ void (*mplayer_callback_playnext)(void) = NULL; -void (*mplayer_callback_notice)(char *, ...) = NULL; -void (*mplayer_callback_error)(char *, ...) = NULL; -void (*mplayer_callback_fatal)(char *, ...) = NULL; +void (*mplayer_callback_notice)(const char *, ...) = NULL; +void (*mplayer_callback_error)(const char *, ...) = NULL; +void (*mplayer_callback_fatal)(const char *, ...) = NULL; /* record keeping */ @@ -178,9 +196,7 @@ mplayer_play(const char *file) static const char *cmd_fmt = "\nloadfile \"%s\" 0\nget_property time_pos\n"; char *cmd; - if (asprintf(&cmd, cmd_fmt, file) == -1) - err(1, "%s: asprintf failed", __FUNCTION__); - + xasprintf(&cmd, cmd_fmt, file); mplayer_send_cmd(cmd); free(cmd); @@ -222,9 +238,7 @@ mplayer_seek(int seconds) if (!mplayer_state.playing) return; - if (asprintf(&cmd, cmd_fmt, seconds) == -1) - err(1, "%s: asprintf failed", __FUNCTION__); - + xasprintf(&cmd, cmd_fmt, seconds); mplayer_send_cmd(cmd); free(cmd); @@ -250,9 +264,7 @@ mplayer_volume_step(float percent) return; } - if (asprintf(&cmd, cmd_fmt, percent) == -1) - err(1, "%s: asprintf failed", __FUNCTION__); - + xasprintf(&cmd, cmd_fmt, percent); mplayer_send_cmd(cmd); free(cmd); @@ -271,10 +283,7 @@ mplayer_volume_set(float percent) if (percent > 100) percent = 100; if (percent < 0) percent = 0; - if (asprintf(&cmd, cmd_fmt, percent) == -1) - err(1, "%s: asprintf failed", __FUNCTION__); - - + xasprintf(&cmd, cmd_fmt, percent); mplayer_send_cmd(cmd); free(cmd); @@ -306,19 +315,19 @@ mplayer_set_callback_playnext(void (*f)(void)) } void -mplayer_set_callback_notice(void (*f)(char *, ...)) +mplayer_set_callback_notice(void (*f)(const char *, ...)) { mplayer_callback_notice = f; } void -mplayer_set_callback_error(void (*f)(char *, ...)) +mplayer_set_callback_error(void (*f)(const char *, ...)) { mplayer_callback_error = f; } void -mplayer_set_callback_fatal(void (*f)(char *, ...)) +mplayer_set_callback_fatal(void (*f)(const char *, ...)) { mplayer_callback_fatal = f; } diff --git a/players/mplayer.h b/players/mplayer.h index 03700cd..4d86fef 100644 --- a/players/mplayer.h +++ b/players/mplayer.h @@ -17,28 +17,6 @@ #ifndef MPLAYER_H #define MPLAYER_H -#include "../compat.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "player_utils.h" - -#ifdef DEBUG -# include "../debug.h" -#endif - void mplayer_start(); void mplayer_finish(); void mplayer_sigchld(); @@ -55,9 +33,9 @@ bool mplayer_is_playing(); bool mplayer_is_paused(); void mplayer_set_callback_playnext(void (*f)(void)); -void mplayer_set_callback_notice(void (*f)(char *, ...)); -void mplayer_set_callback_error(void (*f)(char *, ...)); -void mplayer_set_callback_fatal(void (*f)(char *, ...)); +void mplayer_set_callback_notice(void (*f)(const char *, ...)); +void mplayer_set_callback_error(void (*f)(const char *, ...)); +void mplayer_set_callback_fatal(void (*f)(const char *, ...)); void mplayer_monitor(); diff --git a/players/player_utils.c b/players/player_utils.c index 271774a..c2ac83d 100644 --- a/players/player_utils.c +++ b/players/player_utils.c @@ -14,7 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include +#include +#include + +#include "compat.h" #include "player_utils.h" +#include "xmalloc.h" bool exe_in_path(const char *e) @@ -22,17 +31,13 @@ exe_in_path(const char *e) char *path, *path_copy, *part, *test; bool found; - if ((path = strdup(getenv("PATH"))) == NULL) - err(1, "%s: strdup/getenv failed for $PATH", __FUNCTION__); - + path = xstrdup(getenv("PATH")); path_copy = path; found = false; while ((part = strsep(&path, ":")) != NULL && !found) { if (strlen(part) == 0) continue; - if (asprintf(&test, "%s/%s", part, e) == -1) - err(1, "%s: failed to build path", __FUNCTION__); - + xasprintf(&test, "%s/%s", part, e); if (access(test, X_OK) == 0) found = true; diff --git a/players/player_utils.h b/players/player_utils.h index f54b72e..d6ff969 100644 --- a/players/player_utils.h +++ b/players/player_utils.h @@ -17,15 +17,6 @@ #ifndef PLAYER_UTILS_H #define PLAYER_UTILS_H -#include "../compat.h" - -#include -#include -#include -#include -#include -#include - bool exe_in_path(const char *e); #endif diff --git a/playlist.c b/playlist.c index ceac2fc..080c357 100644 --- a/playlist.c +++ b/playlist.c @@ -14,22 +14,31 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "error.h" +#include "meta_info.h" #include "playlist.h" +#include "xmalloc.h" int history_size = DEFAULT_HISTORY_SIZE; void playlist_increase_capacity(playlist *p) { - meta_info **new_files; - size_t nbytes; - p->capacity += PLAYLIST_CHUNK_SIZE; - nbytes = p->capacity * sizeof(meta_info*); - if ((new_files = realloc(p->files, nbytes)) == NULL) - err(1, "%s: failed to realloc(3) files", __FUNCTION__); - - p->files = new_files; + p->files = xrealloc(p->files, p->capacity, sizeof(meta_info *)); } /* @@ -41,12 +50,8 @@ playlist_new(void) { playlist *p; - if ((p = malloc(sizeof(playlist))) == NULL) - err(1, "playlist_new: failed to allocate playlist"); - - if ((p->files = calloc(PLAYLIST_CHUNK_SIZE, sizeof(meta_info*))) == NULL) - err(1, "playlist_new: failed to allocate files"); - + p = xmalloc(sizeof(playlist)); + p->files = xcalloc(PLAYLIST_CHUNK_SIZE, sizeof(meta_info*)); p->capacity = PLAYLIST_CHUNK_SIZE; p->filename = NULL; p->name = NULL; @@ -85,20 +90,13 @@ playlist_dup(const playlist *original, const char *filename, newplist->nfiles = original->nfiles; newplist->capacity = original->nfiles; - if (name != NULL) { - if ((newplist->name = strdup(name)) == NULL) - err(1, "playlist_dup: strdup name failed"); - } - if (filename != NULL) { - if ((newplist->filename = strdup(filename)) == NULL) - err(1, "playlist_dup: strdup filename failed"); - } + if (name != NULL) + newplist->name = xstrdup(name); + if (filename != NULL) + newplist->filename = xstrdup(filename); /* copy all of the files */ - newplist->files = calloc(original->nfiles, sizeof(meta_info*)); - if (newplist->files == NULL) - err(1, "playlist_dup: failed to allocate files"); - + newplist->files = xcalloc(original->nfiles, sizeof(meta_info*)); for (i = 0; i < original->nfiles; i++) newplist->files[i] = original->files[i]; @@ -115,7 +113,7 @@ playlist_files_add(playlist *p, meta_info **f, int start, int size, bool record) int i; if (start < 0 || start > p->nfiles) - errx(1, "playlist_file_add: index %d out of range", start); + fatalx("playlist_file_add: index %d out of range", start); while (p->capacity <= p->nfiles + size) playlist_increase_capacity(p); @@ -154,7 +152,7 @@ playlist_files_remove(playlist *p, int start, int size, bool record) int i; if (start < 0 || start >= p->nfiles) - errx(1, "playlist_remove_file: index %d out of range", start); + fatalx("playlist_remove_file: index %d out of range", start); if (record) { changes = changeset_create(CHANGE_REMOVE, size, &(p->files[start]), start); @@ -174,7 +172,7 @@ void playlist_file_replace(playlist *p, int index, meta_info *newEntry) { if (index < 0 || index >= p->nfiles) - errx(1, "playlist_file_replace: index %d out of range", index); + fatalx("playlist_file_replace: index %d out of range", index); p->files[index] = newEntry; } @@ -209,14 +207,12 @@ playlist_load(const char *filename, meta_info **db, int ndb) /* open file */ if ((fin = fopen(filename, "r")) == NULL) - err(1, "playlist_load: failed to open playlist '%s'", filename); + fatal("playlist_load: failed to open playlist '%s'", filename); /* create playlist and setup */ playlist *p = playlist_new(); - p->filename = strdup(filename); - p->name = strdup(basename(p->filename)); - if (p->filename == NULL || p->name == NULL) - err(1, "playlist_load: failed to allocate info for playlist '%s'", filename); + p->filename = xstrdup(filename); + p->name = xstrdup(basename(p->filename)); /* hack to remove '.playlist' from name */ period = strrchr(p->name, '.'); @@ -236,13 +232,11 @@ playlist_load(const char *filename, meta_info **db, int ndb) } else { /* file does NOT exist in DB */ /* create empty meta-info object with just the file name */ mi = mi_new(); - mi->filename = strdup(entry); - if (mi->filename == NULL) - err(1, "playlist_load: failed to strdup filename"); + mi->filename = xstrdup(entry); /* add new record to the db and link it to the playlist */ playlist_files_append(p, &mi, 1, false); - warnx("playlist \"%s\", file \"%s\" is NOT in media database (added for now)", + infox("playlist \"%s\", file \"%s\" is NOT in media database (added for now)", p->name, entry); } } @@ -262,12 +256,12 @@ playlist_save(const playlist *p) int i; if ((fout = fopen(p->filename, "w")) == NULL) - err(1, "playlist_save: failed to open playlist \"%s\"", p->filename); + fatal("playlist_save: failed to open playlist \"%s\"", p->filename); /* write each song to file */ for (i = 0; i < p->nfiles; i++) { if (fprintf(fout, "%s\n", p->files[i]->filename) == -1) - err(1, "playlist_save: failed to record playlist \"%s\"", p->filename); + fatal("playlist_save: failed to record playlist \"%s\"", p->filename); } fclose(fout); @@ -282,7 +276,7 @@ playlist_delete(playlist *p) { /* delete file if the playlist is stored in a file */ if (p->filename != NULL && unlink(p->filename) != 0) - err(1, "playlist_delete: failed to delete playlist \"%s\"", p->filename); + fatal("playlist_delete: failed to delete playlist \"%s\"", p->filename); /* destroy/free() all memory */ playlist_free(p); @@ -353,24 +347,24 @@ retrieve_playlist_filenames(const char *dirname, char ***fnames) # endif /* build the search pattern */ - if (asprintf(&glob_pattern, "%s/*.playlist", dirname) == -1) - errx(1, "failed in building glob pattern"); + xasprintf(&glob_pattern, "%s/*.playlist", dirname); /* get the files */ + fcount = 0; globbed = glob(glob_pattern, 0, NULL, &files); if (globbed != 0 && globbed != GLOB_NOMATCH && errno != 0) - err(1, "failed to glob playlists directory"); + fatal("failed to glob playlists directory"); + if (files.gl_pathc == 0) + goto out; /* allocate & copy each of the filenames found into the filenames array */ - if ((*fnames = calloc(files.gl_pathc, sizeof(char*))) == NULL) - err(1, "failed to allocate playlist filenames array"); + *fnames = xcalloc(files.gl_pathc, sizeof(char*)); - for (fcount = 0; fcount < files.gl_pathc; fcount++) { - if (asprintf(&((*fnames)[fcount]), "%s", files.gl_pathv[fcount]) == -1) - errx(1, "failed to allocate filename for playlist"); - } + for (fcount = 0; fcount < files.gl_pathc; fcount++) + xasprintf(&((*fnames)[fcount]), "%s", files.gl_pathv[fcount]); /* cleanup */ +out: globfree(&files); free(glob_pattern); @@ -381,15 +375,10 @@ playlist_changeset* changeset_create(short type, size_t size, meta_info **files, int loc) { size_t i; - playlist_changeset *c; - if ((c = malloc(sizeof(playlist_changeset))) == NULL) - err(1, "%s: malloc(3) failed", __FUNCTION__); - - if ((c->files = calloc(size, sizeof(meta_info*))) == NULL) - err(1, "%s: calloc(3) failed", __FUNCTION__); - + c = xmalloc(sizeof(playlist_changeset)); + c->files = xcalloc(size, sizeof(meta_info*)); c->type = type; c->size = size; c->location = loc; @@ -410,16 +399,7 @@ changeset_free(playlist_changeset *c) playlist_changeset** playlist_history_new(void) { - playlist_changeset **h; - int i; - - if ((h = calloc(history_size, sizeof(playlist_changeset*))) == NULL) - err(1, "%s: calloc(3) failed", __FUNCTION__); - - for (i = 0; i < history_size; i++) - h[i] = NULL; - - return h; + return xcalloc(history_size, sizeof(playlist_changeset*)); } void @@ -479,7 +459,7 @@ playlist_undo(playlist *p) playlist_files_add(p, c->files, c->location, c->size, false); break; default: - errx(1, "%s: invalid change type", __FUNCTION__); + fatalx("%s: invalid change type", __FUNCTION__); } p->hist_present--; @@ -506,7 +486,7 @@ playlist_redo(playlist *p) playlist_files_remove(p, c->location, c->size, false); break; default: - errx(1, "%s: invalid change type", __FUNCTION__); + fatalx("%s: invalid change type", __FUNCTION__); } p->hist_present++; diff --git a/playlist.h b/playlist.h index 60c06a0..a6d98f5 100644 --- a/playlist.h +++ b/playlist.h @@ -17,21 +17,6 @@ #ifndef PLAYLIST_H #define PLAYLIST_H -#include "compat.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" #include "meta_info.h" #define PLAYLIST_CHUNK_SIZE 100 diff --git a/socket.c b/socket.c index af4dcfb..8cacd5a 100644 --- a/socket.c +++ b/socket.c @@ -13,19 +13,22 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include + #include #include #include + +#include +#include +#include #include -#include "socket.h" #include "commands.h" +#include "keybindings.h" +#include "socket.h" #define VITUNES_SOCK "/tmp/.vitunes" - int sock_send_msg(const char *msg) { diff --git a/socket.h b/socket.h index 155f3f0..056d2b1 100644 --- a/socket.h +++ b/socket.h @@ -16,8 +16,6 @@ #ifndef SOCKET_H #define SOCKET_H -#include - #define VITUNES_RUNNING "WHOWASPHONE?" /* diff --git a/str2argv.c b/str2argv.c index 5922ffb..dc420a7 100644 --- a/str2argv.c +++ b/str2argv.c @@ -14,19 +14,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include + +#include "compat.h" +#include "error.h" #include "str2argv.h" +#include "xmalloc.h" /* initialize empty argc/argv struct */ void argv_init(int *argc, char ***argv) { - if ((*argv = calloc(ARGV_MAX_ENTRIES, sizeof(char*))) == NULL) - err(1, "argv_init: argv calloc fail"); - - if (((*argv)[0] = calloc(ARGV_MAX_TOKEN_LEN, sizeof(char))) == NULL) - err(1, "argv_init: argv[i] calloc fail"); - - bzero((*argv)[0], ARGV_MAX_TOKEN_LEN * sizeof(char)); + *argv = xcalloc(ARGV_MAX_ENTRIES, sizeof(char*)); + (*argv)[0] = xcalloc(ARGV_MAX_TOKEN_LEN, sizeof(char)); *argc = 0; } @@ -36,7 +39,7 @@ argv_free(int *argc, char ***argv) { int i; - for (i = 0; i <= *argc; i++) + for (i = 0; i < *argc; i++) free((*argv)[i]); free(*argv); @@ -51,26 +54,37 @@ argv_addch(int argc, char **argv, int c) n = strlen(argv[argc]); if (n == ARGV_MAX_TOKEN_LEN - 1) - errx(1, "argv_addch: reached max token length (%d)", ARGV_MAX_TOKEN_LEN); + fatalx("argv_addch: reached max token length (%d)", ARGV_MAX_TOKEN_LEN); argv[argc][n] = c; } +char ** +argv_copy(int argc, char **argv) +{ + char **new_argv; + int i; + + new_argv = xcalloc(argc + 1, sizeof *new_argv); + for (i = 0; i < argc; i++) + new_argv[i] = xstrdup(argv[i]); + new_argv[i] = NULL; + + return new_argv; +} + /* complete the current entry in the argc/argv and setup the next one */ void argv_finish_token(int *argc, char ***argv) { if (*argc == ARGV_MAX_ENTRIES - 1) - errx(1, "argv_finish_token: reached max argv entries(%d)", ARGV_MAX_ENTRIES); + fatalx("argv_finish_token: reached max argv entries(%d)", ARGV_MAX_ENTRIES); if (strlen((*argv)[*argc]) == 0) return; *argc = *argc + 1; - if (((*argv)[*argc] = calloc(ARGV_MAX_TOKEN_LEN, sizeof(char))) == NULL) - err(1, "argv_finish_token: failed to calloc argv[i]"); - - bzero((*argv)[*argc], ARGV_MAX_TOKEN_LEN * sizeof(char)); + (*argv)[*argc] = xcalloc(ARGV_MAX_TOKEN_LEN, sizeof(char)); } /* @@ -240,9 +254,7 @@ argv2str(int argc, char *argv[]) } /* allocate result */ - if ((result = calloc(len, sizeof(char))) == NULL) - err(1, "argv2str: calloc failed"); - bzero(result, len); + result = xcalloc(len, sizeof(char)); /* build result */ off = 0; diff --git a/str2argv.h b/str2argv.h index ab77c19..1db0428 100644 --- a/str2argv.h +++ b/str2argv.h @@ -17,16 +17,6 @@ #ifndef STR2ARGV_H #define STR2ARGV_H -#include "compat.h" - -#include -#include -#include -#include -#include - -#include "debug.h" - /* hard limits on the size of an argv and each entry/token w/in an argv */ #define ARGV_MAX_ENTRIES 255 #define ARGV_MAX_TOKEN_LEN 255 @@ -51,4 +41,9 @@ void argv_free(int *argc, char ***argv); */ char *argv2str(int argc, char *argv[]); +/* + * Duplicates the given argc/argv set of parameters. + */ +char **argv_copy(int argc, char **argv); + #endif diff --git a/uinterface.c b/uinterface.c index eef84e3..24dbb73 100644 --- a/uinterface.c +++ b/uinterface.c @@ -14,7 +14,23 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "enums.h" +#include "error.h" #include "uinterface.h" +#include "xmalloc.h" /* the global user interface object */ uinterface ui; @@ -26,10 +42,9 @@ uinterface ui; swindow* swindow_new(int h, int w, int y, int x) { - swindow *swin = malloc(sizeof(swindow)); - if (swin == NULL) - err(1, "swindow_new failed to allocate swin"); - + swindow *swin; + + swin = xmalloc(sizeof(swindow)); swin->w = w; swin->h = h; swin->voffset = 0; @@ -38,7 +53,7 @@ swindow_new(int h, int w, int y, int x) swin->nrows = 0; swin->cwin = newwin(h, w, y, x); if (swin->cwin == NULL) - errx(1, "swindow_new: failed to create window"); + fatalx("swindow_new: failed to create window"); return swin; } @@ -95,7 +110,7 @@ swindow_scroll(swindow *win, Direction d, int n) break; default: - err(1, "swindow_scroll: bad direction"); + fatal("swindow_scroll: bad direction"); } } @@ -129,7 +144,7 @@ ui_init(int library_width) ui.player = newwin(1, cols, 0, 0); ui.command = newwin(1, cols, lines - 1, 0); if (ui.player == NULL || ui.command == NULL) - errx(1, "ui_init: failed to create player/command windows"); + fatalx("ui_init: failed to create player/command windows"); /* and the rest */ ui.library = swindow_new(lines - 3, ui.lwidth, 2, 0); @@ -172,16 +187,16 @@ ui_resize() /* get new dimensions and check for changes */ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) - err(1, "ui_resize: ioctl failed"); + fatal("ui_resize: ioctl failed"); /* can we even handle the new display size? if not, just exit */ if (ws.ws_col < ui.lwidth + 2) { endwin(); - errx(1, "ui_resize: not enough columns to render vitunes nicely"); + fatalx("ui_resize: not enough columns to render vitunes nicely"); } if (ws.ws_row < 4) { endwin(); - errx(1, "ui_resize: not enough rows to render vitunes nicely"); + fatalx("ui_resize: not enough rows to render vitunes nicely"); } /* resize ncurses */ @@ -234,7 +249,7 @@ ui_unhide_library() /* create library window */ ui.library->cwin = newwin(h - 3, ui.lwidth, 2, 0); if (ui.library->cwin == NULL) - errx(1, "ui_unhide_library: failed to create newwin"); + fatalx("ui_unhide_library: failed to create newwin"); /* resize & move playlist window */ swindow_resize(ui.playlist, h - 3, w - ui.lwidth - 1, 2, ui.lwidth + 1); diff --git a/uinterface.h b/uinterface.h index e28e15a..2fb1560 100644 --- a/uinterface.h +++ b/uinterface.h @@ -17,23 +17,7 @@ #ifndef UINTERFACE_H #define UINTERFACE_H -#include "compat.h" - -#include - -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "enums.h" /* struct & methods for a scrollable window */ typedef struct diff --git a/vitunes.c b/vitunes.c index 7d1abbb..b09bc22 100644 --- a/vitunes.c +++ b/vitunes.c @@ -14,9 +14,33 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "commands.h" +#include "compat.h" +#include "ecmd.h" +#include "enums.h" +#include "error.h" +#include "keybindings.h" +#include "medialib.h" +#include "paint.h" +#include "player.h" +#include "socket.h" +#include "str2argv.h" +#include "uinterface.h" #include "vitunes.h" +#include "xmalloc.h" #include "config.h" /* NOTE: must be after vitunes.h */ -#include "socket.h" /***************************************************************************** * GLOBALS, EXPORTED @@ -43,9 +67,6 @@ volatile sig_atomic_t VSIG_PLAYER_MONITOR = 0; /* 1 = update player stats */ enum { EXIT_NORMAL, BAD_PLAYER }; volatile sig_atomic_t QUIT_CAUSE = EXIT_NORMAL; -/* used with -DDEBUG */ -FILE *debug_log; - /***************************************************************************** * GLOBALS, LOCAL @@ -67,6 +88,9 @@ char *player_backend; /* program name with directories removed */ char *progname; +/* configuration file line number */ +size_t conf_linenum; + /***************************************************************************** * local functions @@ -95,10 +119,8 @@ main(int argc, char *argv[]) else progname++; -#ifdef DEBUG - if ((debug_log = fopen("vitunes-debug.log", "w")) == NULL) - err(1, "failed to open debug log"); -#endif + /* error messages go to stderr before the user interface is set up */ + error_init(ERROR_STDERR); /*------------------------------------------------------------------------ * build paths names needed by vitunes & handle switches @@ -107,21 +129,17 @@ main(int argc, char *argv[]) /* get home dir */ if ((home = getenv("HOME")) == NULL || *home == '\0') { if ((pw = getpwuid(getuid())) == NULL) - errx(1, "Couldn't determine home directory. Can't find my config files."); - home = pw->pw_dir; + home = "/"; + else + home = pw->pw_dir; } /* build paths & other needed strings */ - if (asprintf(&vitunes_dir, VITUNES_DIR_FMT, home) == -1) - err(1, "main: asprintf failed"); - if (asprintf(&conf_file, CONF_FILE_FMT, home) == -1) - err(1, "main: asprintf failed"); - if (asprintf(&db_file, DB_FILE_FMT, home) == -1) - err(1, "main: asprintf failed"); - if (asprintf(&playlist_dir, PLAYLIST_DIR_FMT, home) == -1) - err(1, "main: asprintf failed"); - if (asprintf(&player_backend, "%s", DEFAULT_PLAYER_BACKEND) == -1) - err(1, "main: asprintf failed"); + xasprintf(&vitunes_dir, VITUNES_DIR_FMT, home); + xasprintf(&conf_file, CONF_FILE_FMT, home); + xasprintf(&db_file, DB_FILE_FMT, home); + xasprintf(&playlist_dir, PLAYLIST_DIR_FMT, home); + xasprintf(&player_backend, "%s", DEFAULT_PLAYER_BACKEND); /* handle command-line switches & e-commands */ handle_switches(argc, argv); @@ -130,7 +148,7 @@ main(int argc, char *argv[]) printf("Vitunes appears to be running already. Won't open socket."); } else { if((sock = sock_listen()) == -1) - errx(1, "failed to open socket."); + fatalx("failed to open socket."); } @@ -179,6 +197,9 @@ main(int argc, char *argv[]) /* setup user interface and default colors */ kb_init(); + + /* user interface being initialised; set context, accordingly */ + error_init(ERROR_CFG); ui_init(DEFAULT_LIBRARY_WINDOW_WIDTH); paint_setup_colors(); @@ -193,6 +214,9 @@ main(int argc, char *argv[]) /* initial painting of the display */ paint_all(); + /* configuration file ok; paint messages from now on */ + error_init(ERROR_PAINT); + /* ----------------------------------------------------------------------- * begin input loop * -------------------------------------------------------------------- */ @@ -252,7 +276,7 @@ main(int argc, char *argv[]) if (QUIT_CAUSE != EXIT_NORMAL) { switch (QUIT_CAUSE) { case BAD_PLAYER: - warnx("It appears the media player is misbehaving. Apologies."); + infox("It appears the media player is misbehaving. Apologies."); break; } } @@ -265,9 +289,8 @@ void usage(void) { fprintf(stderr,"\ -usage: %s [-f config-file] [-d database-file] [-p playlist-dir] [-m player-path] [-e COMMAND ...]\n\ -See \"%s -e help\" for information about what e-commands are available.\n\ -", +usage: %s [-v] [-f config-file] [-d database-file] [-p playlist-dir] [-m player-path]\n\ +\t[-e COMMAND ...]\nSee \"%s -e help\" for information about what e-commands are available.\n", progname, progname); exit(1); } @@ -338,7 +361,7 @@ process_signals() paint_playlist(); } if (prev_volume != player.volume()) { - paint_message("volume: %3.0f%%", player.volume()); + infox("volume: %3.0f%%", player.volume()); prev_volume = player.volume(); } @@ -364,12 +387,12 @@ setup_timer() /* create timer signal handler */ if (sigemptyset(&sig_act.sa_mask) < 0) - err(1, "setup_timer: sigemptyset failed"); + fatal("setup_timer: sigemptyset failed"); sig_act.sa_flags = 0; sig_act.sa_handler = signal_handler; if (sigaction(SIGALRM, &sig_act, NULL) < 0) - err(1, "setup_timer: sigaction failed"); + fatal("setup_timer: sigaction failed"); /* setup timer details */ timer.it_value.tv_sec = 0; @@ -379,7 +402,7 @@ setup_timer() /* register timer */ if (setitimer(ITIMER_REAL, &timer, NULL) < 0) - err(1, "setup_timer: setitimer failed"); + fatal("setup_timer: setitimer failed"); } /* @@ -389,32 +412,16 @@ setup_timer() void load_config() { - const char *errmsg = NULL; - size_t length, linenum; + size_t length; FILE *fin; char *line; char *copy; - char **argv; - int argc; - bool found; - int found_idx = 0; - int num_matches; - int i, ret; if ((fin = fopen(conf_file, "r")) == NULL) return; - linenum = 0; - while (!feof(fin)) { - - /* get next line */ - if ((line = fparseln(fin, &length, &linenum, NULL, 0)) == NULL) { - if (ferror(fin)) - err(1, "error reading config file '%s'", conf_file); - else - break; - } - + /* get next line */ + while ((line = fparseln(fin, &length, &conf_linenum, NULL, 0)) != NULL) { /* skip whitespace */ copy = line; copy += strspn(copy, " \t\n"); @@ -423,42 +430,11 @@ load_config() continue; } - /* parse line into argc/argv */ - if (str2argv(copy, &argc, &argv, &errmsg) != 0) { - endwin(); - errx(1, "%s line %zd: parse error: %s", conf_file, linenum, errmsg); - } - - /* run command */ - found = false; - num_matches = 0; - for (i = 0; i < CommandPathSize; i++) { - if (match_command_name(argv[0], CommandPath[i].name)) { - found = true; - found_idx = i; - num_matches++; - } - } - - if (found && num_matches == 1) { - if ((ret = (CommandPath[found_idx].func)(argc, argv)) != 0) { - endwin(); - errx(1, "%s line %zd: error with command '%s' [%i]", - conf_file, linenum, argv[0], ret); - } - } else if (num_matches > 1) { - endwin(); - errx(1, "%s line %zd: ambiguous abbreviation '%s'", - conf_file, linenum, argv[0]); - } else { - endwin(); - errx(1, "%s line %zd: unknown command '%s'", - conf_file, linenum, argv[0]); - } - - argv_free(&argc, &argv); + cmd_execute(line); free(line); } + if (ferror(fin)) + fatal("error reading config file"); fclose(fin); } @@ -472,17 +448,16 @@ handle_switches(int argc, char *argv[]) { int ch; - while ((ch = getopt(argc, argv, "he:f:d:p:m:c:")) != -1) { + while ((ch = getopt(argc, argv, "he:f:d:p:m:c:v")) != -1) { switch (ch) { case 'c': if(sock_send_msg(optarg) == -1) - errx(1, "Failed to send message. Vitunes not running?"); + fatalx("Failed to send message. Vitunes not running?"); exit(0); case 'd': free(db_file); - if ((db_file = strdup(optarg)) == NULL) - err(1, "handle_switches: strdup db_file failed"); + db_file = xstrdup(optarg); break; case 'e': @@ -494,20 +469,21 @@ handle_switches(int argc, char *argv[]) case 'f': free(conf_file); - if ((conf_file = strdup(optarg)) == NULL) - err(1, "handle_switches: strdup conf_file failed"); + conf_file = xstrdup(optarg); break; case 'm': free(player_backend); - if ((player_backend = strdup(optarg)) == NULL) - err(1, "handle_switches: strdup player_backend failed"); + player_backend = xstrdup(optarg); break; case 'p': free(playlist_dir); - if ((playlist_dir = strdup(optarg)) == NULL) - err(1, "handle_switches: strdup playlist_dir failed"); + playlist_dir = xstrdup(optarg); + break; + + case 'v': + error_open(); break; case 'h': diff --git a/vitunes.h b/vitunes.h index 8348da0..0552e18 100644 --- a/vitunes.h +++ b/vitunes.h @@ -17,26 +17,9 @@ #ifndef VITUNES_H #define VITUNES_H -#include "compat.h" - -#include - -#include -#include -#include #include -#include -#include -#include -#include "debug.h" -#include "enums.h" -#include "commands.h" -#include "keybindings.h" -#include "medialib.h" -#include "player.h" -#include "uinterface.h" -#include "ecmd.h" +#include "playlist.h" /* for unused arguments */ #if defined(__GNUC__) || defined(__clang__) @@ -51,10 +34,12 @@ /* configurable paths */ extern char *vitunes_dir; +extern char *conf_file; extern char *playlist_dir; extern char *db_file; extern char *progname; +size_t conf_linenum; /* record keeping */ extern playlist *viewing_playlist; diff --git a/xmalloc.c b/xmalloc.c new file mode 100644 index 0000000..2cf48ed --- /dev/null +++ b/xmalloc.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2013 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "xmalloc.h" + +void +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (vasprintf(ret, fmt, ap) == -1 || *ret == NULL) + diex("vasprintf"); + va_end(ap); +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (nmemb == 0) + diex("xcalloc: zero objects"); + if (size == 0) + diex("xcalloc: zero size"); + if (nmemb > SIZE_MAX / size) { + errno = ENOMEM; + die("xcalloc: overflow"); + } + if ((ptr = calloc(nmemb, size)) == NULL) + die("calloc"); + + return (ptr); +} + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + diex("xmalloc: zero size"); + if ((ptr = malloc(size)) == NULL) + die("malloc"); + + return (ptr); +} + +void * +xrealloc(void *ptr, size_t nmemb, size_t size) +{ + void *newptr; + + if (nmemb == 0) + diex("xrealloc: zero objects"); + if (size == 0) + diex("xrealloc: zero size"); + if (nmemb > SIZE_MAX / size) { + errno = ENOMEM; + die("xrealloc: overflow"); + } + if ((newptr = realloc(ptr, nmemb * size)) == NULL) + die("realloc"); + + return (newptr); +} + +char * +xstrdup(const char *s) +{ + char *ptr; + + if (s == NULL) + diex("xstrdup: null pointer"); + if ((ptr = strdup(s)) == NULL) + die("strdup"); + + return (ptr); +} diff --git a/debug.h b/xmalloc.h similarity index 51% rename from debug.h rename to xmalloc.h index 45a9cd8..cd41be4 100644 --- a/debug.h +++ b/xmalloc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011 Ryan Flannery + * Copyright (c) 2013 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,37 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef DEBUG_H -#define DEBUG_H +#ifndef XMALLOC_H +#define XMALLOC_H +#include #include -/* log file for debugging */ -extern FILE *debug_log; - -#ifdef DEBUG - -/* debug file logger that goes to the file opened in vitunes.c */ -#define DFLOG(format, args...) \ - fprintf(debug_log, "%s.%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \ - fprintf(debug_log, format, ## args); \ - fprintf(debug_log, "\n"); \ - fflush(debug_log); - -/* console logger. goes to stdout. doesn't work well in curses */ -#define DCLOG(format, args...) \ - printf("%d: ", __LINE__); \ - printf(format, ## args); \ - printf("\n"); \ - fflush(stdout); - - -#else - -#define DFLOG(format, args...) 0; -#define DCLOG(format, args...) 0; - -#endif - +void xasprintf(char **, const char *, ...); +void *xcalloc(size_t, size_t); +void *xmalloc(size_t); +void *xrealloc(void *, size_t, size_t); +char *xstrdup(const char *); #endif