Merge branch 'refs/heads/t/autoplay-fix'
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 10 Jul 2016 10:04:55 +0000 (12:04 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 10 Jul 2016 10:04:55 +0000 (12:04 +0200)
A single commit which was cooking for a month.

* refs/heads/t/autoplay-fix:
  server: Fix --autoplay-delay.

24 files changed:
NEWS.md
audiod.c
configure.ac
daemon.c
daemon.h
error.h
filter.h
filter_common.c
gcc-compat.h
grab_client.c
interactive.c
m4/gengetopt/server.m4
m4/gengetopt/write.m4
mp3_afh.c
net.c
opus_common.c
opus_common.h
opusdec_filter.c
para.h
play.c
string.c
user_list.h
version.c
web/manual.md

diff --git a/NEWS.md b/NEWS.md
index a0bec53..dbf6c45 100644 (file)
--- 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)
 
index 798142f..5909859 100644 (file)
--- 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)
index 2e4b42c..953a9d2 100644 (file)
@@ -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 <sys/types.h>
        #include <sys/socket.h>
 ]], [[
@@ -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
index 478b0f4..945c5a3 100644 (file)
--- 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);
 
index 621420a..e97678d 100644 (file)
--- 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 830d0f8..3d58e63 100644 (file)
--- 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"), \
 
index 31eedcc..686776a 100644 (file)
--- 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);
index 099d056..6fb9bd9 100644 (file)
 /** 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);
index dd6afe1..729e3ae 100644 (file)
 #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
-
index 0ef1c15..926f479 100644 (file)
@@ -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"
index ce48af3..b72148c 100644 (file)
@@ -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)
index 6aca7ba..48e7a1f 100644 (file)
@@ -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.
 "
 
 ####################
index 83e8bca..6667cb8 100644 (file)
@@ -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)
index 74d65ff..382b0e9 100644 (file)
--- 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 42418e5..1b142a3 100644 (file)
--- 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 <netinet/in.h>
 #include <arpa/inet.h>
@@ -32,7 +28,6 @@
 
 #include <regex.h>
 
-#include "para.h"
 #include "error.h"
 #include "net.h"
 #include "string.h"
index 67a8841..f4e18df 100644 (file)
@@ -29,8 +29,6 @@
  * handler.
  */
 
-#include <ogg/ogg.h>
-
 #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;
index 2bcf591..2160f15 100644 (file)
@@ -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. */
index 6a93f41..2822298 100644 (file)
@@ -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 f08d43a..12d2363 100644 (file)
--- 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 59a4cec..a7ce563 100644 (file)
--- 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",
index e731bb4..86e7753 100644 (file)
--- 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 <pwd.h>
 #include <sys/utsname.h> /* uname() */
-
 #include <string.h>
 #include <regex.h>
-
 #include <langinfo.h>
 #include <wchar.h>
 #include <wctype.h>
 
-#include "para.h"
 #include "string.h"
 #include "error.h"
 
index f4e7661..3a77e98 100644 (file)
@@ -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
index 6956756..6cbb053 100644 (file)
--- 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 <maan@tuebingen.mpg.de>.\n"
index b154b39..1538613 100644 (file)
@@ -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