From: Andre Noll Date: Sun, 10 Jul 2016 10:04:55 +0000 (+0200) Subject: Merge branch 'refs/heads/t/autoplay-fix' X-Git-Tag: v0.5.6~1 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=f2940b2fbde865256bc35c74e3bd624d2ff48d2e;hp=e4efba96ba3f2d77eb75fc27631e590c8be3c879 Merge branch 'refs/heads/t/autoplay-fix' A single commit which was cooking for a month. * refs/heads/t/autoplay-fix: server: Fix --autoplay-delay. --- diff --git a/NEWS.md b/NEWS.md index a0bec53c..dbf6c45b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,8 +19,11 @@ not mentioned here have accumulated and are also part of the release. - New option --priority for para_server and para_audiod. - New mood methods: image_id and lyrics_id. - The manual and this NEWS file have been converted to markdown. +- Support for the compile-time loglevel feature has been removed. - Cleanup of the wma decoder and bitstream code. - Improved wide-character support and fixes related to signal handling. +- para_gui no longer reports 100% playing time at the stream start. +- Opus cleanups. Download: [tarball](./releases/paraslash-git.tar.bz2) diff --git a/audiod.c b/audiod.c index 798142f3..5909859b 100644 --- a/audiod.c +++ b/audiod.c @@ -309,7 +309,7 @@ static int get_play_time_slot_num(void) */ char *get_time_string(void) { - int ret, seconds = 0, length; + int ret, seconds = 0, length = stat_task->length_seconds; struct timeval *tmp, sum, sss, /* server stream start */ rstime, /* receiver start time */ wstime, /* writer start time */ @@ -321,19 +321,18 @@ char *get_time_string(void) if (audiod_status == AUDIOD_OFF) goto empty; - if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) { - if (stat_task->length_seconds) /* paused */ + if (stat_task->server_stream_start.tv_sec == 0) { + if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING) + goto out; /* server is about to change file */ + if (length > 0) /* paused */ return NULL; goto empty; /* stopped */ } - if (audiod_status == AUDIOD_ON && !s) - goto empty; /* * Valid status items and playing, set length and tmp to the stream * start. We use the slot info and fall back to the info from current * status items if no slot info is available. */ - length = stat_task->length_seconds; tmp = &stat_task->server_stream_start; if (s && s->wns && s->wns[0].btrn) { /* writer active in this slot */ btr_get_node_start(s->wns[0].btrn, &wstime); @@ -352,7 +351,7 @@ char *get_time_string(void) tv_diff(tmp, &stat_task->sa_time_diff, &sss); else tv_add(tmp, &stat_task->sa_time_diff, &sss); - if (!s || !s->wns || !s->wns[0].btrn) { + if (!s || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) { struct timeval diff; tv_diff(now, &sss, &diff); seconds = diff.tv_sec + stat_task->offset_seconds; @@ -364,7 +363,8 @@ char *get_time_string(void) if (s->receiver_node->btrn) { btr_get_node_start(s->receiver_node->btrn, &rstime); ret = tv_diff(&rstime, &sss, &rskip); - if (ret > 0) { /* audiod was started in the middle of the stream */ + if (ret > 0 && rskip.tv_sec > 2) { + /* audiod was started in the middle of the stream */ tv_add(&wtime, &rskip, &sum); seconds += sum.tv_sec; } else @@ -1230,10 +1230,15 @@ static void close_slot(int slot_num) static void close_unused_slots(void) { int i; + bool dump = false; FOR_EACH_SLOT(i) - if (must_close_slot(i)) + if (must_close_slot(i)) { close_slot(i); + dump = true; + } + if (dump) + audiod_status_dump(true); } /* @@ -1278,6 +1283,7 @@ static void start_stop_decoders(void) open_writers(sl); activate_grab_clients(&sched); btr_log_tree(sl->receiver_node->btrn, LL_NOTICE); + audiod_status_dump(true); } static void status_pre_select(struct sched *s, void *context) diff --git a/configure.ac b/configure.ac index 2e4b42ce..953a9d21 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,7 @@ AC_DEFUN([LIB_SUBST_FLAGS], [ AC_SUBST($1_ldflags) ]) +AC_USE_SYSTEM_EXTENSIONS AC_C_BIGENDIAN() AC_PATH_PROG([GENGETOPT], [gengetopt]) @@ -197,7 +198,6 @@ AC_SUBST(nsl_ldflags) ########################################################################### ucred AC_MSG_CHECKING(for struct ucred) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #define _GNU_SOURCE #include #include ]], [[ @@ -1147,7 +1147,6 @@ audio format handlers: $audio_format_handlers filters: $(echo $filters) writers: $writers -para_fade: $build_fade para_server: $build_server para_gui: $build_gui para_fade: $build_fade diff --git a/daemon.c b/daemon.c index 478b0f47..945c5a39 100644 --- a/daemon.c +++ b/daemon.c @@ -74,7 +74,7 @@ static void daemon_set_log_color_or_die(char const *arg) color_parse_or_die(p, me->log_colors[ll]); return; err: - PARA_EMERG_LOG("%s: color syntax error\n", arg); + PARA_EMERG_LOG("%s: invalid color argument\n", arg); exit(EXIT_FAILURE); } @@ -122,7 +122,7 @@ void daemon_init_colors_or_die(int color_arg, int color_arg_auto, * * \param logfile_name The full path of the logfile. */ -void daemon_set_logfile(char *logfile_name) +void daemon_set_logfile(const char *logfile_name) { free(me->logfile_name); me->logfile_name = NULL; @@ -135,7 +135,7 @@ void daemon_set_logfile(char *logfile_name) * * \param loglevel The smallest level that should be logged. */ -void daemon_set_loglevel(char *loglevel) +void daemon_set_loglevel(const char *loglevel) { int ret = get_loglevel_by_name(loglevel); diff --git a/daemon.h b/daemon.h index 621420a4..e97678d6 100644 --- a/daemon.h +++ b/daemon.h @@ -10,9 +10,9 @@ void daemon_drop_privileges_or_die(const char *username, const char *groupname); void daemon_set_start_time(void); time_t daemon_get_uptime(const struct timeval *current_time); __malloc char *daemon_get_uptime_str(const struct timeval *current_time); -void daemon_set_logfile(char *logfile_name); +void daemon_set_logfile(const char *logfile_name); void daemon_set_flag(unsigned flag); -void daemon_set_loglevel(char *loglevel); +void daemon_set_loglevel(const char *loglevel); void daemon_init_colors_or_die(int color_arg, int color_arg_auto, int color_arg_no, bool logfile_given, char **log_color_argv, int argc); diff --git a/error.h b/error.h index 830d0f8d..3d58e630 100644 --- a/error.h +++ b/error.h @@ -410,7 +410,7 @@ extern const char **para_errlist[]; PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \ PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \ PARA_ERROR(ID3_DETACH, "could not detach id3 frame"), \ - PARA_ERROR(ID3_ATTACH, "could not atttach id3 frame"), \ + PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \ PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \ PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \ diff --git a/filter.h b/filter.h index 31eedcc0..686776a0 100644 --- a/filter.h +++ b/filter.h @@ -140,5 +140,4 @@ static inline void write_int16_host_endian(char *buf, int val) DECLARE_FILTER_INITS -/** The filter array, one structure for each supported filter. */ const struct filter *filter_get(int filter_num); diff --git a/filter_common.c b/filter_common.c index 099d056e..6fb9bd96 100644 --- a/filter_common.c +++ b/filter_common.c @@ -25,6 +25,16 @@ /** The array of supported filters. */ static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY}; +/** + * Obtain a reference to a filter structure. + * + * \param filter_num Between zero and NUM_SUPPORTED_FILTERS, inclusively. + * + * \return Pointer to the filter identified by the given filter number. + * + * It is a fatal error if the given number is out of range. In this case + * the function aborts. + */ const struct filter *filter_get(int filter_num) { assert(filter_num >= 0); diff --git a/gcc-compat.h b/gcc-compat.h index dd6afe1d..729e3ae1 100644 --- a/gcc-compat.h +++ b/gcc-compat.h @@ -21,11 +21,5 @@ #define __printf_2_3 __printf(2,3) #define __printf_3_4 __printf(3,4) -# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) -# define __must_check __attribute__ ((warn_unused_result)) -# else -# define __must_check /* no warn_unused_result */ -# endif - +#define __must_check __attribute__ ((warn_unused_result)) #define _static_inline_ static inline - diff --git a/grab_client.c b/grab_client.c index 0ef1c15f..926f4792 100644 --- a/grab_client.c +++ b/grab_client.c @@ -14,7 +14,6 @@ #include "sched.h" #include "ggo.h" #include "buffer_tree.h" -#include "filter.h" #include "grab_client.h" #include "audiod.h" #include "error.h" diff --git a/interactive.c b/interactive.c index ce48af32..b72148cc 100644 --- a/interactive.c +++ b/interactive.c @@ -28,6 +28,8 @@ struct i9e_private { int num_columns; int num_key_bindings; char empty_line[1000]; + char key_sequence[32]; + unsigned key_sequence_length; struct task *task; struct btr_node *stdout_btrn; bool last_write_was_status; @@ -328,8 +330,26 @@ static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context) ret = 0; if (i9ep->caught_sigint) goto rm_btrn; - while (input_available()) + while (input_available()) { + if (i9ep->stdout_btrn) { + unsigned len = i9ep->key_sequence_length; + assert(len < sizeof(i9ep->key_sequence) - 1); + buf = i9ep->key_sequence + len; + ret = read(i9ep->ici->fds[0], buf, 1); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto rm_btrn; + } + ret = -E_I9E_EOF; + if (ret == 0) + goto rm_btrn; + buf[1] = '\0'; + i9ep->key_sequence_length++; + rl_stuff_char((int)(unsigned char)*buf); + } rl_callback_read_char(); + ret = 0; + } if (!i9ep->stdout_btrn) goto out; ret = btr_node_status(i9ep->stdout_btrn, 0, BTR_NT_LEAF); @@ -417,13 +437,26 @@ static int dispatch_key(__a_unused int count, __a_unused int key) { int i, ret; +again: + if (i9ep->key_sequence_length == 0) + return 0; for (i = i9ep->num_key_bindings - 1; i >= 0; i--) { - if (strcmp(rl_executing_keyseq, i9ep->ici->bound_keyseqs[i])) + if (strcmp(i9ep->key_sequence, i9ep->ici->bound_keyseqs[i])) continue; + i9ep->key_sequence[0] = '\0'; + i9ep->key_sequence_length = 0; ret = i9ep->ici->key_handler(i); return ret < 0? ret : 0; } - assert(0); + PARA_WARNING_LOG("ignoring key %d\n", i9ep->key_sequence[0]); + /* + * We received an undefined key sequence. Throw away the first byte, + * and try to parse the remainder. + */ + memmove(i9ep->key_sequence, i9ep->key_sequence + 1, + i9ep->key_sequence_length); /* move also terminating zero byte */ + i9ep->key_sequence_length--; + goto again; } /** @@ -440,6 +473,7 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s) { int ret; + memset(i9ep, 0, sizeof(struct i9e_private)); if (!isatty(ici->fds[0])) return -E_I9E_SETUPTERM; ret = mark_fd_nonblocking(ici->fds[0]); @@ -467,10 +501,17 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s) if (ici->bound_keyseqs) { char *seq; int i; - /* bind each key sequence to the our dispatcher */ - for (i = 0; (seq = ici->bound_keyseqs[i]); i++) - rl_generic_bind(ISFUNC, seq, (char *)dispatch_key, - i9ep->bare_km); + /* bind each key sequence to our dispatcher */ + for (i = 0; (seq = ici->bound_keyseqs[i]); i++) { + if (strlen(seq) >= sizeof(i9ep->key_sequence) - 1) { + PARA_WARNING_LOG("ignoring overlong key %s\n", + seq); + continue; + } + if (rl_bind_keyseq_in_map(seq, + dispatch_key, i9ep->bare_km) != 0) + PARA_WARNING_LOG("could not bind #%d: %s\n", i, seq); + } i9ep->num_key_bindings = i; } if (ici->history_file) diff --git a/m4/gengetopt/server.m4 b/m4/gengetopt/server.m4 index 6aca7bac..48e7a1f4 100644 --- a/m4/gengetopt/server.m4 +++ b/m4/gengetopt/server.m4 @@ -284,13 +284,12 @@ int typestr = "num" optional default = "4" details = " - This value must be larger than the value given for above - dccp-data-slices-per-group above. The difference being the - number of redundant slices per group, i.e. the number of - data packets that may be lost without causing interruptions - of the resulting audio stream. + This value must be larger than the value of the argument to + --dccp-data-slices-per-group. The difference of the two values is + the number of redundant slices, that is, the number of slices which + may be lost without causing interruptions in the audio stream. - Increase this value if for lossy networks. + Increase this value if you are on a lossy network. " #################### diff --git a/m4/gengetopt/write.m4 b/m4/gengetopt/write.m4 index 83e8bcab..6667cb8c 100644 --- a/m4/gengetopt/write.m4 +++ b/m4/gengetopt/write.m4 @@ -9,12 +9,12 @@ option "writer" w #~~~~~~~~~~~~~~~~ "select stream writer" string typestr="name" -default="alsa (file if alsa is unsupported)" optional multiple details=" - May be give multiple times. The same writer may be specified - more than once. + May be given multiple times, and the same writer may be specified more + than once. If this option is not given, the first supported writer + is started. The list of supported writers is shown in the help output. " include(channels.m4) diff --git a/mp3_afh.c b/mp3_afh.c index 74d65fff..382b0e90 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -300,6 +300,9 @@ static int replace_tags(struct id3_tag *id3_t, struct taginfo *tags) static void free_tag(struct id3_tag *id3_t) { int i, j; + + if (!id3_t) + return; for (i = 0; i < id3_t->nframes; i++) { struct id3_frame *fr = id3_t->frames[i]; for (j = 0; j < fr->nfields; j++) { @@ -337,29 +340,27 @@ static int mp3_rewrite_tags(const char *map, size_t mapsize, if (v2_tag) { PARA_NOTICE_LOG("replacing id3v2 tag\n"); old_v2size = v2_tag->paddedsize; - } else if (!v1_tag) { - PARA_NOTICE_LOG("no id3 tags found, adding id3v2 tag\n"); + } else { + PARA_NOTICE_LOG("adding id3v2 tag\n"); v2_tag = id3_tag_new(); assert(v2_tag); } - if (v2_tag) { - /* - * Turn off all options to avoid creating an extended header. - * id321 does not understand it. - */ - id3_tag_options(v2_tag, ~0U, 0); - ret = replace_tags(v2_tag, tags); - if (ret < 0) - goto out; - new_v2size = id3_tag_render(v2_tag, NULL); - v2_buffer = para_malloc(new_v2size); - id3_tag_render(v2_tag, v2_buffer); - PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size); - ret = write_all(fd, (char *)v2_buffer, new_v2size); - free(v2_buffer); - if (ret < 0) - goto out; - } + /* + * Turn off all options to avoid creating an extended header. id321 + * does not understand it. + */ + id3_tag_options(v2_tag, ~0U, 0); + ret = replace_tags(v2_tag, tags); + if (ret < 0) + goto out; + new_v2size = id3_tag_render(v2_tag, NULL); + v2_buffer = para_malloc(new_v2size); + id3_tag_render(v2_tag, v2_buffer); + PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size); + ret = write_all(fd, (char *)v2_buffer, new_v2size); + free(v2_buffer); + if (ret < 0) + goto out; data_sz = mapsize - old_v2size; if (v1_tag && data_sz >= 128) data_sz -= 128; @@ -372,10 +373,8 @@ static int mp3_rewrite_tags(const char *map, size_t mapsize, ret = write_all(fd, (char *)v1_buffer, 128); } out: - if (v1_tag) - free_tag(v1_tag); - if (v2_tag) - free_tag(v2_tag); + free_tag(v1_tag); + free_tag(v2_tag); return ret; } diff --git a/net.c b/net.c index 42418e5f..1b142a3c 100644 --- a/net.c +++ b/net.c @@ -6,11 +6,7 @@ /** \file net.c Networking-related helper functions. */ -/* - * Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined in order - * to obtain the definition of the ucred structure. - */ -#define _GNU_SOURCE +#include "para.h" #include #include @@ -32,7 +28,6 @@ #include -#include "para.h" #include "error.h" #include "net.h" #include "string.h" diff --git a/opus_common.c b/opus_common.c index 67a8841e..f4e18df9 100644 --- a/opus_common.c +++ b/opus_common.c @@ -29,8 +29,6 @@ * handler. */ -#include - #include "para.h" #include "error.h" #include "opus_common.h" @@ -53,7 +51,7 @@ static int read_chars(struct packet *p, unsigned char *str, int nb_chars) return 1; } -static int read_uint32(struct packet *p, ogg_uint32_t *val) +static int read_uint32(struct packet *p, uint32_t *val) { if (p->pos > p->maxlen - 4) return 0; @@ -62,7 +60,7 @@ static int read_uint32(struct packet *p, ogg_uint32_t *val) return 1; } -static int read_uint16(struct packet *p, ogg_uint16_t *val) +static int read_uint16(struct packet *p, uint16_t *val) { if (p->pos > p->maxlen - 2) return 0; @@ -89,7 +87,6 @@ int opus_parse_header(const char *packet, int len, struct opus_header *h) char str[9]; struct packet p; unsigned char ch, channel_mapping; - ogg_uint16_t shortval; p.data = packet; p.maxlen = len; @@ -113,16 +110,14 @@ int opus_parse_header(const char *packet, int len, struct opus_header *h) if (h->channels == 0) return -E_OPUS_HEADER; - if (!read_uint16(&p, &shortval)) + if (!read_uint16(&p, &h->preskip)) return -E_OPUS_HEADER; - h->preskip = shortval; if (!read_uint32(&p, &h->input_sample_rate)) return -E_OPUS_HEADER; - if (!read_uint16(&p, &shortval)) + if (!read_uint16(&p, &h->gain)) return -E_OPUS_HEADER; - h->gain = (short)shortval; if (!read_chars(&p, &ch, 1)) return -E_OPUS_HEADER; diff --git a/opus_common.h b/opus_common.h index 2bcf5919..2160f151 100644 --- a/opus_common.h +++ b/opus_common.h @@ -16,11 +16,11 @@ struct opus_header { /** 1..255 */ int channels; /** Number of bytes to skip from the beginning. */ - int preskip; + uint16_t preskip; /** Sample rate of the input stream, used by the audio format handler. */ - ogg_uint32_t input_sample_rate; + uint32_t input_sample_rate; /** In dB, should be zero whenever possible. */ - int gain; + uint16_t gain; /** Number of logical streams (usually 1). */ int nb_streams; /** Number of streams to decode as 2 channel streams. */ diff --git a/opusdec_filter.c b/opusdec_filter.c index 6a93f41f..28222985 100644 --- a/opusdec_filter.c +++ b/opusdec_filter.c @@ -70,7 +70,7 @@ struct opusdec_context { ogg_page ogg_page; bool eos; int channels; - int preskip; + uint16_t preskip; bool have_opus_stream; bool have_more; ogg_int32_t opus_serialno; @@ -142,9 +142,10 @@ static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx) static void opusdec_add_output(short *pcm, int frames_available, struct btr_node *btrn, struct opusdec_context *ctx) { - int tmp_skip, num_frames, bytes; + int num_frames, bytes; + uint16_t tmp_skip; - tmp_skip = PARA_MIN(ctx->preskip, frames_available); + tmp_skip = PARA_MIN((int)ctx->preskip, frames_available); ctx->preskip -= tmp_skip; num_frames = frames_available - tmp_skip; if (num_frames <= 0) diff --git a/para.h b/para.h index f08d43a8..12d23639 100644 --- a/para.h +++ b/para.h @@ -240,49 +240,12 @@ enum sample_format {SAMPLE_FORMATS}; /** Number of all loglevels. */ #define NUM_LOGLEVELS 7 -/** Log messages with lower priority than that will not be compiled in. */ -#define COMPILE_TIME_LOGLEVEL 0 - /** \cond log */ -#if LL_DEBUG >= COMPILE_TIME_LOGLEVEL #define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_DEBUG_LOG(...) do {;} while (0) -#endif - -#if LL_INFO >= COMPILE_TIME_LOGLEVEL #define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_INFO_LOG(...) do {;} while (0) -#endif - -#if LL_NOTICE >= COMPILE_TIME_LOGLEVEL #define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_NOTICE_LOG(...) do {;} while (0) -#endif - -#if LL_WARNING >= COMPILE_TIME_LOGLEVEL #define PARA_WARNING_LOG(f,...) para_log(LL_WARNING, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_WARNING_LOG(...) do {;} while (0) -#endif - -#if LL_ERROR >= COMPILE_TIME_LOGLEVEL #define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_ERROR_LOG(...) do {;} while (0) -#endif - -#if LL_CRIT >= COMPILE_TIME_LOGLEVEL #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_CRIT_LOG(...) do {;} while (0) -#endif - -#if LL_EMERG >= COMPILE_TIME_LOGLEVEL #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) -#else -#define PARA_EMERG_LOG(...) -#endif /** \endcond log */ diff --git a/play.c b/play.c index 59a4cec8..a7ce563b 100644 --- a/play.c +++ b/play.c @@ -13,7 +13,6 @@ #include "para.h" #include "list.h" #include "play.cmdline.h" -#include "filter.cmdline.h" #include "error.h" #include "ggo.h" #include "buffer_tree.h" @@ -592,6 +591,28 @@ static char *get_key_map_seq(int key) get_internal_key_map_seq(key) : get_user_key_map_seq(key); } +static char *get_key_map_seq_safe(int key) +{ + const char hex[] = "0123456789abcdef"; + char *seq = get_key_map_seq(key), *sseq; + size_t n, len = strlen(seq); + + if (len == 1 && isprint(*seq)) + return seq; + sseq = para_malloc(2 + 2 * len + 1); + sseq[0] = '0'; + sseq[1] = 'x'; + for (n = 0; n < len; n++) { + uint8_t val = (seq[n] & 0xf0) >> 4; + sseq[2 + 2 * n] = hex[val]; + val = seq[n] & 0xf; + sseq[2 + 2 * n + 1] = hex[val]; + } + free(seq); + sseq[2 + 2 * n] = '\0'; + return sseq; +} + static inline char *get_internal_key_map_cmd(int key) { return para_strdup(default_commands[get_internal_key_map_idx(key)]); @@ -710,7 +731,7 @@ static int com_help(struct play_task *pt, int argc, char **argv) FOR_EACH_MAPPED_KEY(i) { bool internal = is_internal_key(i); int idx = get_key_map_idx(i); - char *seq = get_key_map_seq(i); + char *seq = get_key_map_seq_safe(i); char *cmd = get_key_map_cmd(i); sz = xasprintf(&buf, "%s key #%d: %s -> %s\n", diff --git a/string.c b/string.c index e731bb49..86e77538 100644 --- a/string.c +++ b/string.c @@ -6,19 +6,16 @@ /** \file string.c Memory allocation and string handling functions. */ -#define _GNU_SOURCE +#include "para.h" #include #include /* uname() */ - #include #include - #include #include #include -#include "para.h" #include "string.h" #include "error.h" diff --git a/user_list.h b/user_list.h index f4e7661b..3a77e98f 100644 --- a/user_list.h +++ b/user_list.h @@ -7,14 +7,19 @@ /** \file user_list.h exported functions from user_list.c */ /** - * permission flags that can be set individually for any server command + * Flags for server commands and user permissions. * - * - AFS_READ: read-only command of the audio file selector - * - AFS_WRITE: command changes state of the audio file selector - * - VSS_READ: command reads information about the current audio stream - * - VSS_WRITE: command changes the current audio stream + * For each command, zero or more of these flags are ored to define the command + * permissions. A user is allowed to run the command if and only if all command + * permission flags are set for the user in the server.users config file which + * is read at server startup. */ -enum {AFS_READ = 1, AFS_WRITE = 2, VSS_READ = 4, VSS_WRITE = 8}; +enum server_command_permissions { + AFS_READ = 1, /** Read-only operation on the AFS database. */ + AFS_WRITE = 2, /** Read-write operation on the AFS database. */ + VSS_READ = 4, /** Read-only operation on the virtual streaming system. */ + VSS_WRITE = 8 /** Read-write operation on the virtual streaming system. */ +}; /** * data needed to authenticate the user diff --git a/version.c b/version.c index 69567561..6cbb0534 100644 --- a/version.c +++ b/version.c @@ -43,7 +43,7 @@ const char *version_text(const char *pfx) static char buf[512]; snprintf(buf, sizeof(buf) - 1, "%s\n" - "Copyright (C) 2002-2015 Andre Noll\n" + "Copyright (C) 2002-2016 Andre Noll\n" "This is free software with ABSOLUTELY NO WARRANTY." " See COPYING for details.\n" "Report bugs to .\n" diff --git a/web/manual.md b/web/manual.md index b154b391..15386134 100644 --- a/web/manual.md +++ b/web/manual.md @@ -754,8 +754,8 @@ these commands. The image, lyrics, moods and playlists tables are all blob tables. Blob tables consist of three columns each: The identifier which is -a positive non-negative number that is auto-incremented, the name -(an arbitrary string) and the content (the blob). +a positive number that is auto-incremented, the name (an arbitrary +string) and the content (the blob). All blob tables support the same set of actions: cat, ls, mv, rm and add. Of course, _add_ is used for adding new blobs to the table