/*
* Copyright 2008-2013 Various Authors
* Copyright 2006 Timo Hirvonen
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include "options.h"
#include "list.h"
#include "utils.h"
#include "xmalloc.h"
#include "player.h"
#include "buffer.h"
#include "ui_curses.h"
#include "cmus.h"
#include "misc.h"
#include "lib.h"
#include "pl.h"
#include "browser.h"
#include "keys.h"
#include "filters.h"
#include "command_mode.h"
#include "file.h"
#include "prog.h"
#include "output.h"
#include "input.h"
#include "xstrjoin.h"
#include "track_info.h"
#include "cache.h"
#include "debug.h"
#include "discid.h"
#include "mpris.h"
#ifdef HAVE_CONFIG
#include "config/curses.h"
#endif
#include
#include
#include
#if defined(__sun__)
#include
#else
#include
#endif
/* initialized option variables */
char *cdda_device = NULL;
char *output_plugin = NULL;
char *status_display_program = NULL;
char *server_password;
int auto_reshuffle = 1;
int confirm_run = 1;
int resume_cmus = 0;
int show_hidden = 0;
int show_current_bitrate = 0;
int show_playback_position = 1;
int show_remaining_time = 0;
int set_term_title = 1;
int wrap_search = 1;
int play_library = 1;
int repeat = 0;
int shuffle = 0;
int follow = 0;
int display_artist_sort_name;
int smart_artist_sort = 1;
int sort_albums_by_name = 0;
int scroll_offset = 2;
int rewind_offset = 5;
int skip_track_info = 0;
int ignore_duplicates = 0;
int auto_expand_albums_follow = 1;
int auto_expand_albums_search = 1;
int auto_expand_albums_selcur = 1;
int auto_hide_playlists_panel = 0;
int show_all_tracks = 1;
int mouse = 0;
int mpris = 1;
int time_show_leading_zero = 1;
int start_view = TREE_VIEW;
int stop_after_queue = 0;
int tree_width_percent = 33;
int tree_width_max = 0;
int pause_on_output_change = 0;
int block_key_paste = 1;
int progress_bar = 1;
int search_resets_position = 1;
int colors[NR_COLORS] = {
-1,
-1,
COLOR_RED | BRIGHT,
COLOR_YELLOW | BRIGHT,
COLOR_BLUE,
COLOR_WHITE,
COLOR_BLACK,
COLOR_BLUE,
COLOR_WHITE | BRIGHT,
COLOR_BLUE,
COLOR_WHITE | BRIGHT,
-1,
COLOR_YELLOW | BRIGHT,
COLOR_BLUE,
COLOR_YELLOW | BRIGHT,
COLOR_BLUE | BRIGHT,
-1,
COLOR_WHITE,
COLOR_YELLOW | BRIGHT,
COLOR_WHITE,
COLOR_BLACK,
COLOR_BLUE,
COLOR_WHITE | BRIGHT,
COLOR_BLUE,
COLOR_WHITE | BRIGHT,
-1,
-1,
};
int attrs[NR_ATTRS] = {
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_NORMAL,
A_BOLD,
A_NORMAL,
};
/* uninitialized option variables */
char *tree_win_format = NULL;
char *tree_win_artist_format = NULL;
char *track_win_album_format = NULL;
char *track_win_format = NULL;
char *track_win_format_va = NULL;
char *track_win_alt_format = NULL;
char *list_win_format = NULL;
char *list_win_format_va = NULL;
char *list_win_alt_format = NULL;
char *clipped_text_format = NULL;
char *clipped_text_internal = NULL;
char *current_format = NULL;
char *current_alt_format = NULL;
char *heading_album_format = NULL;
char *heading_artist_format = NULL;
char *heading_playlist_format = NULL;
char *statusline_format = NULL;
char *window_title_format = NULL;
char *window_title_alt_format = NULL;
char *id3_default_charset = NULL;
char *icecast_default_charset = NULL;
char *lib_add_filter = NULL;
char **pl_env_vars;
static void buf_int(char *buf, int val, size_t size)
{
snprintf(buf, size, "%d", val);
}
static int parse_int(const char *buf, int minval, int maxval, int *val)
{
long int tmp;
if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
error_msg("integer in range %d..%d expected", minval, maxval);
return 0;
}
*val = tmp;
return 1;
}
int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
{
long int tmp;
int i;
GBUF(names_buf);
if (str_to_int(buf, &tmp) == 0) {
if (tmp < minval || tmp > maxval)
goto err;
*val = tmp;
return 1;
}
for (i = 0; names[i]; i++) {
if (strcasecmp(buf, names[i]) == 0) {
*val = i + minval;
return 1;
}
}
err:
for (i = 0; names[i]; i++) {
if (i)
gbuf_add_str(&names_buf, ", ");
gbuf_add_str(&names_buf, names[i]);
}
error_msg("expected [%d..%d] or [%s]", minval, maxval, names_buf.buffer);
gbuf_free(&names_buf);
return 0;
}
static const char * const bool_names[] = {
"false", "true", NULL
};
static int parse_bool(const char *buf, int *val)
{
return parse_enum(buf, 0, 1, bool_names, val);
}
/* this is used as id in struct cmus_opt */
enum format_id {
FMT_CLIPPED_TEXT,
FMT_CURRENT_ALT,
FMT_CURRENT,
FMT_HEADING_ALBUM,
FMT_HEADING_ARTIST,
FMT_HEADING_PLAYLIST,
FMT_STATUSLINE,
FMT_PLAYLIST,
FMT_PLAYLIST_ALT,
FMT_PLAYLIST_VA,
FMT_TITLE,
FMT_TITLE_ALT,
FMT_TRACKWIN,
FMT_TRACKWIN_ALBUM,
FMT_TRACKWIN_ALT,
FMT_TRACKWIN_VA,
FMT_TREEWIN,
FMT_TREEWIN_ARTIST,
NR_FMTS
};
/* default values for the variables which we must initialize but
* can't do it statically */
static const struct {
const char *name;
const char *value;
} str_defaults[] = {
[FMT_CLIPPED_TEXT] = { "format_clipped_text" , "…" },
[FMT_CURRENT_ALT] = { "altformat_current" , " %F " },
[FMT_CURRENT] = { "format_current" , " %a - %l%! - %n. %t%= %y " },
[FMT_HEADING_ALBUM] = { "format_heading_album" , "%a - %l%= %y %{duration}" },
[FMT_HEADING_ARTIST] = { "format_heading_artist" , "%a%= %{duration}" },
[FMT_HEADING_PLAYLIST] = { "format_heading_playlist" , "%{?!panel?Playlist - }%{title}%= %{duration} " },
[FMT_STATUSLINE] = { "format_statusline" ,
" %{status} %{?show_playback_position?%{position} %{?duration?/ %{duration} }?%{?duration?%{duration} }}"
"%{?bpm>0?at %{bpm} BPM }"
"%{?stream?buf: %{buffer} }"
"%{?show_current_bitrate & bitrate>=0? %{bitrate} kbps }"
"%= "
"%{?repeat_current?repeat current?%{?play_library?%{?playlist_mode!=\"all\"?%{playlist_mode} from }%{?play_sorted?sorted }library?playlist}} | "
"%{?volume>=0?%{?lvolume!=rvolume?%{lvolume}%% %{rvolume}?%{volume}}%% | }"
"%1{continue}%1{follow}%1{repeat}%1{shuffle} "
},
[FMT_PLAYLIST_ALT] = { "altformat_playlist" , " %f%= %d %{?X!=0?%3X ? }" },
[FMT_PLAYLIST] = { "format_playlist" , " %-21%a %3n. %t%= %y %d %{?X!=0?%3X ? }" },
[FMT_PLAYLIST_VA] = { "format_playlist_va" , " %-21%A %3n. %t (%a)%= %y %d %{?X!=0?%3X ? }" },
[FMT_TITLE_ALT] = { "altformat_title" , "%f" },
[FMT_TITLE] = { "format_title" , "%a - %l - %t (%y)" },
[FMT_TRACKWIN_ALBUM] = { "format_trackwin_album" , " %l %= %y %{duration} " },
[FMT_TRACKWIN_ALT] = { "altformat_trackwin" , " %f%= %d " },
[FMT_TRACKWIN] = { "format_trackwin" , "%3n. %t%= %d " },
[FMT_TRACKWIN_VA] = { "format_trackwin_va" , "%3n. %t (%a)%= %d " },
[FMT_TREEWIN] = { "format_treewin" , " %l" },
[FMT_TREEWIN_ARTIST] = { "format_treewin_artist" , "%a" },
[NR_FMTS] =
{ "lib_sort", "albumartist date album discnumber tracknumber title filename play_count" },
{ "pl_sort", "" },
{ "id3_default_charset", "ISO-8859-1" },
{ "icecast_default_charset", "ISO-8859-1" },
{ "pl_env_vars", "" },
{ NULL, NULL }
};
static int seen_pr1317_fmts = 0;
/* callbacks for normal options {{{ */
static void get_device(void *data, char *buf, size_t size)
{
strscpy(buf, cdda_device, size);
}
static void set_device(void *data, const char *buf)
{
free(cdda_device);
cdda_device = expand_filename(buf);
}
#define SECOND_SIZE (44100 * 16 / 8 * 2)
static void get_buffer_seconds(void *data, char *buf, size_t size)
{
int val = (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) /
SECOND_SIZE;
buf_int(buf, val, size);
}
static void set_buffer_seconds(void *data, const char *buf)
{
int sec;
if (parse_int(buf, 1, 300, &sec))
player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
}
static void get_scroll_offset(void *data, char *buf, size_t size)
{
buf_int(buf, scroll_offset, size);
}
static void set_scroll_offset(void *data, const char *buf)
{
int offset;
if (parse_int(buf, 0, 9999, &offset))
scroll_offset = offset;
}
static void get_rewind_offset(void *data, char *buf, size_t size)
{
buf_int(buf, rewind_offset, size);
}
static void set_rewind_offset(void *data, const char *buf)
{
int offset;
if (parse_int(buf, -1, 9999, &offset))
rewind_offset = offset;
}
static void get_id3_default_charset(void *data, char *buf, size_t size)
{
strscpy(buf, id3_default_charset, size);
}
static void get_icecast_default_charset(void *data, char *buf, size_t size)
{
strscpy(buf, icecast_default_charset, size);
}
static void set_id3_default_charset(void *data, const char *buf)
{
free(id3_default_charset);
id3_default_charset = xstrdup(buf);
}
static void set_icecast_default_charset(void *data, const char *buf)
{
free(icecast_default_charset);
icecast_default_charset = xstrdup(buf);
}
static void get_lib_sort(void *data, char *buf, size_t size)
{
strscpy(buf, lib_editable.shared->sort_str, size);
}
static void set_lib_sort(void *data, const char *buf)
{
sort_key_t *keys = parse_sort_keys(buf);
if (keys) {
editable_shared_set_sort_keys(lib_editable.shared, keys);
editable_sort(&lib_editable);
sort_keys_to_str(keys, lib_editable.shared->sort_str,
sizeof(lib_editable.shared->sort_str));
}
}
static void get_pl_sort(void *data, char *buf, size_t size)
{
pl_get_sort_str(buf, size);
}
static void set_pl_sort(void *data, const char *buf)
{
pl_set_sort_str(buf);
}
static void get_output_plugin(void *data, char *buf, size_t size)
{
const char *value = op_get_current();
if (value)
strscpy(buf, value, size);
}
static void set_output_plugin(void *data, const char *buf)
{
if (ui_initialized) {
if (!soft_vol || pause_on_output_change)
mixer_close();
player_set_op(buf);
if (!soft_vol || pause_on_output_change)
mixer_open();
} else {
/* must set it later manually */
output_plugin = xstrdup(buf);
}
}
static void get_passwd(void *data, char *buf, size_t size)
{
if (server_password)
strscpy(buf, server_password, size);
}
static void set_passwd(void *data, const char *buf)
{
int len = strlen(buf);
if (len == 0) {
free(server_password);
server_password = NULL;
} else if (len < 6) {
error_msg("unsafe password");
} else {
free(server_password);
server_password = xstrdup(buf);
}
}
static void get_replaygain_preamp(void *data, char *buf, size_t size)
{
snprintf(buf, size, "%f", replaygain_preamp);
}
static void set_replaygain_preamp(void *data, const char *buf)
{
double val;
char *end;
val = strtod(buf, &end);
if (end == buf) {
error_msg("floating point number expected (dB)");
return;
}
player_set_rg_preamp(val);
}
static void get_softvol_state(void *data, char *buf, size_t size)
{
snprintf(buf, size, "%d %d", soft_vol_l, soft_vol_r);
}
static void set_softvol_state(void *data, const char *buf)
{
char buffer[OPTION_MAX_SIZE];
char *ptr;
long int l, r;
strscpy(buffer, buf, sizeof(buffer));
ptr = strchr(buffer, ' ');
if (!ptr)
goto err;
while (*ptr == ' ')
*ptr++ = 0;
if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
goto err;
if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
goto err;
player_set_soft_volume(l, r);
return;
err:
error_msg("two integers in range 0..100 expected");
}
static void get_status_display_program(void *data, char *buf, size_t size)
{
if (status_display_program)
strscpy(buf, status_display_program, size);
}
static void set_status_display_program(void *data, const char *buf)
{
free(status_display_program);
status_display_program = NULL;
if (buf[0])
status_display_program = expand_filename(buf);
}
static void get_tree_width_percent(void *data, char *buf, size_t size)
{
buf_int(buf, tree_width_percent, size);
}
static void set_tree_width_percent(void *data, const char *buf)
{
int percent;
if (parse_int(buf, 1, 100, &percent))
tree_width_percent = percent;
update_size();
}
static void get_tree_width_max(void *data, char *buf, size_t size)
{
buf_int(buf, tree_width_max, size);
}
static void set_tree_width_max(void *data, const char *buf)
{
int cols;
if (parse_int(buf, 0, 9999, &cols))
tree_width_max = cols;
update_size();
}
/* get_pl_env_vars converts the pl_env_vars array into a comma-separated list */
static void get_pl_env_vars(void *data, char *buf, size_t size)
{
if (!pl_env_vars) {
strscpy(buf, "", size);
return;
}
char *p = buf;
size_t r = size - 1;
for (char **x = pl_env_vars; *x; x++) {
if (x != pl_env_vars) {
if (!--r)
return; /* overflow */
*p++ = ',';
}
size_t l = strlen(*x);
if (!(r -= l))
return; /* overflow */
strcpy(p, *x);
p += l;
}
*p = '\0';
}
/* set_pl_env_vars splits the pl_env_vars config into the pl_env_vars array */
static void set_pl_env_vars(void *data, const char *buf)
{
if (pl_env_vars) {
free(*pl_env_vars);
free(pl_env_vars);
}
if (!*buf) {
pl_env_vars = NULL;
}
size_t n = 1;
for (const char *x = buf; *x; x++) {
if (*x == ',') {
n++;
}
}
char **a = pl_env_vars = xnew(char*, n+1);
for (char *x = *a++ = xstrdup(buf); *x; x++) {
if (*x == ',') {
*a++ = x+1;
*x = '\0';
}
}
*a = NULL;
}
/* }}} */
/* callbacks for toggle options {{{ */
static void get_auto_hide_playlists_panel(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[auto_hide_playlists_panel], size);
}
static void set_auto_hide_playlists_panel(void *data, const char *buf)
{
parse_bool(buf, &auto_hide_playlists_panel);
}
static void toggle_auto_hide_playlists_panel(void *data)
{
auto_hide_playlists_panel ^= 1;
}
static void get_auto_reshuffle(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[auto_reshuffle], size);
}
static void set_auto_reshuffle(void *data, const char *buf)
{
parse_bool(buf, &auto_reshuffle);
}
static void toggle_auto_reshuffle(void *data)
{
auto_reshuffle ^= 1;
}
static void get_follow(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[follow], size);
}
static void set_follow(void *data, const char *buf)
{
if (!parse_bool(buf, &follow))
return;
update_statusline();
}
static void toggle_follow(void *data)
{
follow ^= 1;
update_statusline();
}
static void get_continue(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[player_cont], size);
}
static void set_continue(void *data, const char *buf)
{
if (!parse_bool(buf, &player_cont))
return;
update_statusline();
}
static void toggle_continue(void *data)
{
player_cont ^= 1;
update_statusline();
}
static void get_continue_album(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[player_cont_album], size);
}
static void set_continue_album(void *data, const char *buf)
{
if (!parse_bool(buf, &player_cont_album))
return;
update_statusline();
}
static void toggle_continue_album(void *data)
{
player_cont_album ^= 1;
update_statusline();
}
static void get_repeat_current(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[player_repeat_current], size);
}
static void set_repeat_current(void *data, const char *buf)
{
int old = player_repeat_current;
if (!parse_bool(buf, &player_repeat_current))
return;
if (old != player_repeat_current)
mpris_loop_status_changed();
update_statusline();
}
static void toggle_repeat_current(void *data)
{
player_repeat_current ^= 1;
mpris_loop_status_changed();
update_statusline();
}
static void get_confirm_run(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[confirm_run], size);
}
static void set_confirm_run(void *data, const char *buf)
{
parse_bool(buf, &confirm_run);
}
static void toggle_confirm_run(void *data)
{
confirm_run ^= 1;
}
const char * const view_names[NR_VIEWS + 1] = {
"tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
};
static void get_play_library(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[play_library], size);
}
static void set_play_library(void *data, const char *buf)
{
if (!parse_bool(buf, &play_library))
return;
update_statusline();
}
static void toggle_play_library(void *data)
{
play_library ^= 1;
update_statusline();
}
static void get_play_sorted(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[play_sorted], size);
}
static void set_play_sorted(void *data, const char *buf)
{
int tmp;
if (!parse_bool(buf, &tmp))
return;
play_sorted = tmp;
update_statusline();
}
static void toggle_play_sorted(void *data)
{
play_sorted = play_sorted ^ 1;
if (play_sorted) {
/* play_sorted makes no sense in playlist */
play_library = 1;
}
update_statusline();
}
static void get_smart_artist_sort(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[smart_artist_sort], size);
}
static void set_smart_artist_sort(void *data, const char *buf)
{
if (parse_bool(buf, &smart_artist_sort))
lib_sort_artists();
}
static void toggle_smart_artist_sort(void *data)
{
smart_artist_sort ^= 1;
lib_sort_artists();
}
static void get_sort_albums_by_name(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[sort_albums_by_name], size);
}
static void set_sort_albums_by_name(void *data, const char *buf)
{
parse_bool(buf, &sort_albums_by_name);
lib_sort_artists();
}
static void toggle_sort_albums_by_name(void *data)
{
sort_albums_by_name ^= 1;
lib_sort_artists();
}
static void get_display_artist_sort_name(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[display_artist_sort_name], size);
}
static void set_display_artist_sort_name(void *data, const char *buf)
{
parse_bool(buf, &display_artist_sort_name);
lib_tree_win->changed = 1;
}
static void toggle_display_artist_sort_name(void *data)
{
display_artist_sort_name ^= 1;
lib_tree_win->changed = 1;
}
const char * const aaa_mode_names[] = {
"all", "artist", "album", NULL
};
static void get_aaa_mode(void *data, char *buf, size_t size)
{
strscpy(buf, aaa_mode_names[aaa_mode], size);
}
static void set_aaa_mode(void *data, const char *buf)
{
int tmp;
if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
return;
aaa_mode = tmp;
update_statusline();
}
static void toggle_aaa_mode(void *data)
{
/* aaa mode makes no sense in playlist */
play_library = 1;
aaa_mode++;
aaa_mode %= 3;
update_statusline();
}
static void get_repeat(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[repeat], size);
}
static void set_repeat(void *data, const char *buf)
{
int old = repeat;
if (!parse_bool(buf, &repeat))
return;
if (!player_repeat_current && old != repeat)
mpris_loop_status_changed();
update_statusline();
}
static void toggle_repeat(void *data)
{
repeat ^= 1;
if (!player_repeat_current)
mpris_loop_status_changed();
update_statusline();
}
static const char * const replaygain_names[] = {
"disabled", "track", "album", "track-preferred", "album-preferred", "smart", NULL
};
static const size_t replaygain_names_len = sizeof(replaygain_names) / sizeof(replaygain_names[0]) - 1;
static void get_replaygain(void *data, char *buf, size_t size)
{
strscpy(buf, replaygain_names[replaygain], size);
}
static void set_replaygain(void *data, const char *buf)
{
int tmp;
if (!parse_enum(buf, 0, replaygain_names_len - 1, replaygain_names, &tmp))
return;
player_set_rg(tmp);
}
static void toggle_replaygain(void *data)
{
player_set_rg((replaygain + 1) % replaygain_names_len);
}
static void get_replaygain_limit(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[replaygain_limit], size);
}
static void set_replaygain_limit(void *data, const char *buf)
{
int tmp;
if (!parse_bool(buf, &tmp))
return;
player_set_rg_limit(tmp);
}
static void toggle_replaygain_limit(void *data)
{
player_set_rg_limit(replaygain_limit ^ 1);
}
static void get_resume(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[resume_cmus], size);
}
static void set_resume(void *data, const char *buf)
{
parse_bool(buf, &resume_cmus);
}
static void toggle_resume(void *data)
{
resume_cmus ^= 1;
}
static void get_show_hidden(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[show_hidden], size);
}
static void set_show_hidden(void *data, const char *buf)
{
if (!parse_bool(buf, &show_hidden))
return;
browser_reload();
}
static void toggle_show_hidden(void *data)
{
show_hidden ^= 1;
browser_reload();
}
static void set_show_all_tracks_int(int); /* defined below */
static void get_auto_expand_albums_follow(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[auto_expand_albums_follow], size);
}
static void set_auto_expand_albums_follow_int(int value)
{
auto_expand_albums_follow = !!value;
if (!auto_expand_albums_follow && !show_all_tracks)
set_show_all_tracks_int(1);
}
static void set_auto_expand_albums_follow(void *data, const char *buf)
{
int tmp = 0;
parse_bool(buf, &tmp);
set_auto_expand_albums_follow_int(tmp);
}
static void toggle_auto_expand_albums_follow(void *data)
{
set_auto_expand_albums_follow_int(!auto_expand_albums_follow);
}
static void get_auto_expand_albums_search(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[auto_expand_albums_search], size);
}
static void set_auto_expand_albums_search_int(int value)
{
auto_expand_albums_search = !!value;
if (!auto_expand_albums_search && !show_all_tracks)
set_show_all_tracks_int(1);
}
static void set_auto_expand_albums_search(void *data, const char *buf)
{
int tmp = 0;
parse_bool(buf, &tmp);
set_auto_expand_albums_search_int(tmp);
}
static void toggle_auto_expand_albums_search(void *data)
{
set_auto_expand_albums_search_int(!auto_expand_albums_search);
}
static void get_auto_expand_albums_selcur(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[auto_expand_albums_selcur], size);
}
static void set_auto_expand_albums_selcur_int(int value)
{
auto_expand_albums_selcur = !!value;
if (!auto_expand_albums_selcur && !show_all_tracks)
set_show_all_tracks_int(1);
}
static void set_auto_expand_albums_selcur(void *data, const char *buf)
{
int tmp = 0;
parse_bool(buf, &tmp);
set_auto_expand_albums_selcur_int(tmp);
}
static void toggle_auto_expand_albums_selcur(void *data)
{
set_auto_expand_albums_selcur_int(!auto_expand_albums_selcur);
}
static void get_show_all_tracks(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[show_all_tracks], size);
}
static void set_show_all_tracks_int(int value)
{
value = !!value;
if (show_all_tracks == value)
return;
show_all_tracks = value;
if (!show_all_tracks) {
if (!auto_expand_albums_follow)
set_auto_expand_albums_follow_int(1);
if (!auto_expand_albums_search)
set_auto_expand_albums_search_int(1);
if (!auto_expand_albums_selcur)
set_auto_expand_albums_selcur_int(1);
}
tree_sel_update(0);
}
static void set_show_all_tracks(void *data, const char *buf)
{
int tmp = 0;
parse_bool(buf, &tmp);
set_show_all_tracks_int(tmp);
}
static void toggle_show_all_tracks(void *data)
{
set_show_all_tracks_int(!show_all_tracks);
}
static void get_show_current_bitrate(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[show_current_bitrate], size);
}
static void set_show_current_bitrate(void *data, const char *buf)
{
if (parse_bool(buf, &show_current_bitrate))
update_statusline();
}
static void toggle_show_current_bitrate(void *data)
{
show_current_bitrate ^= 1;
update_statusline();
}
static void get_show_playback_position(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[show_playback_position], size);
}
static void set_show_playback_position(void *data, const char *buf)
{
if (!parse_bool(buf, &show_playback_position))
return;
update_statusline();
}
static void toggle_show_playback_position(void *data)
{
show_playback_position ^= 1;
update_statusline();
}
static void get_show_remaining_time(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[show_remaining_time], size);
}
static void set_show_remaining_time(void *data, const char *buf)
{
if (!parse_bool(buf, &show_remaining_time))
return;
update_statusline();
}
static void toggle_show_remaining_time(void *data)
{
show_remaining_time ^= 1;
update_statusline();
}
static void get_set_term_title(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[set_term_title], size);
}
static void set_set_term_title(void *data, const char *buf)
{
parse_bool(buf, &set_term_title);
}
static void toggle_set_term_title(void *data)
{
set_term_title ^= 1;
}
const char * const shuffle_names[] = {
"off", "tracks", "albums", "false", "true", NULL
};
static void get_shuffle(void *data, char *buf, size_t size)
{
strscpy(buf, shuffle_names[shuffle], size);
}
static void set_shuffle(void *data, const char *buf)
{
int tmp;
if (!parse_enum(buf, 0, 4, shuffle_names, &tmp))
return;
if (tmp == SHUFFLE_FALSE)
tmp = SHUFFLE_OFF;
else if (tmp == SHUFFLE_TRUE)
tmp = SHUFFLE_TRACKS;
if (tmp != shuffle)
mpris_shuffle_changed();
shuffle = tmp;
update_statusline();
}
static void toggle_shuffle(void *data)
{
shuffle++;
shuffle %= 3;
/* album mode makes no sense in playlist */
if (!play_library && shuffle == SHUFFLE_ALBUMS)
shuffle = SHUFFLE_OFF;
update_statusline();
}
static void get_softvol(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[soft_vol], size);
}
static void do_set_softvol(int soft)
{
if (!soft_vol || pause_on_output_change)
mixer_close();
player_set_soft_vol(soft);
if (!soft_vol || pause_on_output_change)
mixer_open();
update_statusline();
}
static void set_softvol(void *data, const char *buf)
{
int soft;
if (!parse_bool(buf, &soft))
return;
do_set_softvol(soft);
}
static void toggle_softvol(void *data)
{
do_set_softvol(soft_vol ^ 1);
}
static void get_wrap_search(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[wrap_search], size);
}
static void set_wrap_search(void *data, const char *buf)
{
parse_bool(buf, &wrap_search);
}
static void toggle_wrap_search(void *data)
{
wrap_search ^= 1;
}
static void get_skip_track_info(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[skip_track_info], size);
}
static void set_skip_track_info(void *data, const char *buf)
{
parse_bool(buf, &skip_track_info);
}
static void toggle_skip_track_info(void *data)
{
skip_track_info ^= 1;
}
static void get_ignore_duplicates(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[ignore_duplicates], size);
}
static void set_ignore_duplicates(void *data, const char *buf)
{
parse_bool(buf, &ignore_duplicates);
}
static void toggle_ignore_duplicates(void *data)
{
ignore_duplicates ^= 1;
}
void update_mouse(void)
{
if (mouse) {
mouseinterval(25);
mousemask(BUTTON_CTRL | BUTTON_ALT
| BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED
| BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
| BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED
| BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED
| BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED
| BUTTON4_PRESSED | BUTTON4_RELEASED | BUTTON4_CLICKED
| BUTTON4_DOUBLE_CLICKED | BUTTON4_TRIPLE_CLICKED
#if NCURSES_MOUSE_VERSION >= 2
| BUTTON5_PRESSED | BUTTON5_RELEASED | BUTTON5_CLICKED
| BUTTON5_DOUBLE_CLICKED | BUTTON5_TRIPLE_CLICKED
#endif
, NULL);
} else {
mousemask(0, NULL);
}
}
static void get_mouse(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[mouse], size);
}
static void set_mouse(void *data, const char *buf)
{
parse_bool(buf, &mouse);
update_mouse();
}
static void toggle_mouse(void *data)
{
mouse ^= 1;
update_mouse();
}
static void get_mpris(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[mpris], size);
}
static void set_mpris(void *data, const char *buf)
{
parse_bool(buf, &mpris);
}
static void toggle_mpris(void *data)
{
mpris ^= 1;
}
static void get_time_show_leading_zero(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[time_show_leading_zero], size);
}
static void set_time_show_leading_zero(void *data, const char *buf)
{
if (!parse_bool(buf, &time_show_leading_zero))
return;
update_statusline();
}
static void toggle_time_show_leading_zero(void *data)
{
time_show_leading_zero ^= 1;
update_statusline();
}
static void get_lib_add_filter(void *data, char *buf, size_t size)
{
strscpy(buf, lib_add_filter ? lib_add_filter : "", size);
}
static void set_lib_add_filter(void *data, const char *buf)
{
struct expr *expr = NULL;
if (strlen(buf) != 0) {
/* parse expression if non-empty string given */
expr = expr_parse(buf);
if (!expr)
return;
}
free(lib_add_filter);
lib_add_filter = xstrdup(buf);
lib_set_add_filter(expr);
}
static void get_stop_after_queue(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[stop_after_queue], size);
}
static void set_stop_after_queue(void *data, const char *buf)
{
parse_bool(buf, &stop_after_queue);
}
static void toggle_stop_after_queue(void *data)
{
stop_after_queue ^= 1;
}
static void get_pause_on_output_change(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[pause_on_output_change], size);
}
static void set_pause_on_output_change(void *data, const char *buf)
{
parse_bool(buf, &pause_on_output_change);
}
static void toggle_pause_on_output_change(void *data)
{
pause_on_output_change ^= 1;
}
static void get_block_key_paste(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[block_key_paste], size);
}
static void set_block_key_paste(void *data, const char *buf)
{
parse_bool(buf, &block_key_paste);
}
static void toggle_block_key_paste(void *data)
{
block_key_paste ^= 1;
}
const char * const progress_bar_names[] = {
"disabled", "line", "shuttle", "color", "color_shuttle", NULL
};
static void get_progress_bar(void *data, char *buf, size_t size)
{
strscpy(buf, progress_bar_names[progress_bar], size);
}
static void set_progress_bar(void *data, const char *buf)
{
parse_enum(buf, 0, 4, progress_bar_names, &progress_bar);
}
static void toggle_progress_bar(void *data)
{
progress_bar++;
progress_bar %= NR_PROGRESS_BAR_MODES;
update_statusline();
}
static void get_search_resets_position(void *data, char *buf, size_t size)
{
strscpy(buf, bool_names[search_resets_position], size);
}
static void set_search_resets_position(void *data, const char *buf)
{
parse_bool(buf, &search_resets_position);
}
static void toggle_search_resets_position(void *data)
{
search_resets_position ^= 1;
}
/* }}} */
/* special callbacks (id set) {{{ */
static const char * const color_enum_names[1 + 8 * 2 + 1] = {
"default",
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
"darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
NULL
};
static void get_color(void *data, char *buf, size_t size)
{
int val = *(int *)data;
if (val < 16) {
strscpy(buf, color_enum_names[val + 1], size);
} else {
buf_int(buf, val, size);
}
}
static void set_color(void *data, const char *buf)
{
int color;
if (!parse_enum(buf, -1, 255, color_enum_names, &color))
return;
*(int *)data = color;
update_colors();
update_full();
}
static void get_start_view(void *data, char *buf, size_t size)
{
strscpy(buf, view_names[start_view], size);
}
static void set_start_view(void *data, const char *buf)
{
int view;
if (parse_enum(buf, 0, NR_VIEWS - 1, view_names, &view)) {
start_view = view;
}
}
static void get_attr(void *data, char *buf, size_t size)
{
int attr = *(int *)data;
if (attr == 0) {
strscpy(buf, "default", size);
return;
}
const char *standout = "";
const char *underline = "";
const char *reverse = "";
const char *blink = "";
const char *bold = "";
const char *italic = "";
if (attr & A_STANDOUT)
standout = "standout|";
if (attr & A_UNDERLINE)
underline = "underline|";
if (attr & A_REVERSE)
reverse = "reverse|";
if (attr & A_BLINK)
blink = "blink|";
if (attr & A_BOLD)
bold = "bold|";
#if HAVE_ITALIC
if (attr & A_ITALIC)
italic = "italic|";
#endif
size_t len = snprintf(buf, size, "%s%s%s%s%s%s",
standout, underline, reverse, blink, bold, italic);
if (0 < len && len < size)
buf[len - 1] = 0;
}
static void set_attr(void *data, const char *buf)
{
int attr = 0;
size_t i = 0;
size_t offset = 0;
size_t length = 0;
char* current;
do {
if (buf[i] == '|' || buf[i] == '\0') {
current = xstrndup(&buf[offset], length);
if (strcmp(current, "default") == 0)
attr |= A_NORMAL;
else if (strcmp(current, "standout") == 0)
attr |= A_STANDOUT;
else if (strcmp(current, "underline") == 0)
attr |= A_UNDERLINE;
else if (strcmp(current, "reverse") == 0)
attr |= A_REVERSE;
else if (strcmp(current, "blink") == 0)
attr |= A_BLINK;
else if (strcmp(current, "bold") == 0)
attr |= A_BOLD;
#if HAVE_ITALIC
else if (strcmp(current, "italic") == 0)
attr |= A_ITALIC;
#endif
free(current);
offset = i;
length = -1;
}
i++;
length++;
} while (buf[i - 1] != '\0');
*(int *)data = attr;
update_colors();
update_full();
}
static char **id_to_fmt(enum format_id id)
{
switch (id) {
case FMT_CLIPPED_TEXT:
return &clipped_text_format;
case FMT_CURRENT_ALT:
return ¤t_alt_format;
case FMT_PLAYLIST_ALT:
return &list_win_alt_format;
case FMT_TITLE_ALT:
return &window_title_alt_format;
case FMT_TRACKWIN_ALT:
return &track_win_alt_format;
case FMT_CURRENT:
return ¤t_format;
case FMT_HEADING_ALBUM:
return &heading_album_format;
case FMT_HEADING_ARTIST:
return &heading_artist_format;
case FMT_HEADING_PLAYLIST:
return &heading_playlist_format;
case FMT_PLAYLIST:
return &list_win_format;
case FMT_PLAYLIST_VA:
return &list_win_format_va;
case FMT_TITLE:
return &window_title_format;
case FMT_TRACKWIN:
return &track_win_format;
case FMT_TRACKWIN_ALBUM:
return &track_win_album_format;
case FMT_TRACKWIN_VA:
return &track_win_format_va;
case FMT_TREEWIN:
return &tree_win_format;
case FMT_TREEWIN_ARTIST:
return &tree_win_artist_format;
case FMT_STATUSLINE:
return &statusline_format;
default:
die("unhandled format code: %d\n", id);
}
return NULL;
}
static void get_format(void *data, char *buf, size_t size)
{
char **fmtp = data;
strscpy(buf, *fmtp, size);
}
static void set_format(void *data, const char *buf)
{
char **fmtp = data;
if (fmtp == id_to_fmt(FMT_HEADING_ALBUM) || fmtp == id_to_fmt(FMT_HEADING_ARTIST) || fmtp == id_to_fmt(FMT_HEADING_PLAYLIST))
seen_pr1317_fmts = 1;
if (!track_format_valid(buf)) {
error_msg("invalid format string");
return;
}
free(*fmtp);
*fmtp = xstrdup(buf);
update_full();
}
static void set_clipped_text_format(void *data, const char *buf)
{
free(clipped_text_format);
clipped_text_format = clipped_text_internal = xstrdup(buf);
update_full();
}
/* }}} */
#define DN(name) { #name, get_ ## name, set_ ## name, NULL, 0 },
#define DN_FLAGS(name, flags) { #name, get_ ## name, set_ ## name, NULL, flags },
#define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name, 0 },
static const struct {
const char *name;
opt_get_cb get;
opt_set_cb set;
opt_toggle_cb toggle;
unsigned int flags;
} simple_options[] = {
DT(aaa_mode)
DT(auto_reshuffle)
DN_FLAGS(device, OPT_PROGRAM_PATH)
DN(buffer_seconds)
DN(scroll_offset)
DN(rewind_offset)
DT(confirm_run)
DT(continue)
DT(continue_album)
DT(smart_artist_sort)
DT(sort_albums_by_name)
DN(id3_default_charset)
DN(icecast_default_charset)
DN(lib_sort)
DN(output_plugin)
DN(passwd)
DN(pl_sort)
DT(play_library)
DT(play_sorted)
DT(display_artist_sort_name)
DT(repeat)
DT(repeat_current)
DT(replaygain)
DT(replaygain_limit)
DN(replaygain_preamp)
DT(resume)
DT(show_hidden)
DT(auto_expand_albums_follow)
DT(auto_expand_albums_search)
DT(auto_expand_albums_selcur)
DT(auto_hide_playlists_panel)
DT(show_all_tracks)
DT(show_current_bitrate)
DT(show_playback_position)
DT(show_remaining_time)
DT(set_term_title)
DT(shuffle)
DT(follow)
DT(softvol)
DN(softvol_state)
DN_FLAGS(status_display_program, OPT_PROGRAM_PATH)
DT(wrap_search)
DT(skip_track_info)
DT(ignore_duplicates)
DT(mouse)
DT(mpris)
DT(time_show_leading_zero)
DN(lib_add_filter)
DN(start_view)
DT(stop_after_queue)
DN(tree_width_percent)
DN(tree_width_max)
DT(pause_on_output_change)
DN(pl_env_vars)
DT(block_key_paste)
DT(progress_bar)
DT(search_resets_position)
{ NULL, NULL, NULL, NULL, 0 }
};
static const char * const color_names[NR_COLORS] = {
"color_cmdline_bg",
"color_cmdline_fg",
"color_error",
"color_info",
"color_separator",
"color_statusline_bg",
"color_statusline_fg",
"color_statusline_progress_bg",
"color_statusline_progress_fg",
"color_titleline_bg",
"color_titleline_fg",
"color_win_bg",
"color_win_cur",
"color_win_cur_sel_bg",
"color_win_cur_sel_fg",
"color_win_dir",
"color_win_fg",
"color_win_inactive_cur_sel_bg",
"color_win_inactive_cur_sel_fg",
"color_win_inactive_sel_bg",
"color_win_inactive_sel_fg",
"color_win_sel_bg",
"color_win_sel_fg",
"color_win_title_bg",
"color_win_title_fg",
"color_trackwin_album_bg",
"color_trackwin_album_fg",
};
static const char * const attr_names[NR_ATTRS] = {
"color_cmdline_attr",
"color_statusline_attr",
"color_statusline_progress_attr",
"color_titleline_attr",
"color_win_attr",
"color_win_cur_sel_attr",
"color_cur_sel_attr",
"color_win_inactive_cur_sel_attr",
"color_win_inactive_sel_attr",
"color_win_sel_attr",
"color_win_title_attr",
"color_trackwin_album_attr",
"color_win_cur_attr",
};
LIST_HEAD(option_head);
int nr_options = 0;
void option_add(const char *name, const void *data, opt_get_cb get,
opt_set_cb set, opt_toggle_cb toggle, unsigned int flags)
{
struct cmus_opt *opt = xnew(struct cmus_opt, 1);
struct list_head *item;
opt->name = name;
opt->data = (void *)data;
opt->get = get;
opt->set = set;
opt->toggle = toggle;
opt->flags = flags;
item = option_head.next;
while (item != &option_head) {
struct cmus_opt *o = container_of(item, struct cmus_opt, node);
if (strcmp(name, o->name) < 0)
break;
item = item->next;
}
/* add before item */
list_add_tail(&opt->node, item);
nr_options++;
}
struct cmus_opt *option_find(const char *name)
{
struct cmus_opt *opt = option_find_silent(name);
if (opt == NULL)
error_msg("no such option %s", name);
return opt;
}
struct cmus_opt *option_find_silent(const char *name)
{
struct cmus_opt *opt;
list_for_each_entry(opt, &option_head, node) {
if (strcmp(name, opt->name) == 0)
return opt;
}
return NULL;
}
void option_set(const char *name, const char *value)
{
struct cmus_opt *opt = option_find(name);
if (opt)
opt->set(opt->data, value);
}
void options_add(void)
{
int i;
for (i = 0; simple_options[i].name; i++)
option_add(simple_options[i].name, NULL, simple_options[i].get,
simple_options[i].set, simple_options[i].toggle,
simple_options[i].flags);
for (i = 0; i < NR_FMTS; i++)
option_add(str_defaults[i].name, id_to_fmt(i), get_format,
set_format, NULL, 0);
option_find("format_clipped_text")->set = set_clipped_text_format;
for (i = 0; i < NR_COLORS; i++)
option_add(color_names[i], &colors[i], get_color, set_color,
NULL, 0);
for (i = 0; i < NR_ATTRS; i++)
option_add(attr_names[i], &attrs[i], get_attr, set_attr, NULL,
0);
ip_add_options();
op_add_options();
}
static int handle_line(void *data, const char *line)
{
run_command(line);
return 0;
}
int source_file(const char *filename)
{
return file_for_each_line(filename, handle_line, NULL);
}
void options_load(void)
{
char filename[512];
int i;
/* initialize those that can't be statically initialized */
cdda_device = get_default_cdda_device();
for (i = 0; str_defaults[i].name; i++)
option_set(str_defaults[i].name, str_defaults[i].value);
/* reset the flag before loading the autosave */
seen_pr1317_fmts = 0;
/* load autosave config */
snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
if (source_file(filename) == -1) {
char *def = xstrjoin(cmus_data_dir, "/rc");
if (errno != ENOENT)
error_msg("loading %s: %s", filename, strerror(errno));
/* load defaults */
if (source_file(def) == -1)
die_errno("loading %s", def);
free(def);
}
/* replace old format with new one if upgrading past PR #1317 */
if (!seen_pr1317_fmts) {
option_set(str_defaults[FMT_STATUSLINE].name, str_defaults[FMT_STATUSLINE].value);
option_set(str_defaults[FMT_PLAYLIST_ALT].name, str_defaults[FMT_PLAYLIST_ALT].value);
option_set(str_defaults[FMT_TRACKWIN_ALBUM].name, str_defaults[FMT_TRACKWIN_ALBUM].value);
option_set(str_defaults[FMT_TRACKWIN].name, str_defaults[FMT_TRACKWIN].value);
option_set(str_defaults[FMT_TRACKWIN_VA].name, str_defaults[FMT_TRACKWIN_VA].value);
}
/* load optional static config */
snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
if (source_file(filename) == -1) {
if (errno != ENOENT)
error_msg("loading %s: %s", filename, strerror(errno));
}
/* replace the default format_clipped_text symbol in ascii terminal */
if (!using_utf8 && strcmp(clipped_text_format, str_defaults[FMT_CLIPPED_TEXT].value) == 0) {
clipped_text_internal = xstrdup("...");
}
}
void options_exit(void)
{
struct cmus_opt *opt;
struct filter_entry *filt;
char filename_tmp[512];
char filename[512];
FILE *f;
int i;
snprintf(filename_tmp, sizeof(filename_tmp), "%s/autosave.tmp", cmus_config_dir);
f = fopen(filename_tmp, "w");
if (f == NULL) {
warn_errno("creating %s", filename_tmp);
return;
}
/* save options */
list_for_each_entry(opt, &option_head, node) {
char buf[OPTION_MAX_SIZE];
buf[0] = 0;
opt->get(opt->data, buf, OPTION_MAX_SIZE);
fprintf(f, "set %s=%s\n", opt->name, buf);
}
/* save key bindings */
for (i = 0; i < NR_CTXS; i++) {
struct binding *b = key_bindings[i];
while (b) {
fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
b = b->next;
}
}
/* save filters */
list_for_each_entry(filt, &filters_head, node)
fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
fprintf(f, "factivate");
list_for_each_entry(filt, &filters_head, node) {
switch (filt->act_stat) {
case FS_YES:
fprintf(f, " %s", filt->name);
break;
case FS_NO:
fprintf(f, " !%s", filt->name);
break;
}
}
fprintf(f, "\n");
fclose(f);
snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
i = rename(filename_tmp, filename);
if (i)
warn_errno("renaming %s to %s", filename_tmp, filename);
}
struct resume {
enum player_status status;
char *filename;
long int position;
char *lib_filename;
int view;
char *live_filter;
char *browser_dir;
char *marked_pl;
};
static int handle_resume_line(void *data, const char *line)
{
struct resume *resume = data;
char *cmd, *arg;
if (!parse_command(line, &cmd, &arg))
return 0;
if (!arg)
goto out;
if (strcmp(cmd, "status") == 0) {
parse_enum(arg, 0, NR_PLAYER_STATUS, player_status_names, (int *) &resume->status);
} else if (strcmp(cmd, "file") == 0) {
free(resume->filename);
resume->filename = xstrdup(unescape(arg));
} else if (strcmp(cmd, "position") == 0) {
str_to_int(arg, &resume->position);
} else if (strcmp(cmd, "lib_file") == 0) {
free(resume->lib_filename);
resume->lib_filename = xstrdup(unescape(arg));
} else if (strcmp(cmd, "view") == 0) {
parse_enum(arg, 0, NR_VIEWS, view_names, &resume->view);
} else if (strcmp(cmd, "live-filter") == 0) {
free(resume->live_filter);
resume->live_filter = xstrdup(unescape(arg));
} else if (strcmp(cmd, "browser-dir") == 0) {
free(resume->browser_dir);
resume->browser_dir = xstrdup(unescape(arg));
} else if (strcmp(cmd, "active-pl") == 0) {
free(pl_resume_name);
pl_resume_name = xstrdup(unescape(arg));
} else if (strcmp(cmd, "active-pl-row") == 0) {
str_to_int(arg, &pl_resume_row);
} else if (strcmp(cmd, "marked-pl") == 0) {
free(resume->marked_pl);
resume->marked_pl = xstrdup(unescape(arg));
}
free(arg);
out:
free(cmd);
return 0;
}
void resume_load(void)
{
char filename[512];
struct track_info *ti, *old;
struct resume resume = { .status = PLAYER_STATUS_STOPPED, .view = -1 };
snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
if (file_for_each_line(filename, handle_resume_line, &resume) == -1) {
if (errno != ENOENT)
error_msg("loading %s: %s", filename, strerror(errno));
return;
}
if (resume.view >= 0 && resume.view != cur_view)
set_view(resume.view);
if (resume.lib_filename) {
cache_lock();
ti = old = cache_get_ti(resume.lib_filename, 0);
cache_unlock();
if (ti) {
lib_add_track(ti, NULL);
track_info_unref(ti);
lib_store_cur_track(ti);
track_info_unref(ti);
ti = lib_set_track(lib_find_track(ti));
if (ti) {
BUG_ON(ti != old);
track_info_unref(ti);
tree_sel_current(auto_expand_albums_follow);
sorted_sel_current();
}
}
free(resume.lib_filename);
}
if (resume.filename) {
cache_lock();
ti = cache_get_ti(resume.filename, 0);
cache_unlock();
if (ti) {
player_set_file(ti);
if (resume.status != PLAYER_STATUS_STOPPED)
player_seek(resume.position, 0, resume.status == PLAYER_STATUS_PLAYING);
}
free(resume.filename);
}
if (resume.live_filter) {
filters_set_live(resume.live_filter);
free(resume.live_filter);
}
if (resume.browser_dir) {
browser_chdir(resume.browser_dir);
free(resume.browser_dir);
}
if (resume.marked_pl) {
pl_set_marked_pl_by_name(resume.marked_pl);
free(resume.marked_pl);
}
}
void resume_exit(void)
{
char filename_tmp[512];
char filename[512];
const char *pl_name;
struct track_info *ti;
FILE *f;
int rc;
snprintf(filename_tmp, sizeof(filename_tmp), "%s/resume.tmp", cmus_config_dir);
f = fopen(filename_tmp, "w");
if (!f) {
warn_errno("creating %s", filename_tmp);
return;
}
fprintf(f, "status %s\n", player_status_names[player_info.status]);
ti = player_info.ti;
if (ti) {
fprintf(f, "file %s\n", escape(ti->filename));
fprintf(f, "position %d\n", player_info.pos);
}
if (lib_cur_track)
ti = tree_track_info(lib_cur_track);
else
ti = lib_get_cur_stored_track();
if (ti)
fprintf(f, "lib_file %s\n", escape(ti->filename));
fprintf(f, "view %s\n", view_names[cur_view]);
if (lib_live_filter)
fprintf(f, "live-filter %s\n", escape(lib_live_filter));
fprintf(f, "browser-dir %s\n", escape(browser_dir));
if ((pl_name = pl_playing_pl_name())) {
fprintf(f, "active-pl %s\n", escape(pl_name));
fprintf(f, "active-pl-row %d\n", pl_playing_pl_row());
}
fprintf(f, "marked-pl %s\n", escape(pl_marked_pl_name()));
fclose(f);
snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
rc = rename(filename_tmp, filename);
if (rc)
warn_errno("renaming %s to %s", filename_tmp, filename);
}