tmus

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

commit 7902429e5d69bb6aa57c6f1411b163d3e037e0e1
parent fb0a1cabe8d61d3305d9d14acc4754a1b6151b8c
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed, 29 Dec 2021 01:46:08 +0100

More refactoring, moved track manipulation into player

Planning on creating a modular player_backend component

Diffstat:
Msrc/main.c | 214+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/pane.c | 10++++++++++
Msrc/pane.h | 1+
Msrc/player.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/player.h | 15++++++++-------
Msrc/ref.c | 2+-
Msrc/tag.c | 34++++++++++++++++++++++++++++++++++
Msrc/tag.h | 4++++
Msrc/track.c | 16++++++++++++----
Msrc/track.h | 3+--
Msrc/util.c | 11+++++++++--
Msrc/util.h | 4++--
12 files changed, 259 insertions(+), 150 deletions(-)

diff --git a/src/main.c b/src/main.c @@ -5,7 +5,6 @@ #include "list.h" #include "history.h" #include "tag.h" -#include "track.h" #include "pane.h" #include "player.h" #include "listnav.h" @@ -17,6 +16,7 @@ #include <dirent.h> #include <locale.h> #include <signal.h> +#include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -44,6 +44,7 @@ enum { STYLE_ITEM_SEL, STYLE_ITEM_HOVER, STYLE_ITEM_HOVER_SEL, + STYLE_PREV, STYLE_COUNT }; @@ -80,6 +81,11 @@ static struct pane *const panes[] = { &pane_bot }; +/* 'tracks' holds all *unique* tracks */ +static struct link tracks; +static struct link tags; +static struct link tags_sel; + /* bottom 'cmd' pane for search / exec */ static struct pane *cmd_pane; static struct history search_history, command_history; @@ -92,16 +98,10 @@ static completion_gen completion; /* left pane for tags */ static struct pane *tag_pane; static struct listnav tag_nav; -static struct link tags; -static struct link tags_sel; /* right pane for tracks */ static struct pane *track_pane; static struct listnav track_nav; -static struct link playlist; -static struct link tracks; -static int autoplay; -static int shuffle; static void init(void); static void cleanup(int code, void *arg); @@ -114,6 +114,7 @@ static void ncurses_init(void); static void data_load(void); static void data_save(void); +static void data_free(void); static void tracks_load(struct tag *tag); static void tracks_save(struct tag *tag); @@ -134,8 +135,8 @@ static void tag_pane_vis(struct pane *pane, int sel); static int track_pane_input(wint_t c); static void track_pane_vis(struct pane *pane, int sel); -static int cmd_input(wint_t c); -static void cmd_vis(struct pane *pane, int sel); +static int cmd_pane_input(wint_t c); +static void cmd_pane_vis(struct pane *pane, int sel); static void queue_hover(void); static void main_input(wint_t c); @@ -143,8 +144,6 @@ static void main_vis(void); static int usercmd_save(const wchar_t *args); -static void update_player(void); - const struct cmd cmds[] = { { L"save", usercmd_save }, }; @@ -198,10 +197,11 @@ tui_init(void) 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); + style_init(STYLE_PREV, COLOR_WHITE, COLOR_BLACK, A_DIM); pane_init((tag_pane = &pane_left), tag_pane_input, tag_pane_vis); pane_init((track_pane = &pane_right), track_pane_input, track_pane_vis); - pane_init((cmd_pane = &pane_bot), cmd_input, cmd_vis); + pane_init((cmd_pane = &pane_bot), cmd_pane_input, cmd_pane_vis); pane_sel = &pane_left; pane_top_sel = pane_sel; @@ -279,8 +279,6 @@ data_load(void) tracks = LIST_HEAD; tags = LIST_HEAD; - - playlist = LIST_HEAD; tags_sel = LIST_HEAD; datadir = getenv("TMUS_DATA"); @@ -296,23 +294,11 @@ data_load(void) 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; + tag = tag_init(datadir, ent->d_name); list_push_back(&tags, LINK(tag)); - tracks_load(tag); } closedir(dir); - - autoplay = 0; - shuffle = 0; } void @@ -322,10 +308,17 @@ data_save(void) } void +data_free(void) +{ + /* TODO free track info */ +} + +void tracks_load(struct tag *tag) { + struct track *track, *track_iter; struct dirent *ent; - struct track *track; + struct link *iter; struct ref *ref; DIR *dir; @@ -340,9 +333,28 @@ tracks_load(struct tag *tag) continue; track = track_init(tag->fpath, ent->d_name); + + track->tags = LIST_HEAD; ref = ref_init(tag); + ASSERT(ref != NULL); list_push_back(&track->tags, LINK(ref)); - list_push_back(&tracks, LINK(track)); + + ref = ref_init(track); + ASSERT(ref != NULL); + list_push_back(&tag->tracks, LINK(ref)); + + for (iter = tracks.next; iter; iter = iter->next) { + track_iter = UPCAST(iter, struct ref)->data; + if (track->fid == track_iter->fid) + break; + } + + if (!iter) { + ref = ref_init(track); + ASSERT(ref != NULL); + list_push_back(&tracks, LINK(ref)); + } + } closedir(dir); } @@ -414,19 +426,23 @@ wchar_t * track_name_gen(const wchar_t *text, int fwd, int reset) { static struct link *cur; + struct link *iter; struct track *track; if (reset) { cur = tracks.next; - } else if (cur) { - cur = fwd ? cur->next : cur->prev; + iter = cur; + } else { + iter = fwd ? cur->next : cur->prev; } - while (cur != &tracks && cur) { - track = UPCAST(cur, struct track); - if (wcsstr(track->name, text)) + while (iter && iter != &tracks) { + track = UPCAST(iter, struct ref)->data; + if (wcsstr(track->name, text)) { + cur = iter; return wcsdup(track->name); - cur = fwd ? cur->next : cur->prev; + } + iter = fwd ? iter->next : iter->prev; } return NULL; @@ -439,6 +455,7 @@ toggle_current_tag(void) struct track *track; struct tag *tag; struct ref *ref; + int in_tags, in_playlist; link = link_iter(tags.next, tag_nav.sel); ASSERT(link != NULL); @@ -453,16 +470,13 @@ toggle_current_tag(void) } /* rebuild the full playlist */ - refs_free(&playlist); + refs_free(&player->playlist); for (link = tags_sel.next; link; link = link->next) { tag = UPCAST(link, struct ref)->data; - for (iter = tracks.next; iter; iter = iter->next) { - track = UPCAST(iter, struct track); - if (refs_incl(&track->tags, tag) - && !refs_incl(&playlist, track)) { - ref = ref_init(track); - list_push_back(&playlist, LINK(ref)); - } + for (iter = tag->tracks.next; iter; iter = iter->next) { + ref = ref_init(UPCAST(iter, struct ref)->data); + ASSERT(ref != NULL); + list_push_back(&player->playlist, LINK(ref)); } } } @@ -478,11 +492,9 @@ tag_pane_input(wint_t c) listnav_update_sel(&tag_nav, tag_nav.sel + 1); return 1; case KEY_SPACE: - player->next = NULL; toggle_current_tag(); return 1; case KEY_ENTER: - player->next = NULL; refs_free(&tags_sel); toggle_current_tag(); return 1; @@ -522,16 +534,20 @@ tag_pane_vis(struct pane *pane, int sel) style_on(pane->win, STYLE_ITEM_HOVER_SEL); else if (sel && index == tag_nav.sel) style_on(pane->win, STYLE_ITEM_HOVER); + else if (index == tag_nav.sel) + style_on(pane->win, STYLE_PREV); else if (tsel) style_on(pane->win, STYLE_ITEM_SEL); wmove(pane->win, 1 + index - tag_nav.wmin, 0); wprintw(pane->win, "%*.*s", pane->w, pane->w, tag->name); - if (index == tag_nav.sel && tsel) + if (sel && index == tag_nav.sel && tsel) style_off(pane->win, STYLE_ITEM_HOVER_SEL); - else if (index == tag_nav.sel) + else if (sel && index == tag_nav.sel) style_off(pane->win, STYLE_ITEM_HOVER); + else if (index == tag_nav.sel) + style_off(pane->win, STYLE_PREV); else if (tsel) style_off(pane->win, STYLE_ITEM_SEL); } @@ -551,10 +567,9 @@ track_pane_input(wint_t c) listnav_update_sel(&track_nav, track_nav.sel + 1); return 1; case KEY_ENTER: - link = link_iter(playlist.next, track_nav.sel); + link = link_iter(player->playlist.next, track_nav.sel); ASSERT(link != NULL); track = UPCAST(link, struct ref)->data; - player->track = track; player_play_track(track); return 1; case KEY_PPAGE: @@ -580,11 +595,11 @@ track_pane_vis(struct pane *pane, int sel) werase(pane->win); pane_title(pane, "Tracks", sel); - listnav_update_bounds(&track_nav, 0, list_len(&playlist)); + listnav_update_bounds(&track_nav, 0, list_len(&player->playlist)); listnav_update_wlen(&track_nav, pane->h - 1); index = 0; - for (iter = playlist.next; iter; iter = iter->next, index++) { + for (iter = player->playlist.next; iter; iter = iter->next, index++) { track = UPCAST(iter, struct ref)->data; if (index < track_nav.wmin) continue; @@ -594,6 +609,8 @@ track_pane_vis(struct pane *pane, int sel) style_on(pane->win, STYLE_ITEM_HOVER_SEL); else if (sel && index == track_nav.sel) style_on(pane->win, STYLE_ITEM_HOVER); + else if (index == track_nav.sel) + style_on(pane->win, STYLE_PREV); else if (track == player->track) style_on(pane->win, STYLE_ITEM_SEL); @@ -604,6 +621,8 @@ track_pane_vis(struct pane *pane, int sel) style_off(pane->win, STYLE_ITEM_HOVER_SEL); else if (sel && index == track_nav.sel) style_off(pane->win, STYLE_ITEM_HOVER); + else if (index == track_nav.sel) + style_off(pane->win, STYLE_PREV); else if (track == player->track) style_off(pane->win, STYLE_ITEM_SEL); } @@ -634,7 +653,7 @@ play_track(const wchar_t *query) struct link *iter; for (iter = tracks.next; iter; iter = iter->next) { - track = UPCAST(iter, struct track); + track = UPCAST(iter, struct ref)->data; if (wcsstr(track->name, query)) { player_play_track(track); return 1; @@ -645,7 +664,7 @@ play_track(const wchar_t *query) } int -cmd_input(wint_t c) +cmd_pane_input(wint_t c) { wchar_t *res; @@ -730,7 +749,7 @@ cmd_input(wint_t c) } void -cmd_vis(struct pane *pane, int sel) +cmd_pane_vis(struct pane *pane, int sel) { static wchar_t *linebuf = NULL; static int linecap = 0; @@ -741,20 +760,27 @@ cmd_vis(struct pane *pane, int sel) werase(pane->win); + /* TODO add clear_line func to fill with spaces instead of + * filling buffer */ + /* static line buffer for perf */ - if (pane->w + 1 > linecap) { - linecap = MAX(linecap, pane->w + 1); + if (pane->w > linecap) { + linecap = MAX(linecap, pane->w); linebuf = realloc(linebuf, linecap * sizeof(wchar_t)); } end = linebuf + linecap; - wmove(pane->win, 0, 0); + /* track name */ style_on(pane->win, STYLE_TITLE); - wprintw(pane->win, " %-*.*ls\n", pane->w - 1, pane->w - 1, - player->track ? player->track->name : L""); + pane_clearln(pane, 0); + if (player->track) { + swprintf(linebuf, linecap, L"%ls", player->track->name); + mvwaddwstr(pane->win, 0, 1, linebuf); + } style_off(pane->win, STYLE_TITLE); if (player->loaded) { + /* status line */ line = linebuf; line += swprintf(line, end - line, L"%c ", player_state_chars[player->state]); @@ -779,27 +805,31 @@ cmd_vis(struct pane *pane, int sel) list_len(&player->queue)); } - if (autoplay) { + if (player->autoplay) { line += swprintf(line, end - line, L" | AUTOPLAY"); } - if (shuffle) { + if (player->shuffle) { line += swprintf(line, end - line, L" | SHUFFLE"); } - line += swprintf(line, end - line, L"%*s", pane->w, L" "); - ATTR_ON(pane->win, A_REVERSE); + pane_clearln(pane, 1); mvwaddwstr(pane->win, 1, 0, linebuf); ATTR_OFF(pane->win, A_REVERSE); } else if (player->msg) { + /* player message */ line = linebuf; line += swprintf(line, linecap, L"[PLAYER] %s", player->msg); - line += swprintf(line, end - line, L"%*s", pane->w, L" "); + line += swprintf(line, end - line, L"%*.*s", + pane->w, pane->w, L" "); + + pane_clearln(pane, 1); mvwaddwstr(pane->win, 1, 0, linebuf); } if (sel || cmd_show) { + /* cmd and search input */ line = linebuf; cmd = history->cmd; @@ -818,8 +848,8 @@ cmd_vis(struct pane *pane, int sel) offset = wcslen(linebuf); line += swprintf(line, end - line, L"%ls", cmd->buf); - line += swprintf(line, end - line, L"%*s", pane->w, L" "); + pane_clearln(pane, 2); mvwaddwstr(pane->win, 2, 0, linebuf); if (sel) { /* show cursor in text */ @@ -837,7 +867,7 @@ queue_hover(void) { struct link *link; - link = link_iter(playlist.next, track_nav.sel); + link = link_iter(player->playlist.next, track_nav.sel); ASSERT(link != NULL); player_queue_append(UPCAST(link, struct ref)->data); } @@ -860,11 +890,7 @@ main_input(wint_t c) break; case KEY_RIGHT: if (!player->loaded) break; - if (player->time_end > player->time_pos + 10) { - player_seek(player->time_pos + 10); - } else { - player_next(); - } + player_seek(MIN(player->time_pos + 10, player->time_end)); break; case L'y': queue_hover(); @@ -884,16 +910,15 @@ main_input(wint_t c) player_prev(); break; case L'w': - autoplay ^= 1; + player->autoplay ^= 1; break; case L'g': - player->next = NULL; - shuffle ^= 1; + player->shuffle ^= 1; break; case L'b': player_seek(0); break; - case L's': + case L'x': if (player->state == PLAYER_STATE_PLAYING) { player_stop(); } else { @@ -949,43 +974,6 @@ usercmd_save(const wchar_t *args) return 0; } -void -update_player(void) -{ - static struct track *first = NULL; - struct track *track; - struct link *iter; - int index; - - player_update(); - - if (list_empty(&playlist)) first = NULL; - - /* dont start autoplay before the first song was chosen */ - if (!player->next && first) { - iter = NULL; - if (shuffle) { - /* TODO better algorithm for random sequence */ - index = rand() % list_len(&playlist); - iter = link_iter(playlist.next, index); - ASSERT(iter != NULL); - } else if (player->loaded) { - iter = playlist.next; - for (; iter; iter = iter->next) { - track = UPCAST(iter, struct ref)->data; - if (track == player->track) - break; - } - iter = iter->next; - } - if (!iter) iter = playlist.next; - - player->next = UPCAST(iter, struct ref)->data; - } else { - first = player->track; - } -} - int main(int argc, const char **argv) { @@ -996,7 +984,7 @@ main(int argc, const char **argv) c = KEY_RESIZE; do { - update_player(); + player_update(); if (c == KEY_RESIZE) { tui_resize(); diff --git a/src/pane.c b/src/pane.c @@ -29,6 +29,16 @@ pane_resize(struct pane *pane, int sx, int sy, int ex, int ey) } void +pane_clearln(struct pane *pane, int y) +{ + int i; + + wmove(pane->win, y, 0); + for (i = 0; i < pane->w; i++) + waddch(pane->win, ' '); +} + +void pane_free(struct pane *pane) { delwin(pane->win); diff --git a/src/pane.h b/src/pane.h @@ -21,5 +21,6 @@ struct pane { void pane_init(struct pane *pane, pane_handler handle, pane_updater update); void pane_resize(struct pane *pane, int sx, int sy, int ex, int ey); +void pane_clearln(struct pane *pane, int y); void pane_free(struct pane *p); diff --git a/src/player.c b/src/player.c @@ -63,10 +63,17 @@ player_init(void) player->queue = LIST_HEAD; player->history = LIST_HEAD; + + player->playlist = LIST_HEAD; + player->track = NULL; player->state = PLAYER_STATE_PAUSED; - player->next = NULL; + player->autoplay = 0; + + player->shuffle = 0; + + player->action = PLAYER_ACTION_NONE; player->seek_delay = 0; @@ -93,6 +100,37 @@ player_free(void) } void +player_play_next(struct track *prev) +{ + struct link *iter; + struct track *track; + int index; + + if (list_empty(&player->playlist)) + return; + + if (player->shuffle) { + /* TODO better algorithm for random sequence */ + index = rand() % list_len(&player->playlist); + iter = link_iter(player->playlist.next, index); + ASSERT(iter != NULL); + } else if (player->loaded) { + iter = player->playlist.next; + for (; iter; iter = iter->next) { + track = UPCAST(iter, struct ref)->data; + if (track == prev) + break; + } + if (iter) iter = iter->next; + } + + if (!iter) iter = player->playlist.next; + track = UPCAST(iter, struct ref)->data; + + player_play_track(track); +} + +void player_update(void) { struct mpd_status *status; @@ -100,6 +138,20 @@ player_update(void) struct ref *ref; const char *tmp; + status = mpd_run_status(player->conn); + ASSERT(status != NULL); + + if (!mpd_run_current_song(player->conn)) { + /* if autoplay and another track just finished, + * or there are tracks in queue to be played */ + if (player->track && player->autoplay + || !list_empty(&player->queue)) { + player->action = PLAYER_ACTION_PLAY_NEXT; + } + } + + free(status); + if (player->action != PLAYER_ACTION_NONE) { handle_mpd_status(mpd_run_clear(player->conn)); @@ -111,6 +163,9 @@ player_update(void) ref = UPCAST(list_pop_front(&player->history), struct ref); + /* TODO keep index instead until new track is played */ + /* TODO create slimmer player_backend interface */ + /* dont add current song to history */ player->track = NULL; @@ -123,9 +178,8 @@ player_update(void) struct ref); player_play_track(ref->data); ref_free(ref); - } else if (player->next) { - player_play_track(player->next); - player->next = NULL; + } else { + player_play_next(player->track); } break; default: @@ -134,9 +188,24 @@ player_update(void) player->action = PLAYER_ACTION_NONE; } + /* TODO move prev / next handling to own functions */ + status = mpd_run_status(player->conn); ASSERT(status != NULL); + song = mpd_run_current_song(player->conn); + if (song) { + player->loaded = true; + player->time_pos = mpd_status_get_elapsed_time(status); + player->time_end = mpd_song_get_duration(song); + mpd_song_free(song); + } else { + player->track = NULL; + player->loaded = false; + player->time_pos = 0; + player->time_end = 0; + } + switch (mpd_status_get_state(status)) { case MPD_STATE_PAUSE: player->state = PLAYER_STATE_PAUSED; @@ -158,18 +227,6 @@ player_update(void) player_play(); } - song = mpd_run_current_song(player->conn); - if (song) { - player->loaded = true; - player->time_pos = mpd_status_get_elapsed_time(status); - player->time_end = mpd_song_get_duration(song); - mpd_song_free(song); - } else { - player->track = NULL; - player->loaded = false; - player->time_pos = 0; - player->time_end = 0; - } mpd_status_free(status); } @@ -213,16 +270,14 @@ player_play_track(struct track *track) struct link *link; int status; - player->next = NULL; if (player->track && player->track != track) { list_push_front(&player->history, LINK(ref_init(player->track))); } - player->track = track; handle_mpd_status(mpd_run_clear(player->conn)); - status = mpd_run_add(player->conn, player->track->fpath); + status = mpd_run_add(player->conn, track->fpath); if (handle_mpd_status(status)) return PLAYER_ERR; @@ -230,6 +285,8 @@ player_play_track(struct track *track) if (handle_mpd_status(status)) return PLAYER_ERR; + player->track = track; + return PLAYER_OK; } diff --git a/src/player.h b/src/player.h @@ -36,9 +36,14 @@ struct player { /* TODO move implementation details to source file */ struct mpd_connection *conn; + /* TODO combine with index */ + /* for navigating forward and backwards in time */ struct link queue; struct link history; + /* list of track refs to choose from on prev / next */ + struct link playlist; + /* last player track */ struct track *track; @@ -49,15 +54,11 @@ struct player { /* stopped, paused or playing */ int state; - /* TODO: replace with JIT solution */ - /* track to play when queue is empty - * and player_next is called */ - struct track *next; - - /* automatically start playing player->next - * when queue is empty */ + /* automatically select new tracks when queue empty */ int autoplay; + int shuffle; + int action; /* number of frames to wait before unpausing after diff --git a/src/ref.c b/src/ref.c @@ -7,7 +7,7 @@ ref_init(void *data) struct ref *ref; ref = malloc(sizeof(struct ref)); - ASSERT(ref != NULL); + if (!ref) return NULL; ref->link = LINK_EMPTY; ref->data = data; return ref; diff --git a/src/tag.c b/src/tag.c @@ -1,4 +1,38 @@ #include "tag.h" #include "link.h" +#include "ref.h" +#include <string.h> +struct tag * +tag_init(const char *path, const char *fname) +{ + struct tag *tag; + + tag = malloc(sizeof(struct tag)); + ASSERT(tag != NULL); + + tag->fname = strdup(fname); + ASSERT(tag->fname != NULL); + + tag->fpath = aprintf("%s/%s", path, fname); + ASSERT(tag->fpath != NULL); + + tag->name = sanitized(tag->fname); + ASSERT(tag->name != NULL); + + tag->link = LINK_EMPTY; + + tag->tracks = LIST_HEAD; + + return tag; +} + +void +tag_free(struct tag *tag) +{ + free(tag->fname); + free(tag->fpath); + free(tag->name); + refs_free(&tag->tracks); +} diff --git a/src/tag.h b/src/tag.h @@ -7,6 +7,10 @@ struct tag { char *name; char *fname, *fpath; + struct link tracks; + struct link link; }; +struct tag *tag_init(const char *path, const char *fname); +void tag_free(struct tag *tag); diff --git a/src/track.c b/src/track.c @@ -2,24 +2,32 @@ #include <wchar.h> #include <string.h> +#include <sys/stat.h> struct track * -track_init(const char *dir, const char *file) +track_init(const char *dir, const char *fname) { struct track *track; + struct stat info; track = malloc(sizeof(struct track)); ASSERT(track != NULL); - track->fname = strdup(file); + track->fname = strdup(fname); ASSERT(track->fname != NULL); - track->fpath = aprintf("%s/%s", dir, file); + track->fpath = aprintf("%s/%s", dir, fname); ASSERT(track->fpath != NULL); track->name = calloc(strlen(track->fname) + 1, sizeof(wchar_t)); + ASSERT(track->name != NULL); mbstowcs(track->name, track->fname, strlen(track->fname) + 1); - track->link = LINK_EMPTY; + if (!stat(track->fpath, &info)) { + track->fid = info.st_ino; + } else { + track->fid = -1; + } + track->tags = LIST_HEAD; return track; diff --git a/src/track.h b/src/track.h @@ -8,8 +8,7 @@ struct track { wchar_t *name; struct link tags; char *fname, *fpath; - - struct link link; + int fid; }; struct track *track_init(const char *dir, const char *file); diff --git a/src/util.c b/src/util.c @@ -41,10 +41,17 @@ done: } void -panic(const char *msg, const char *file, int line) +panic(const char *file, int line, const char *msg, ...) { + va_list ap; + endwin(); - fprintf(stderr, "Panic at %s:%i (%s)\n", file, line, msg); + fprintf(stderr, "Panic at %s:%i (", file, line); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, ")\n"); + exit(1); } diff --git a/src/util.h b/src/util.h @@ -6,12 +6,12 @@ #define MIN(a, b) ((a) > (b) ? (b) : (a)) #define ARRLEN(x) (sizeof(x)/sizeof((x)[0])) -#define PANIC(...) panic("" __VA_ARGS__, __FILE__, __LINE__) +#define PANIC(...) panic(__FILE__, __LINE__, "" __VA_ARGS__) #define ASSERT(x) assert((x), __FILE__, __LINE__, #x) int strnwidth(const char *s, int n); -void panic(const char *msg, const char *file, int line); +void panic(const char *file, int line, const char *msg, ...); void assert(int cond, const char *file, int line, const char *condstr); char *aprintf(const char *fmtstr, ...);