tmus

TUI Music Player
git clone https://git.sinitax.com/sinitax/tmus
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

commit 9fe644f0d99375ffd3011d8828f7dbd0fb103af0
parent d68d0f39ab7675745e7d177f6774736f7ec58783
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat,  4 Dec 2021 13:48:01 +0100

Added more gui interaction

Diffstat:
M.gitignore | 1+
MMakefile | 6+++---
Mhistory.c | 6+++++-
Mlink.c | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mlink.h | 15+++++++++++----
Mmain.c | 406++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Aplayer.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aplayer.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atag.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Atag.h | 22++++++++++++++++++++++
Atrack.c | 37+++++++++++++++++++++++++++++++++++++
Atrack.h | 25+++++++++++++++++++++++++
Mutil.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Mutil.h | 4++++
14 files changed, 759 insertions(+), 93 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ vgcore* main +env.sh *.o diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -I . -LDLIBS = -lcurses -lreadline +CFLAGS = -I . -g +LDLIBS = -lcurses -lreadline -lportaudio -lsndfile .PHONY: all main @@ -11,5 +11,5 @@ clean: %.o: %.c %.h $(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS) -main: main.c util.o history.o link.o +main: main.c util.o history.o link.o player.o tag.o track.o $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS) diff --git a/history.c b/history.c @@ -103,7 +103,8 @@ history_add(struct history *history, struct inputln *line) struct link *back; if (list_len(&history->list) == HISTORY_MAX) { - back = list_back(&history->list); + /* pop last item to make space */ + back = link_back(&history->list); back->prev->next = NULL; ln = UPCAST(back, struct inputln); inputln_free(ln); @@ -163,6 +164,7 @@ inputln_addch(struct inputln *line, wchar_t c) for (i = line->len; i > line->cur; i--) line->buf[i] = line->buf[i-1]; line->buf[line->cur] = c; + line->len++; line->cur++; } @@ -177,8 +179,10 @@ inputln_del(struct inputln *line, int n) n = MIN(n, line->cur); for (i = line->cur; i <= line->len; i++) line->buf[i-n] = line->buf[i]; + for (i = line->len - n; i <= line->len; i++) line->buf[i] = 0; + line->len -= n; line->cur -= n; } diff --git a/link.c b/link.c @@ -1,30 +1,50 @@ #include "link.h" +#include "util.h" int -list_len(struct link *list) +list_len(struct link *head) { struct link *iter; int len; + ASSERT(head != NULL); + len = 0; - for (iter = list; iter; iter = iter->next) + for (iter = head->next; iter; iter = iter->next) len += 1; return len; } +int +list_ffind(struct link *head, struct link *link) +{ + struct link *iter; + + ASSERT(head != NULL); + + for (iter = head->next; iter && iter != link; iter = iter->next); + + return (iter == link); +} + struct link * -list_back(struct link *link) +link_back(struct link *link) { + ASSERT(link != NULL); + for (; link->next; link = link->next); + return link; } void -link_prepend(struct link *list, struct link *link) +link_prepend(struct link *cur, struct link *link) { - link->prev = list->prev; - link->next = list; + ASSERT(cur != NULL && link != NULL); + + link->prev = cur->prev; + link->next = cur; if (link->prev) link->prev->next = link; @@ -33,10 +53,12 @@ link_prepend(struct link *list, struct link *link) } void -link_append(struct link *list, struct link *link) +link_append(struct link *cur, struct link *link) { - link->prev = list; - link->next = list->next; + ASSERT(cur != NULL && link != NULL); + + link->prev = cur; + link->next = cur->next; if (link->prev) link->prev->next = link; @@ -47,8 +69,32 @@ link_append(struct link *list, struct link *link) void link_pop(struct link *link) { + ASSERT(link != NULL); + if (link->prev) link->prev->next = link->next; if (link->next) link->next->prev = link->prev; } + +struct link * +link_iter(struct link *link, int n) +{ + int i; + + for (i = 0; i < n; i++) { + if (!link) return NULL; + link = link->next; + } + + return link; +} + +void +link_push_back(struct link *cur, struct link *link) +{ + struct link *back; + + back = link_back(cur); + link_append(back, link); +} diff --git a/link.h b/link.h @@ -8,16 +8,23 @@ (type *)( (char *)__mptr - OFFSET(type, link) ); }) #define LIST_HEAD ((struct link) { .prev = NULL, .next = NULL }) +#define LINK_EMPTY ((struct link) { 0 }) struct link { - struct link *prev, *next; + struct link *prev; + struct link *next; }; -int list_len(struct link *list); -struct link* list_back(struct link *list); +/* list_XXX functions operate on the list head */ -void link_push_back(struct link *list, struct link *link); +int list_len(struct link *head); +int list_ffind(struct link *head, struct link *link); +struct link *link_back(struct link *list); void link_prepend(struct link *list, struct link *link); void link_append(struct link *list, struct link *link); void link_pop(struct link *link); + +struct link *link_iter(struct link *link, int n); + +void link_push_back(struct link *list, struct link *link); diff --git a/main.c b/main.c @@ -1,39 +1,52 @@ #define _XOPEN_SOURCE 600 +#define _DEFAULT_SOURCE #include "util.h" #include "link.h" #include "history.h" +#include "tag.h" +#include "track.h" +#include "player.h" +#include "sndfile.h" +#include "portaudio.h" #include "curses.h" +#include <dirent.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> +#include <sys/wait.h> #include <time.h> #include <unistd.h> #include <wchar.h> #include <wctype.h> - -#define ATTR_ON(win, attr) wattr_on((win), (attr), NULL) -#define ATTR_OFF(win, attr) wattr_off((win), (attr), NULL) -#define STYLE_ON(win, style) ATTR_ON((win), COLOR_PAIR(STYLE_ ## style)) -#define STYLE_OFF(win, style) ATTR_OFF((win), COLOR_PAIR(STYLE_ ## style)) - +#undef KEY_ENTER +#define KEY_ENTER '\n' +#define KEY_SPACE ' ' #define KEY_ESC '\x1b' #define KEY_TAB '\t' #define KEY_CTRL(c) ((c) & ~0x60) +#define ATTR_ON(win, attr) wattr_on(win, attr, NULL) +#define ATTR_OFF(win, attr) wattr_off(win, attr, NULL) enum { STYLE_DEFAULT, STYLE_TITLE, - STYLE_PANE_SEP + STYLE_PANE_SEP, + STYLE_ITEM_SEL, + STYLE_ITEM_HOVER, + STYLE_ITEM_HOVER_SEL, + STYLE_COUNT }; enum { - EXECUTE, - SEARCH + IMODE_EXECUTE, + IMODE_SEARCH }; struct pane; @@ -53,33 +66,15 @@ struct pane { pane_updater update; }; -struct track { - char *title; - char *artist; - float duration; - struct link *tags; - - struct link link; -}; - -struct track_ref { - struct track *track; - - struct link link; -}; - -struct tag { - const char *name; - - struct link link; -}; - struct cmd { const char *name; cmd_handler func; }; +int style_attrs[STYLE_COUNT] = { 0 }; + +const char *datadir; int scrw, scrh; int quit; @@ -92,8 +87,10 @@ struct pane *const panes[] = { &pane_bot }; +struct link tags; + struct track *track_sel; -struct track *tracks; +struct link tracks; struct link playlist; int track_paused; @@ -102,10 +99,21 @@ struct history search_history, command_history; struct history *history; int cmd_show, cmd_mode; +int tag_index; +struct link tags_sel; + +int track_index; + void init(void); void cleanup(void); +void data_load(void); +void tracks_load(struct tag *tag); + +void data_save(void); +void tracks_save(struct tag *tag); + void resize(void); void pane_resize(struct pane *pane, int sx, int sy, int ex, int ey); @@ -113,21 +121,32 @@ void pane_resize(struct pane *pane, void pane_init(struct pane *p, pane_handler handle, pane_updater update); void pane_title(struct pane *pane, const char *title, int highlight); +void style_init(int style, int fg, int bg, int attr); +void style_on(WINDOW *win, int style); +void style_off(WINDOW *win, int style); + char *command_name_generator(const char *text, int state); char *track_name_generator(const char *text, int state); -int track_input(wint_t c); -void track_vis(struct pane *pane, int sel); - int tag_input(wint_t c); void tag_vis(struct pane *pane, int sel); +int track_input(wint_t c); +void track_vis(struct pane *pane, int sel); + int cmd_input(wint_t c); void cmd_vis(struct pane *pane, int sel); void main_input(wint_t c); void main_vis(void); +int usercmd_save(const char *args); + + +const struct cmd cmds[] = { + { "save", usercmd_save }, +}; + void init(void) @@ -138,6 +157,7 @@ init(void) initscr(); cbreak(); noecho(); + halfdelay(1); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); start_color(); @@ -148,8 +168,12 @@ init(void) history_init(&search_history); history_init(&command_history); - init_pair(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE); - init_pair(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK); + style_init(STYLE_DEFAULT, COLOR_WHITE, COLOR_BLACK, 0); + style_init(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE, A_BOLD); + style_init(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK, 0); + style_init(STYLE_ITEM_SEL, COLOR_YELLOW, COLOR_BLACK, A_BOLD); + style_init(STYLE_ITEM_HOVER, COLOR_WHITE, COLOR_BLUE, 0); + style_init(STYLE_ITEM_HOVER_SEL, COLOR_YELLOW, COLOR_BLUE, A_BOLD); pane_init(&pane_left, tag_input, tag_vis); pane_init(&pane_right, track_input, track_vis); @@ -157,12 +181,34 @@ init(void) pane_sel = &pane_left; + datadir = getenv("TMUS_DATA"); + ASSERT(datadir != NULL); + + track_index = 0; + tag_index = 0; + tags_sel = LIST_HEAD; + + // player = player_thread(); + + data_load(); + atexit(cleanup); } void cleanup(void) { + int status; + + // TODO stop player + + data_save(); + + kill(player->pid, SIGTERM); + waitpid(player->pid, &status, 0); + + // TODO free player + history_free(&search_history); history_free(&command_history); @@ -173,6 +219,83 @@ cleanup(void) } void +data_load(void) +{ + struct dirent *ent; + struct tag *tag; + DIR *dir; + + tags = LIST_HEAD; + + dir = opendir(datadir); + ASSERT(dir != NULL); + while ((ent = readdir(dir))) { + if (ent->d_type != DT_DIR) + continue; + if (!strcmp(ent->d_name, ".")) + continue; + if (!strcmp(ent->d_name, "..")) + continue; + + tag = malloc(sizeof(struct tag)); + ASSERT(tag != NULL); + tag->fname = strdup(ent->d_name); + ASSERT(tag->fname != NULL); + tag->fpath = aprintf("%s/%s", datadir, ent->d_name); + ASSERT(tag->fpath != NULL); + tag->name = sanitized(tag->fname); + ASSERT(tag->name != NULL); + tag->link = LINK_EMPTY; + link_push_back(&tags, &tag->link); + + tracks_load(tag); + } + closedir(dir); +} + +void +tracks_load(struct tag *tag) +{ + struct dirent *ent; + struct track *track; + struct tag_ref *tagref; + DIR *dir; + + dir = opendir(tag->fpath); + ASSERT(dir != NULL); + while ((ent = readdir(dir))) { + if (ent->d_type != DT_REG) + continue; + if (!strcmp(ent->d_name, ".")) + continue; + if (!strcmp(ent->d_name, "..")) + continue; + + track = track_init(tag->fpath, ent->d_name); + tagref = malloc(sizeof(struct tag_ref)); + ASSERT(tagref != NULL); + tagref->tag = tag; + tagref->link = LINK_EMPTY; + link_push_back(&track->tags, &tagref->link); + + link_push_back(&tracks, &track->link); + } + closedir(dir); +} + +void +data_save(void) +{ + +} + +void +tracks_save(struct tag *tag) +{ + +} + +void resize(void) { int i; @@ -223,91 +346,210 @@ pane_title(struct pane *pane, const char *title, int highlight) { wmove(pane->win, 0, 0); - STYLE_ON(pane->win, TITLE); - ATTR_ON(pane->win, A_BOLD); + style_on(pane->win, STYLE_TITLE); if (highlight) ATTR_ON(pane->win, A_STANDOUT); wprintw(pane->win, " %-*.*s", pane->w - 1, pane->w - 1, title); if (highlight) ATTR_OFF(pane->win, A_STANDOUT); - ATTR_OFF(pane->win, A_BOLD); - STYLE_OFF(pane->win, TITLE); + style_off(pane->win, STYLE_TITLE); } -char * -command_name_generator(const char *text, int reset) +void +style_init(int style, int fg, int bg, int attr) { - return NULL; + style_attrs[style] = attr; + init_pair(style, fg, bg); +} + +void +style_on(WINDOW *win, int style) +{ + ATTR_ON(win, COLOR_PAIR(style) | style_attrs[style]); +} + +void +style_off(WINDOW *win, int style) +{ + ATTR_OFF(win, COLOR_PAIR(style) | style_attrs[style]); } char * -track_name_generator(const char *text, int reset) +command_name_generator(const char *text, int reset) { static int index, len; - char *name; - - if (cmd_mode != SEARCH) return NULL; if (reset) { index = 0; len = strlen(text); } - if (index++ == 0 && !strncmp("hello", text, len)) - return strdup("hello"); - - // TODO: iter over playlist - // while ((name = track_names[list_index++])) { - // if (strncmp(name, text, len) == 0) { - // return strdup(name); - // } - // } + for (; index < ARRLEN(cmds); index++) { + if (!strncmp(cmds[index].name, text, len)) + return strdup(cmds[index].name); + } return NULL; } -void -tag_init(void) +char * +track_name_generator(const char *text, int reset) { - /* TODO: load tags as directory names (excluding unsorted) */ + static struct link *cur; + struct track *track; + static int len; + + if (reset) { + cur = tracks.next; + len = strlen(text); + } + + for (; cur; cur = cur->next) { + track = UPCAST(cur, struct track); + if (!strncmp(track->name, text, len)) + return strdup(track->name); + } + + return NULL; } int tag_input(wint_t c) { + struct link *cur; + struct tag *tag; + + switch (c) { + case KEY_UP: + tag_index = MAX(0, tag_index - 1); + return 1; + case KEY_DOWN: + tag_index = MIN(list_len(&tags) - 1, + tag_index + 1); + return 1; + case KEY_SPACE: + cur = link_iter(tags.next, tag_index); + ASSERT(cur != NULL); + tag = UPCAST(cur, struct tag); + if (tagrefs_incl(&tags_sel, tag)) { + tagrefs_rm(&tags_sel, tag); + } else { + tagrefs_add(&tags_sel, tag); + } + return 1; + } + return 0; } void tag_vis(struct pane *pane, int sel) { + struct tag *tag, *tag2; + struct link *iter, *iter2; + int index, tsel; + werase(pane->win); pane_title(pane, "Tags", sel); -} -void -track_init(void) -{ - /* TODO: for each tag director load track names */ + index = 0; + for (iter = tags.next; iter; iter = iter->next) { + tag = UPCAST(iter, struct tag); + tsel = tagrefs_incl(&tags_sel, tag); + + if (sel && index == tag_index && tsel) + style_on(pane->win, STYLE_ITEM_HOVER_SEL); + else if (sel && index == tag_index) + style_on(pane->win, STYLE_ITEM_HOVER); + else if (tsel) + style_on(pane->win, STYLE_ITEM_SEL); + + wmove(pane->win, 1 + index, 0); + wprintw(pane->win, "%*.*s", pane->w, pane->w, tag->name); + + if (index == tag_index && tsel) + style_off(pane->win, STYLE_ITEM_HOVER_SEL); + else if (index == tag_index) + style_off(pane->win, STYLE_ITEM_HOVER); + else if (tsel) + style_off(pane->win, STYLE_ITEM_SEL); + index++; + } } int track_input(wint_t c) { + struct link *link; + struct track *track; + + switch (c) { + case KEY_UP: + track_index = MAX(0, track_index - 1); + return 1; + case KEY_DOWN: + track_index = MIN(list_len(&tracks) - 1, + track_index + 1); + return 1; + case KEY_ENTER: + link = link_iter(tracks.next, track_index); + ASSERT(link != NULL); + track = UPCAST(link, struct track); + if (track != track_sel) { + track_sel = track; + track_paused = 0; + } else { + track_paused ^= 1; + } + // if (!track_paused) + // track_play(); + // else + // track_pause(); + return 1; + } + return 0; } void track_vis(struct pane *pane, int sel) { + struct track *track; + struct link *iter; + int index; + werase(pane->win); pane_title(pane, "Tracks", sel); + + index = 0; + for (iter = tracks.next; iter; iter = iter->next) { + track = UPCAST(iter, struct track); + + if (sel && index == track_index && track == track_sel) + style_on(pane->win, STYLE_ITEM_HOVER_SEL); + else if (sel && index == track_index) + style_on(pane->win, STYLE_ITEM_HOVER); + else if (track == track_sel) + style_on(pane->win, STYLE_ITEM_SEL); + + wmove(pane->win, 1 + index, 0); + wprintw(pane->win, "%-*.*s", pane->w, pane->w, track->name); + + if (sel && index == track_index && track == track_sel) + style_off(pane->win, STYLE_ITEM_HOVER_SEL); + else if (sel && index == track_index) + style_off(pane->win, STYLE_ITEM_HOVER); + else if (track == track_sel) + style_off(pane->win, STYLE_ITEM_SEL); + + index++; + } } int cmd_input(wint_t c) { - if (cmd_mode == EXECUTE) { + if (cmd_mode == IMODE_EXECUTE) { history = &command_history; completion = command_name_generator; } else { @@ -337,9 +579,10 @@ cmd_input(wint_t c) case KEY_DOWN: history_prev(history); break; - case '\n': case KEY_ENTER: history_submit(history); + if (!*history->cmd->buf) + pane_sel = NULL; break; case KEY_TAB: break; @@ -362,13 +605,17 @@ cmd_vis(struct pane *pane, int sel) werase(pane->win); if (track_sel) - pane_title(pane, track_sel->title, track_paused); + pane_title(pane, track_sel->name, sel); else - pane_title(pane, "", 0); + pane_title(pane, "", sel); + + static int i = 0; + wmove(pane->win, 1, 0); + wprintw(pane->win, "%i", i++); if (sel || cmd_show) { wmove(pane->win, 2, 0); - waddch(pane->win, cmd_mode == SEARCH ? '/' : ':'); + waddch(pane->win, cmd_mode == IMODE_SEARCH ? '/' : ':'); cmd = history->cmd; wprintw(pane->win, "%-*.*ls", pane->w - 1, pane->w - 1, cmd->buf); // TODO: if query != cmd, highlight query substr @@ -401,11 +648,11 @@ main_input(wint_t c) pane_sel = &pane_right; break; case ':': - cmd_mode = EXECUTE; + cmd_mode = IMODE_EXECUTE; pane_sel = &pane_bot; break; case '/': - cmd_mode = SEARCH; + cmd_mode = IMODE_SEARCH; pane_sel = &pane_bot; break; } @@ -416,17 +663,24 @@ main_vis(void) { int i; - STYLE_ON(stdscr, TITLE); + style_on(stdscr, STYLE_TITLE); move(0, pane_left.ex); addch(' '); - STYLE_OFF(stdscr, TITLE); + style_off(stdscr, STYLE_TITLE); - STYLE_ON(stdscr, PANE_SEP); + style_on(stdscr, STYLE_PANE_SEP); for (i = pane_left.sy + 1; i < pane_left.ey; i++) { move(i, pane_left.ex); addch(ACS_VLINE); } - STYLE_OFF(stdscr, PANE_SEP); + style_off(stdscr, STYLE_PANE_SEP); +} + +int +usercmd_save(const char *args) +{ + data_save(); + return 1; } int @@ -441,7 +695,7 @@ main(int argc, const char **argv) do { if (c == KEY_RESIZE) { resize(); - } else { + } else if (c != ERR) { handled = 0; if (pane_sel && pane_sel->active) handled = pane_sel->handle(c); diff --git a/player.c b/player.c @@ -0,0 +1,113 @@ +#include "player.h" + +#include "portaudio.h" +#include "sndfile.h" + +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> + +static struct player player_static; +struct player *player; + +struct player * +player_thread() +{ + struct player *player; + pid_t pid; + + player = mmap(&player_static, sizeof(struct player), + PROT_READ | PROT_WRITE, MAP_SHARED, -1, 0); + player->action = PLAYER_NONE; + player->resp = PLAYER_NOTSET; + player->alive = 1; + + pid = fork(); + if (!pid) { + player_main(); + player->alive = 0; + exit(0); + } + + player->pid = pid; + + return player; +} + +void +player_main(void) +{ + PaStream *stream; + int status; + + if (Pa_Initialize() != paNoError) + return; + + while (player->action != PLAYER_EXIT) { + player->resp = PLAYER_NOTSET; + switch (player->action) { + case PLAYER_PLAY: + player->resp = player_play(); + break; + case PLAYER_PAUSE: + player->resp = player_pause(); + break; + case PLAYER_SKIP: + player->resp = player_skip(); + break; + case PLAYER_PREV: + player->resp = player_prev(); + break; + } + Pa_Sleep(100); + } + + Pa_Terminate(); +} + +int +player_alive(void) +{ + return player->alive && !kill(player->pid, 0); +} + +void +player_loadfile(const char *file) +{ + ASSERT(player_alive()); + player_action(PLAYER_STOP); +} + +void +player_action(int action) +{ + ASSERT(player_alive()); + player->action = action; +} + +int +player_play(void) +{ + return PLAYER_OK; +} + +int +player_pause(void) +{ + return PLAYER_OK; +} + +int +player_skip(void) +{ + return PLAYER_OK; +} + +int +player_prev(void) +{ + return PLAYER_OK; +} + + + diff --git a/player.h b/player.h @@ -0,0 +1,54 @@ +#pragma once + +#include "util.h" + +#include "sndfile.h" + +#include <signal.h> + +enum { + PLAYER_NONE, + PLAYER_PAUSE, + PLAYER_PLAY, + PLAYER_SKIP, + PLAYER_PREV, + PLAYER_STOP, + PLAYER_LOAD, + PLAYER_EXIT +}; + +enum { + PLAYER_NOTSET, + PLAYER_OK, + PLAYER_FAIL +}; + +struct player { + int action, resp; + + int reload; + char *filepath; + SNDFILE *file; + SF_INFO info; + + int sample_index; + + int alive; + pid_t pid; +}; + +struct player *player_thread(void); + +void player_main(void); + +int player_alive(void); + +void player_loadfile(const char *path); +void player_action(int action); + +int player_pause(void); +int player_play(void); +int player_prev(void); +int player_skip(void); + +extern struct player *player; diff --git a/tag.c b/tag.c @@ -0,0 +1,53 @@ +#include "tag.h" +#include "link.h" + +static struct link * +tagrefs_ffind(struct link *head, struct tag *tag) +{ + struct link *iter; + + for (iter = head->next; iter; iter = iter->next) { + if (UPCAST(iter, struct tag_ref)->tag == tag) + return iter; + } + + return NULL; +} + +int +tagrefs_incl(struct link *head, struct tag *tag) +{ + struct link *ref; + + ref = tagrefs_ffind(head, tag); + return ref != NULL; +} + +void +tagrefs_add(struct link *head, struct tag *tag) +{ + struct tag_ref *ref; + + if (tagrefs_incl(head, tag)) + return; + + ref = malloc(sizeof(struct tag_ref)); + ASSERT(ref != NULL); + ref->link = LINK_EMPTY; + ref->tag = tag; + link_push_back(head, &ref->link); +} + +void +tagrefs_rm(struct link *head, struct tag *tag) +{ + struct link *ref; + struct tag_ref *tagref; + + ref = tagrefs_ffind(head, tag); + if (!ref) return; + + tagref = UPCAST(ref, struct tag_ref); + link_pop(ref); + free(tagref); +} diff --git a/tag.h b/tag.h @@ -0,0 +1,22 @@ +#pragma once + +#include "link.h" +#include "util.h" + +struct tag { + char *name; + char *fname, *fpath; + + struct link link; +}; + +struct tag_ref { + struct tag *tag; + + struct link link; +}; + +int tagrefs_incl(struct link *head, struct tag *tag); +void tagrefs_add(struct link *head, struct tag *tag); +void tagrefs_rm(struct link *head, struct tag *tag); + diff --git a/track.c b/track.c @@ -0,0 +1,37 @@ +#include "track.h" + +#include <string.h> + + +struct track * +track_init(const char *dir, const char *file) +{ + struct track *track; + + track = malloc(sizeof(struct track)); + + ASSERT(track != NULL); + track->fname = strdup(file); + ASSERT(track->fname != NULL); + track->fpath = aprintf("%s/%s", dir, file); + ASSERT(track->fpath != NULL); + track->name = sanitized(track->fname); + ASSERT(track->name != NULL); + + // TODO track_load_info(track) + track->artist = NULL; + track->duration = 0; + track->link = LINK_EMPTY; + + track->tags = LIST_HEAD; + + return track; +} + +void +track_free(struct track *t) +{ + free(t->fname); + free(t->fpath); + free(t->name); +} diff --git a/track.h b/track.h @@ -0,0 +1,25 @@ +#pragma once + +#include "link.h" +#include "util.h" + + +struct track { + char *name; + char *artist; + float duration; + struct link tags; + char *fname, *fpath; + + struct link link; +}; + +struct track_ref { + struct track *track; + + struct link link; +}; + + +struct track *track_init(const char *dir, const char *file); +void track_free(struct track *t); diff --git a/util.c b/util.c @@ -2,11 +2,15 @@ #include "util.h" +#include <stdarg.h> #include <stdlib.h> #include <string.h> #include <wchar.h> #include <wctype.h> +static const char *allowed = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;-_(){}[]"; + int strnwidth(const char *s, int n) { @@ -41,3 +45,45 @@ assert(int cond, const char *file, int line, const char *condstr) fprintf(stderr, "Assertion failed %s:%i (%s)", file, line, condstr); exit(1); } + +char * +sanitized(const char *instr) +{ + const char *p; + char *clean; + int i; + + clean = strdup(instr); + ASSERT(clean != NULL); + for (i = 0, p = instr; *p; p++) { + if (strchr(allowed, *p)) + clean[i++] = *p; + } + ASSERT(i != 0); + clean[i] = '\0'; + + return clean; +} + +char * +aprintf(const char *fmtstr, ...) +{ + va_list ap, cpy; + size_t size; + char *str; + + va_copy(cpy, ap); + + va_start(ap, fmtstr); + size = vsnprintf(NULL, 0, fmtstr, ap); + va_end(ap); + + str = malloc(size + 1); + ASSERT(str != NULL); + + va_start(cpy, fmtstr); + vsnprintf(str, size + 1, fmtstr, cpy); + va_end(cpy); + + return str; +} diff --git a/util.h b/util.h @@ -10,3 +10,7 @@ int strnwidth(const char *s, int n); void assert(int cond, const char *file, int line, const char *condstr); + +char *sanitized(const char *instr); + +char *aprintf(const char *fmtstr, ...);