]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'refs/heads/t/sha256'
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 21 Feb 2022 14:52:42 +0000 (15:52 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Mon, 21 Feb 2022 14:53:52 +0000 (15:53 +0100)
A couple of changes which start to eliminate the use of sha1 in favor
of sha256. This series is only the first step, though, as we need to
keep sha1 for the time being to provide backward compatibility.

Cooking for four months.

* refs/heads/t/sha256:
  manual: Avoid sha1.
  upgrade_db: Add copyright and purpose to upgrade_db.c.
  web: Add link to the para_upgrade_db(1) man page.
  afs: Switch to sha256 and change default database path.
  Add para_upgrade_db.
  Use sha256 for the challenge response.
  Introduce hash2 (sha256).
  Assume sideband and aes_ctr128 are always supported/requested.

40 files changed:
Makefile.in
Makefile.real
NEWS.md
afs.c
alsa_write.c
ao_write.c
audiod.c
buffer_tree.c
chunk_queue.c
client.c
close_on_fork.c
command.c
configure.ac
daemon.c
error.h
interactive.c
interactive.h
list.h
m4/lls/server_cmd.suite.m4
mm.c [deleted file]
mm.h [deleted file]
mood.c
mp.c
mp.h
net.c
oss_write.c
play.c
sched.c
send_common.c
string.h
sync_filter.c
udp_send.c
vss.c
web/documentation.in.html
web/download.in.html
web/manual.md
write.h
write_common.c
yy/mp.lex
yy/mp.y

index f9efa7deaf5d621c87c9505549bf4fc58f771e15..c618561d4dfeb18ebeb31f93a90ac69e32f84c63 100644 (file)
@@ -68,4 +68,6 @@ curses_ldflags := @curses_ldflags@
 crypto_ldflags := @crypto_ldflags@
 iconv_ldflags := @iconv_ldflags@
 
+ENABLE_UBSAN := @ENABLE_UBSAN@
+
 include Makefile.real
index 140d8f311b81029a5fa01023ecfb33c0a94a2d19..6e8084d542b8888e2a838671b6a9be845ac2cae2 100644 (file)
@@ -20,7 +20,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2021
+COPYRIGHT_YEAR := 2022
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -131,6 +131,11 @@ STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 
+ifeq ($(ENABLE_UBSAN), yes)
+       STRICT_CFLAGS += -fsanitize=undefined
+       LDFLAGS += -lubsan
+endif
+
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
        CPPFLAGS += -Wunused-macros
diff --git a/NEWS.md b/NEWS.md
index 3e3e7abfda28365cba7abfa2ead1c2c408d75801..9e67364d9d6c2bb4dd8390e4a5ee5a524bbe5157 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,80 @@
 NEWS
 ====
 
+---------------------------------------
+0.7.0 (to be announced) "seismic orbit"
+---------------------------------------
+
+- Starting with paraslash-0.7.0, the sha256 hash value of each known
+  audio file is stored in the database while older versions employed the
+  sha1 hash algorithm which has been considered insecure since 2005
+  and should no longer be used today. The switch from sha1 to sha256
+  requires users to upgrade their database using the new para_upgrade_db
+  program, followed by re-adding all files to recompute the hashes. With
+  this approach all metadata stored in the database (last played date,
+  num played value, moods, playlists, attributes etc.) are kept. An
+  simpler alternative is to start over from scratch by running the
+  "init" command but this will lose these metadata.
+- Server and client now hash the session keys with sha256 rather
+  than sha1 during the initial handshake. This feature is optional and
+  backwards compatible: old clients can still connect to a new server
+  (using sha1). Also new clients can connect to an old server (again
+  using sha1).
+- The new "duration" keyword of the mood grammar makes it possible to
+  impose a constraint on the duration of the admissible files.
+- The long deprecated version 1 mood syntax is no longer supported.
+- Paraslash writers handle early end-of-file more gracefully.
+- The alsa writer no longer warns about spurious underruns.
+- The score formula  of the audio file selector has been reworked.
+- Cleanups of the doubly linked lists code.
+- New option for configure: --enable-ubsan to detect and report undefined
+  behaviour.
+- The "tasks" server command has been removed.
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.xz)
+
+--------------------------------------
+0.6.4 (2021-11-04) "fuzzy calibration"
+--------------------------------------
+
+This point release contains a fair number of fixes but no new features.
+This marks the end of the 0.6 development, although paraslash-0.6 will
+still be supported for some time and subsequent maintenance releases
+may follow.
+
+- The udp sender no longer crashes when empty chunks are encountered.
+- Fix a double-free bug in the exit path of the server.
+- The "jmp" command now errors out when given a negative percentage.
+- A fix for a bug in para_afh which triggered on the attempt to modify
+  the tags of an invalid mp4 file.
+- A memory leak in para_afh has been fixed.
+- The udp sender no longer sends multiple EOF packets.
+- Some gcc warnings have been silenced.
+- Minor log level adjustments and documentation improvements.
+
+Downloads:
+[tarball](./releases/paraslash-0.6.4.tar.xz),
+[signature](./releases/paraslash-0.6.4.tar.xz.asc)
+
+---------------------------------------
+0.5.9 (2021-11-04) "reversed dimension"
+---------------------------------------
+
+This release contains a few important fixes which have accumulated in
+the maint branch. The paraslash-0.5.x series has now reached its end
+of life and will no longer be supported. All users should upgrade to
+a more recent version at this point.
+
+- Fix an issue with the bash completion script.
+- Initialize the random seed also when using libgrypt.
+- Fix some compiler warnings in the resample filter
+- Don't return spurious errors from the ff server command.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.9.tar.bz2),
+[signature](./releases/paraslash-0.5.9.tar.bz2.asc)
+
 ----------------------------------------
 0.6.3 (2021-02-18) "generalized activity"
 -----------------------------------------
diff --git a/afs.c b/afs.c
index c1393711dcff4699865d3d87580ca1c64b76c8fa..e1639cc7c1447e0e9f31deff65f79fab1e3239e3 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -981,7 +981,7 @@ __noreturn void afs_init(int socket_fd)
        int i, ret;
 
        register_signal_task(&s);
-       INIT_LIST_HEAD(&afs_client_list);
+       init_list_head(&afs_client_list);
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
index bc06fc315bc51fb3fc1fa9729ae429b47a70d4b4..bbbf8b650ac86c4952ac46f22c166583018bfb88 100644 (file)
@@ -240,6 +240,8 @@ static void alsa_close(struct writer_node *wn)
 
        if (!pad)
                return;
+       if (!pad->handle)
+               goto free_pad;
        /*
         * It's OK to have a blocking operation here because we already made
         * sure that the PCM output buffer is (nearly) empty.
@@ -248,6 +250,7 @@ static void alsa_close(struct writer_node *wn)
        snd_pcm_drain(pad->handle);
        snd_pcm_close(pad->handle);
        snd_config_update_free_global();
+free_pad:
        free(pad);
 }
 
@@ -294,21 +297,23 @@ again:
                if (bytes == 0) /* no data available */
                        return 0;
                pad = wn->private_data = para_calloc(sizeof(*pad));
-               get_btr_sample_rate(btrn, &val);
+               ret = get_btr_sample_rate(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_rate = val;
-               get_btr_channels(btrn, &val);
+               ret = get_btr_channels(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->channels = val;
-               get_btr_sample_format(btrn, &val);
+               ret = get_btr_sample_format(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_format = get_alsa_pcm_format(val);
-
                PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
                ret = alsa_init(wn);
-               if (ret < 0) {
-                       free(wn->private_data);
-                       wn->private_data = NULL;
+               if (ret < 0)
                        goto err;
-               }
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
@@ -326,7 +331,11 @@ again:
                goto again;
        }
        if (frames == -EPIPE) {
-               PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
+               snd_pcm_status_t *status;
+               snd_pcm_status_malloc(&status);
+               if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN)
+                       PARA_WARNING_LOG("underrun\n");
+               snd_pcm_status_free(status);
                snd_pcm_prepare(pad->handle);
                return 0;
        }
index 447dea84c00a468330fe8229bfd70b74292264bd..037b92993325539552ddd625b881a843aa57c5a9 100644 (file)
@@ -357,9 +357,15 @@ static int aow_post_select(__a_unused struct sched *s, void *context)
                        goto remove_btrn;
                if (ret == 0)
                        return 0;
-               get_btr_sample_rate(wn->btrn, &rate);
-               get_btr_channels(wn->btrn, &ch);
-               get_btr_sample_format(wn->btrn, &format);
+               ret = get_btr_sample_rate(wn->btrn, &rate);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_channels(wn->btrn, &ch);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_sample_format(wn->btrn, &format);
+               if (ret < 0)
+                       goto remove_btrn;
                ret = aow_init(wn, rate, ch, format);
                if (ret < 0)
                        goto remove_btrn;
index 88599c3fa297337b6a3f598b31f92d6e6b4b0993..7293285ef9687ee1c35fd46bfb735a73f6870ec0 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -445,7 +445,8 @@ static void close_receiver(int slot_num)
        task_reap(&s->receiver_node->task);
        free(s->receiver_node);
        s->receiver_node = NULL;
-       stat_task->current_audio_format_num = -1;
+       if (audiod_status == AUDIOD_ON)
+               stat_task->current_audio_format_num = -1;
        tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
                &a->restart_barrier);
 }
index 8a3175133e69aa57eff7da5155341cbf05532d03..f0d2002d13c28b31c17f7130a4f3c34fc858bd58 100644 (file)
@@ -270,8 +270,8 @@ struct btr_node *btr_new_node(struct btr_node_description *bnd)
        btrn->context = bnd->context;
        btrn->start.tv_sec = 0;
        btrn->start.tv_usec = 0;
-       INIT_LIST_HEAD(&btrn->children);
-       INIT_LIST_HEAD(&btrn->input_queue);
+       init_list_head(&btrn->children);
+       init_list_head(&btrn->input_queue);
        if (!bnd->child) {
                if (bnd->parent) {
                        list_add_tail(&btrn->node, &bnd->parent->children);
index 08f57e9d1ecf9bae1cadead0757fe29418c446c3..cf74cc336de25caa593b198bcfd79a3166996381 100644 (file)
@@ -131,7 +131,7 @@ int cq_get(struct queued_chunk *qc, const char **buf, size_t *num_bytes)
 struct chunk_queue *cq_new(size_t max_pending)
 {
        struct chunk_queue *cq = para_malloc(sizeof(*cq));
-       INIT_LIST_HEAD(&cq->q);
+       init_list_head(&cq->q);
        cq->max_pending = max_pending;
        cq->num_pending = 0;
        return cq;
index 3edaab5dbbf2ec6f086a9483500e431069770b28..8caf44839919fb33c1368715c528f507ebc7a5c9 100644 (file)
--- a/client.c
+++ b/client.c
@@ -243,7 +243,6 @@ I9E_DUMMY_COMPLETER(term);
 I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(addatt);
 I9E_DUMMY_COMPLETER(init);
-I9E_DUMMY_COMPLETER(tasks);
 
 static struct i9e_completer completers[];
 
index 28c5eabb1ae9a4821b60b638be559e837dc54f36..7b464d09cab0ed446aac327fbba6d32f9aadd7b3 100644 (file)
@@ -34,7 +34,7 @@ void add_close_on_fork_list(int fd)
        struct close_on_fork *cof = para_malloc(sizeof(struct close_on_fork));
 
        if (!initialized) {
-               INIT_LIST_HEAD(&close_on_fork_list);
+               init_list_head(&close_on_fork_list);
                initialized = 1;
        }
        cof->fd = fd;
index 2171457168c294861ece3c644efe34709f74f5cf..5b17f116d53c93956faf60f88d903b233b7e9b69 100644 (file)
--- a/command.c
+++ b/command.c
@@ -6,10 +6,7 @@
 #include <sys/socket.h>
 #include <regex.h>
 #include <signal.h>
-#include <sys/types.h>
-#include <osl.h>
 #include <arpa/inet.h>
-#include <sys/un.h>
 #include <netdb.h>
 #include <lopsub.h>
 
@@ -22,7 +19,6 @@
 #include "command.h"
 #include "string.h"
 #include "afh.h"
-#include "afs.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
@@ -554,7 +550,7 @@ out:
 }
 EXPORT_SERVER_CMD_HANDLER(stat);
 
-const char *aux_info_cb(unsigned cmd_num, bool verbose)
+static const char *aux_info_cb(unsigned cmd_num, bool verbose)
 {
        static char result[80];
        unsigned perms = server_command_perms[cmd_num];
@@ -745,14 +741,6 @@ out:
 }
 EXPORT_SERVER_CMD_HANDLER(jmp);
 
-/* deprecated, does nothing */
-static int com_tasks(__a_unused struct command_context *cc,
-               __a_unused struct lls_parse_result *lpr)
-{
-       return 1;
-}
-EXPORT_SERVER_CMD_HANDLER(tasks);
-
 static void reset_signals(void)
 {
        para_sigaction(SIGCHLD, SIG_IGN);
index 42860bbb74009330826719c2c4a2d7fecde721ef..b817979f39ec79c141cd67eaaef6f49f559b5d21 100644 (file)
@@ -373,6 +373,11 @@ AC_CHECK_HEADER(samplerate.h, [], HAVE_SAMPLERATE=no)
 AC_CHECK_LIB([samplerate], [src_process], [], HAVE_SAMPLERATE=no)
 LIB_SUBST_FLAGS(samplerate)
 UNSTASH_FLAGS
+######################################################################### ubsan
+AC_ARG_ENABLE([ubsan], [AS_HELP_STRING(--enable-ubsan,
+       [Detect and report undefined behaviour.])],
+       [ENABLE_UBSAN=yes], [ENABLE_UBSAN=no])
+AC_SUBST(ENABLE_UBSAN)
 ######################################################################### server
 if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                test -n "$FLEX"; then
@@ -391,7 +396,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                daemon
                http_send
                close_on_fork
-               mm
                crypt_common
                base64
                ipc
index a4e2f3193730d456ce9908eb55888cdb759154e0..dd5420a6eab92de518e3af60ea49d094ae5db6bb 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -58,9 +58,9 @@ static void daemon_set_default_log_colors(void)
 }
 
 /**
- * Set the color for one loglevel.
+ * Set the color for log messages of the given severity level.
  *
- * \param arg Must be of the form "ll:[fg [bg]] [attr]".
+ * \param arg Must be of the form "severity:[fg [bg]] [attr]".
  */
 void daemon_set_log_color_or_die(const char *arg)
 {
diff --git a/error.h b/error.h
index fe44ff5c50487744881ea1d21d1007528a2ecd70..e94821212fb6ecb0c46fbfa5230a04f5b2295b88 100644 (file)
--- a/error.h
+++ b/error.h
        PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
        PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
-       PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
        PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \
        PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
  * 'E_') and gets later redefined to expand to the error text only
  */
 #define PARA_ERROR(err, msg) E_ ## err
+/**
+ * Numeric error codes.
+ *
+ * Public functions which can fail return the negated value of one of the
+ * constants defined here to indicate the cause of the error.
+ *
+ * \sa \ref para_strerror().
+ */
 enum para_error_codes {PARA_ERRORS};
 #undef PARA_ERROR
 #define PARA_ERROR(err, msg) msg
index a8197308e8323c8fe78546117cc2e9479df0fbfd..8c4545b476e8f792ea7bba6f88b4e83fb952e540 100644 (file)
@@ -45,7 +45,7 @@ static struct i9e_private i9e_private, *i9ep = &i9e_private;
  * running.
  *
  * \return A negative return value of zero means the i9e task terminated. Only
- * in this case it is safe to call ie9_close().
+ * in this case it is safe to call i9e_close().
  */
 int i9e_get_error(void)
 {
@@ -556,7 +556,7 @@ __printf_2_3 void i9e_log(int ll, const char* fmt,...)
  * the given text. If the length of this text exceeds the width of the
  * terminal, the text is shortened by leaving out a part in the middle.
  */
-void ie9_print_status_bar(char *buf, unsigned len)
+void i9e_print_status_bar(char *buf, unsigned len)
 {
        size_t x = i9ep->num_columns, y = (x - 4) / 2;
 
index 40ff294018b221708dd0d1db9cac23c80fb70533..ddf02d76d2bc44a71133844fa0dddf4d9db03778 100644 (file)
@@ -80,7 +80,7 @@ struct i9e_client_info {
 
 int i9e_open(struct i9e_client_info *ici, struct sched *s);
 void i9e_attach_to_stdout(struct btr_node *producer);
-void ie9_print_status_bar(char *buf, unsigned len);
+void i9e_print_status_bar(char *buf, unsigned len);
 void i9e_close(void);
 void i9e_signal_dispatch(int sig_num);
 __printf_2_3 void i9e_log(int ll, const char* fmt,...);
diff --git a/list.h b/list.h
index 66c6d91525031261ff94cd3028e672cb340c06cd..78c302fa322fe6bc2dae2926e95e58189c10c944 100644 (file)
--- a/list.h
+++ b/list.h
  * Copied from the Linux kernel source tree, version 2.6.13.
  *
  * Licensed under the GPL v2 as per the whole kernel source tree.
- *
  */
 
-/** \file list.h doubly linked list implementation */
+/** \file list.h Doubly linked list implementation. */
 
 #include <stddef.h> /* offsetof */
 
-/** get the struct this entry is embedded in */
+/** Get the struct this entry is embedded in. */
 #define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
 
-/**
- * Non-NULL pointers that will result in page faults under normal
- * circumstances, used to verify that nobody uses non-initialized list entries.
- * Used for poisoning the \a next pointer of struct list_head.
- */
-#define LIST_POISON1  ((void *) 0x00100100)
-/** Non-null pointer, used for poisoning the \a prev pointer of struct
- * list_head
- */
-#define LIST_POISON2  ((void *) 0x00200200)
-
-/** Simple doubly linked list implementation. */
+/** A list head is just a pair of pointers. */
 struct list_head {
-       /** pointer to the next list entry */
+       /** Pointer to the next list entry. */
        struct list_head *next;
-       /** pointer to the previous list entry */
+       /** Pointer to the previous list entry. */
        struct list_head *prev;
 };
 
 /** Define an initialized list head. */
-#define INITIALIZED_LIST_HEAD(name) struct list_head name = { &(name), &(name) }
-
-
-/** must be called before using any other list functions */
-#define INIT_LIST_HEAD(ptr) do { \
-       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
+#define INITIALIZED_LIST_HEAD(name) struct list_head name = {&(name), &(name)}
 
-
-/*
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
-                             struct list_head *prev,
-                             struct list_head *next)
+/** This must be called before using any other list functions. */
+static inline void init_list_head(struct list_head *head)
 {
-       next->prev = new;
-       new->next = next;
-       new->prev = prev;
-       prev->next = new;
+       head->next = head;
+       head->prev = head;
 }
 
 /**
- * add a new entry
+ * Insert a new entry after the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it after
+ * \param entry The new entry to add.
+ * \param head The list head to add it after.
  *
- * Insert a new entry after the specified head.
  * This is good for implementing stacks.
  */
-static inline void para_list_add(struct list_head *new, struct list_head *head)
+static inline void para_list_add(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head, head->next);
+       entry->prev = head;
+       entry->next = head->next;
+       head->next->prev = entry;
+       head->next = entry;
 }
 
 /**
- * add a new entry
+ * Insert a new entry before the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it before
+ * \param entry The new entry to add.
+ * \param head list head to add it before.
  *
- * Insert a new entry before the specified head.
  * This is useful for implementing queues.
  */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void list_add_tail(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head->prev, head);
+       entry->prev = head->prev;
+       entry->next = head;
+       head->prev->next = entry;
+       head->prev = entry;
 }
 
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
+/**
+ * Delete an entry from a list.
+ *
+ * \param entry The element to delete.
  *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
+ * The list entry is in an undefined state after this and \ref list_empty()
+ * does not return true.
  */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
+static inline void list_del(struct list_head *entry)
 {
-       next->prev = prev;
-       prev->next = next;
+       entry->prev->next = entry->next;
+       entry->next->prev = entry->prev;
+       /*
+        * These non-NULL pointers result in page faults when dereferenced.
+        * This helps to catch bugs resulting from using deleted list heads.
+        */
+       entry->next = (void *)0x00100100;
+       entry->prev = (void *)0x00200200;
 }
 
 /**
- * Delete entry from list.
- *
- * \param entry the element to delete from the list.
+ * Delete an entry from one list and add it as another list's head.
  *
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
+ * \param entry The entry to move.
+ * \param head The head that will precede our entry.
  */
-static inline void list_del(struct list_head *entry)
+static inline void list_move(struct list_head *entry, struct list_head *head)
 {
-       __list_del(entry->prev, entry->next);
-       entry->next = LIST_POISON1;
-       entry->prev = LIST_POISON2;
+        list_del(entry);
+        para_list_add(entry, head);
 }
 
 /**
- * delete from one list and add as another's head
+ * Test whether a list contains no entries.
  *
- * \param list: the entry to move
- * \param head: the head that will precede our entry
+ * \param head The list to test.
  */
-static inline void list_move(struct list_head *list, struct list_head *head)
+static inline int list_empty(const struct list_head *head)
 {
-        __list_del(list->prev, list->next);
-        para_list_add(list, head);
+       return head->next == head;
 }
 
 /**
- * test whether a list is empty
+ * Test whether a list has just one entry.
  *
- * \param head the list to test.
+ * \param head The list to test.
  */
-static inline int list_empty(const struct list_head *head)
+static inline int list_is_singular(const struct list_head *head)
 {
-       return head->next == head;
+       return !list_empty(head) && (head->next == head->prev);
 }
 
 /**
- * get the struct for this entry
+ * Get the struct in which this entry is embedded in.
  *
- * \param ptr the &struct list_head pointer.
- * \param type the type of the struct this is embedded in.
- * \param member the name of the list_struct within the struct.
+ * \param ptr The list head pointer.
+ * \param type The type of containing structure.
+ * \param member The name of the list head member within the structure.
  */
-#define list_entry(ptr, type, member) \
-       container_of(ptr, type, member)
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
 
 /**
- * iterate over list of given type
+ * Iterate over a list.
  *
- * \param pos the type * to use as a loop counter.
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos A struct pointer which serves as the iterator.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry(pos, head, member)                         \
        for (pos = list_entry((head)->next, typeof(*pos), member);      \
@@ -169,49 +137,27 @@ static inline int list_empty(const struct list_head *head)
             pos = list_entry(pos->member.next, typeof(*pos), member))
 
 /**
- * iterate over list of given type safe against removal of list entry
+ * Iterate over list, safe against removal of list entry.
  *
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos The iterator struct pointer.
+ * \param n A second struct pointer which is used as temporary storage.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry_safe(pos, n, head, member)                 \
        for (pos = list_entry((head)->next, typeof(*pos), member),      \
                n = list_entry(pos->member.next, typeof(*pos), member); \
             &pos->member != (head);                                    \
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
-/**
- * iterate backwards over list of given type safe against removal of list entry
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member)         \
-       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
-               n = list_entry(pos->member.prev, typeof(*pos), member); \
-            &pos->member != (head);                                    \
-            pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 
 /**
- * Get the first element from a list
- * \param ptr the list head to take the element from.
+ * Get the first element of a list.
+ *
+ * \param ptr The list head to take the element from.
  * \param type The type of the struct this is embedded in.
  * \param member The name of the list_struct within the struct.
  *
- * Note that list is expected to be not empty.
+ * Note that the list is expected to be non-empty.
  */
 #define list_first_entry(ptr, type, member) \
-        list_entry((ptr)->next, type, member)
-
-/**
- * Test whether a list has just one entry.
- *
- * \param head The list to test.
- */
-static inline int list_is_singular(const struct list_head *head)
-{
-       return !list_empty(head) && (head->next == head->prev);
-}
-
+       list_entry((ptr)->next, type, member)
index 2a39907cee624219d1fb166b07dbf84288f67bb4..f73d66b26a713ce70ed144e38362a73f5ddcf8c1 100644 (file)
@@ -500,15 +500,6 @@ aux_info_prefix = Permissions:
                the vss status flags, effectively stopping playback.
        [/description]
 
-[subcommand tasks]
-       purpose = list active server tasks (deprecated)
-       aux_info = NO_PERMISSION_REQUIRED
-       [description]
-               This used to print the ID, the status and the name of each task,
-               mainly for debugging purposes. As of version 0.6.2, the subcommand
-               prints nothing. It will be removed in 0.7.0. Don't use.
-       [/description]
-
 [subcommand term]
        purpose = ask the server to terminate
        aux_info = VSS_READ | VSS_WRITE
diff --git a/mm.c b/mm.c
deleted file mode 100644 (file)
index 358783a..0000000
--- a/mm.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.c Paraslash's mood methods. */
-
-#include <regex.h>
-#include <fnmatch.h>
-#include <osl.h>
-#include <lopsub.h>
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "afh.h"
-#include "afs.h"
-#include "mm.h"
-
-/** The comparators for numeric mood methods (year, bitrate, ...). */
-#define MOOD_COMPARATORS \
-       MC(LESS, <) \
-       MC(LESS_OR_EQUAL, <=) \
-       MC(EQUAL, =) \
-       MC(EQUAL2, ==) \
-       MC(NOT_EQUAL, !=) \
-       MC(NOT_EQUAL2, <>) \
-       MC(GREATER, >) \
-       MC(GREATER_OR_EQUAL, >=) \
-
-/** Prefix mood comparator name with "_MC", example: MC_LESS. */
-#define MC(a, b) MC_ ## a,
-/** Each mood comparator is identified by an integer of this type. */
-enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
-#undef MC
-/** Stringfied mood comparator, example: "<". */
-#define MC(a, b) # b,
-/** Array of mood comparators represented as C strings ("<", "<=", ...). */
-static const char *mood_comparators[] = {MOOD_COMPARATORS};
-#undef MC
-
-static int parse_mood_comparator(const char *word)
-{
-       int i;
-
-       for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
-               if (!strcmp(word, mood_comparators[i]))
-                       return i;
-       return -E_MOOD_SYNTAX;
-}
-
-struct mm_compare_num_data {
-       /** <, <=, =, !=, >=, or >. */
-       enum mood_comparator_id id;
-       /** The value given at the mood line. */
-       int32_t arg;
-};
-
-static int mm_compare_num_score_function(int32_t val,
-               const struct mm_compare_num_data *cnd)
-{
-       int res;
-       int32_t arg = cnd->arg;
-
-       switch (cnd->id) {
-       case MC_LESS:
-               res = val < arg; break;
-       case MC_LESS_OR_EQUAL:
-               res = val <= arg; break;
-       case MC_EQUAL:
-       case MC_EQUAL2:
-               res = val == arg; break;
-       case MC_NOT_EQUAL:
-       case MC_NOT_EQUAL2:
-               res = val != arg; break;
-       case MC_GREATER:
-               res = val > arg; break;
-       case MC_GREATER_OR_EQUAL:
-               res = val >= arg; break;
-       default:
-               PARA_EMERG_LOG("BUG: invalid mood comparator\n");
-               exit(EXIT_FAILURE);
-       }
-       return res? 100 : -100;
-}
-
-static int mm_compare_num_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       enum mood_comparator_id id;
-       int32_t arg;
-       struct mm_compare_num_data *cnd;
-       if (argc != 2)
-               return -E_MOOD_SYNTAX;
-       ret = parse_mood_comparator(argv[1]);
-       if (ret < 0)
-               return ret;
-       id = ret;
-       ret = para_atoi32(argv[2], &arg);
-       if (ret < 0)
-               return ret;
-       cnd = para_malloc(sizeof(struct mm_compare_num_data));
-       cnd->id = id;
-       cnd->arg = arg;
-       *private = cnd;
-       return 1;
-}
-
-static int mm_regex_parser(int argc, char **argv, void **private)
-{
-       regex_t *preg;
-       int ret;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       preg = para_malloc(sizeof(*preg));
-       ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
-       if (ret < 0) {
-               free(preg);
-               return ret;
-       }
-       *private = preg;
-       return 1;
-}
-
-static int mm_regex_score_function(const regex_t *preg, const char *pattern)
-{
-       return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
-}
-
-static void mm_regex_cleanup(void *private)
-{
-       regex_t *preg = private;
-       regfree(preg);
-       free(preg);
-}
-
-static int mm_artist_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.artist);
-}
-
-static int mm_title_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.title);
-}
-
-static int mm_album_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.album);
-}
-
-static int mm_comment_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.comment);
-}
-
-static int mm_bitrate_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->bitrate, private);
-}
-
-static int mm_frequency_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->frequency, private);
-}
-
-static int mm_channels_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->channels, private);
-}
-
-static int mm_image_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->image_id, private);
-}
-
-static int mm_lyrics_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->lyrics_id, private);
-}
-
-static int mm_num_played_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->num_played, private);
-}
-
-struct mm_year_data {
-       /** Comparator and year given at the mood line. */
-       struct mm_compare_num_data *cnd;
-       /** Used to detect Y2K issues. */
-       int32_t current_year;
-};
-
-static int mm_year_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
-       time_t current_time;
-       struct tm *gmt;
-
-       ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
-       if (ret < 0)
-               goto err;
-       current_time = time(NULL);
-       gmt = gmtime(&current_time);
-       /* tm_year is the number of years since 1900 */
-       mmyd->current_year = gmt->tm_year + 1900;
-       *private = mmyd;
-       return 1;
-err:
-       free(mmyd);
-       return ret;
-}
-
-static int mm_year_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       const struct mm_year_data *mmyd = private;
-       int32_t tag_year;
-       int ret = para_atoi32(afhi->tags.year, &tag_year);
-
-       if (ret < 0) /* year tag not present or not a number */
-               return -100;
-       if (tag_year < 0)
-               return -100;
-       /* try to work around Y2K issues */
-       if (tag_year < 100) {
-               tag_year += 1900;
-               if (tag_year + 100 <= mmyd->current_year)
-                       tag_year += 100; /* assume tag_year >= 2000 */
-       }
-       return mm_compare_num_score_function(tag_year, mmyd->cnd);
-}
-
-static void mm_year_cleanup(void *private)
-{
-       struct mm_year_data *mmyd = private;
-
-       free(mmyd->cnd);
-       free(mmyd);
-}
-
-static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
-               __a_unused void **ignored)
-{
-       return argc? -E_MOOD_SYNTAX : 1;
-}
-
-static int mm_no_attributes_set_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               __a_unused const void *data)
-{
-       if (!afsi->attributes)
-               return 100;
-       return -100;
-}
-
-static int mm_path_matches_score_function(const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       if (fnmatch(data, path, 0))
-               return -100;
-       return 100;
-}
-
-static int mm_path_matches_parser(int argc, char **argv, void **data)
-{
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       *data = para_strdup(argv[1]);
-       return 1;
-}
-
-static void mm_path_matches_cleanup(void *data)
-{
-       free(data);
-}
-
-static int mm_is_set_parser(int argc, char **argv, void **bitnum)
-{
-       int ret;
-       unsigned char c, *res;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       ret = get_attribute_bitnum_by_name(argv[1], &c);
-       if (ret < 0)
-               return ret;
-       res = para_malloc(1);
-       *res = c;
-       *bitnum = res;
-       return 1;
-}
-
-static int mm_is_set_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       const unsigned char *bn = data;
-       if (afsi->attributes & (1ULL << *bn))
-               return 100;
-       return -100;
-}
-
-#define DEFINE_MOOD_METHOD(_name) \
-.parser = mm_ ## _name ## _parser, \
-.score_function = mm_ ## _name ## _score_function, \
-.name = #_name
-
-#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
-       DEFINE_MOOD_METHOD(_name), \
-       .cleanup = mm_ ## _name ## _cleanup
-
-#define DEFINE_REGEX_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_regex_parser, \
-       .score_function = mm_ ## _name ## _score_function, \
-       .cleanup = mm_regex_cleanup
-
-#define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_compare_num_parser, \
-       .score_function = mm_ ## _name ## _score_function
-
-const struct mood_method mood_methods[] = {
-       {DEFINE_MOOD_METHOD(no_attributes_set)},
-       {DEFINE_MOOD_METHOD(is_set)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
-       {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(title_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(album_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},
-       {.parser = NULL}
-};
diff --git a/mm.h b/mm.h
deleted file mode 100644 (file)
index 28038e7..0000000
--- a/mm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.h Symbols and declarations for mood methods. */
-
-/**
- * Assign scores according to a mood_method.
- *
- * Each mood_method has its own mood_score_function. The first three parameters
- * passed to that function are informations about the audio file whose score is
- * to be computed. The data argument depends on the mood method this function
- * is used for. It usually is the argument given at the end of a mood line.
- *
- * Mood score functions must return values between -100 and +100 inclusively.
- * Boolean score functions should always return either -100 or +100.
- *
- * \sa struct \ref mood_method, \ref mood_parser.
- */
-typedef int mood_score_function(const char *path, const struct afs_info *afsi,
-               const struct afh_info *afhi, const void *data);
-
-/**
- * Pre-process a mood line.
- *
- * The mood_parser of a mood_method is called once at mood open time for each
- * line of the current mood definition that contains the mood_method's name as
- * a keyword. The line is passed to the mood_parser as the first argument. The
- * mood_parser must determine whether the line is syntactically correct and
- * return a positive value if so and a negative value otherwise.
- *
- * Some mood parsers pre-process the data given in the mood line to compute a
- * structure which depends of the particular mood_method and which is used
- * later in the mood_score_function of the mood_method. The mood_parser may
- * store a pointer to its structure via the void** pointer.
- *
- * \sa \ref mood_cleanup_function, \ref mood_score_function.
- */
-typedef int mood_parser(int, char **, void **);
-
-/**
- * Deallocate resources which were allocated by the mood_parser.
- *
- * Function to free the resources allocated in \ref mood_method::parser. The
- * argument is a pointer to mood method specific data returned by ->parser().
- */
-typedef void mood_cleanup_function(void *);
-
-/**
- * Used for scoring and to determine whether a file is admissible.
- */
-struct mood_method {
-       /** The name of the method. */
-       const char *name;
-       /** Pointer to the mood parser. */
-       mood_parser *parser;
-       /** Pointer to the score function */
-       mood_score_function *score_function;
-       /** Optional cleanup function. */
-       mood_cleanup_function *cleanup;
-};
-
-/** The array of available mood methods. */
-extern const struct mood_method mood_methods[];
diff --git a/mood.c b/mood.c
index a63d4d2af5d10d7b64c319d915e00b9b7ea62e89..bbe84734deffd189d7fb8d05ac3e58877b356f8f 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mm.h"
 #include "mood.h"
 
 /*
@@ -39,47 +38,21 @@ struct afs_statistics {
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
+       /** Correction factor for the num played score. */
+       int64_t num_played_correction;
+       /** Correction factor for the last played score. */
+       int64_t last_played_correction;
+       /** Common divisor of the correction factors. */
+       int64_t normalization_divisor;
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics;
+static struct afs_statistics statistics = {.normalization_divisor = 1};
 
-/**
- * Each line of the current mood corresponds to a mood_item.
- */
-struct mood_item {
-       /** The method this line is referring to. */
-       const struct mood_method *method;
-       /** The data structure computed by the mood parser. */
-       void *parser_data;
-       /** The given score value, or zero if none was given. */
-       int32_t score_arg;
-       /** Non-zero if random scoring was requested. */
-       int random_score;
-       /** Whether the "not" keyword was given in the mood line. */
-       int logical_not;
-       /** The position in the list of items. */
-       struct list_head mood_item_node;
-};
-
-/*
- * Created from the mood definition by \ref change_current_mood().
- *
- * When a mood is opened, each line of its definition is investigated, and a
- * corresponding mood item is produced. Each mood line starts with accept,
- * deny, or score which determines the type of the mood line. For each such
- * type a linked list is maintained whose entries are the mood items.
- */
 struct mood {
        /** The name of this mood. */
        char *name;
-       /** The list of mood items of type \p accept. */
-       struct list_head accept_list;
-       /** The list of mood items of type \p deny. */
-       struct list_head deny_list;
-       /** The list of mood items of type \p score. */
-       struct list_head score_list;
-       /* Only used for version 2 moods. */
+       /** Info for the bison parser. */
        struct mp_context *parser_context;
 };
 
@@ -146,115 +119,20 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/*
- * Returns true if row matches, false if it does not match. In any case score
- * and score_arg_sum are set/increased accordingly.
- */
-static bool get_item_score(struct mood_item *item, const struct afs_info *afsi,
-               const struct afh_info *afhi, const char *path, long *score,
-               long *score_arg_sum)
-{
-       int ret;
-       bool match = true;
-
-       *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg);
-       ret = 100;
-       if (item->method) {
-               ret = item->method->score_function(path, afsi, afhi,
-                       item->parser_data);
-               if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not))
-                       match = false;
-       }
-       if (item->random_score)
-               *score = PARA_ABS(ret) * para_random(100);
-       else
-               *score = PARA_ABS(ret) * item->score_arg;
-       return match;
-}
-
 /* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m,
-               long *scorep)
+static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
 {
-       struct mood_item *item;
-       int ret;
-       bool match;
-       long score_arg_sum = 0, score = 0, item_score;
-       struct afs_info afsi;
-       struct afh_info afhi;
-       char *path;
-
        if (!m)
                return -E_NO_MOOD;
-       if (m->parser_context) {
-               *scorep = 0;
-               return mp_eval_row(aft_row, m->parser_context);
-       }
-       ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret < 0)
-               return ret;
-       ret = get_afhi_of_row(aft_row, &afhi);
-       if (ret < 0)
-               return ret;
-       ret = get_audio_file_path_of_row(aft_row, &path);
-       if (ret < 0)
-               return ret;
-       /* reject audio file if it matches any entry in the deny list */
-       list_for_each_entry(item, &m->deny_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match) /* not admissible */
-                       return 0;
-               score += item_score;
-       }
-       match = false;
-       list_for_each_entry(item, &m->accept_list, mood_item_node) {
-               ret = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (ret == 0)
-                       continue;
-               match = true;
-               score += item_score;
-       }
-       /* reject if there is no matching entry in the accept list */
-       if (!match && !list_empty(&m->accept_list))
-               return 0;
-       list_for_each_entry(item, &m->score_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match)
-                       score += item_score;
-       }
-       if (score_arg_sum)
-               score /= score_arg_sum;
-       *scorep = score;
-       return 1;
-}
-
-static void cleanup_list_entry(struct mood_item *item)
-{
-       if (item->method && item->method->cleanup)
-               item->method->cleanup(item->parser_data);
-       else
-               free(item->parser_data);
-       list_del(&item->mood_item_node);
-       free(item);
+       return mp_eval_row(aft_row, m->parser_context);
 }
 
 static void destroy_mood(struct mood *m)
 {
-       struct mood_item *tmp, *item;
-
        if (!m)
                return;
-       list_for_each_entry_safe(item, tmp, &m->accept_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->deny_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->score_list, mood_item_node)
-               cleanup_list_entry(item);
-       free(m->name);
        mp_shutdown(m->parser_context);
+       free(m->name);
        free(m);
 }
 
@@ -263,160 +141,16 @@ static struct mood *alloc_new_mood(const char *name)
        struct mood *m = para_calloc(sizeof(struct mood));
        if (name)
                m->name = para_strdup(name);
-       INIT_LIST_HEAD(&m->accept_list);
-       INIT_LIST_HEAD(&m->deny_list);
-       INIT_LIST_HEAD(&m->score_list);
        return m;
 }
 
-/** The different types of a mood line. */
-enum mood_line_type {
-       /** Invalid. */
-       ML_INVALID,
-       /** Accept line. */
-       ML_ACCEPT,
-       /** Deny line. */
-       ML_DENY,
-       /** Score line. */
-       ML_SCORE
-};
-
-/** Data passed to the parser of a mood line. */
-struct mood_line_parser_data {
-       /** The mood this mood line belongs to. */
-       struct mood *m;
-       /** The line number in the mood definition. */
-       unsigned line_num;
-};
-
-/*
- * <accept [with score <score>] | deny [with score <score>]  | score <score>>
- *     [if] [not] <mood_method> [options]
- * <score> is either an integer or "random" which assigns a random score to
- * all matching files
- */
-static int parse_mood_line(char *mood_line, void *data)
-{
-       struct mood_line_parser_data *mlpd = data;
-       char **argv;
-       unsigned num_words;
-       char **w;
-       int i, ret;
-       enum mood_line_type mlt = ML_INVALID;
-       struct mood_item *mi = NULL;
-
-       mlpd->line_num++;
-       ret = create_argv(mood_line, " \t", &argv);
-       if (ret < 0)
-               return ret;
-       num_words = ret;
-       if (!num_words) /* empty line */
-               goto out;
-       w = argv;
-       if (**w == '#') /* comment */
-               goto out;
-       if (!strcmp(*w, "accept"))
-               mlt = ML_ACCEPT;
-       else if (!strcmp(*w, "deny"))
-               mlt = ML_DENY;
-       else if (!strcmp(*w, "score"))
-               mlt = ML_SCORE;
-       ret = -E_MOOD_SYNTAX;
-       if (mlt == ML_INVALID)
-               goto out;
-       mi = para_calloc(sizeof(struct mood_item));
-       if (mlt != ML_SCORE) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "with"))
-                       goto check_for_if;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "score"))
-                       goto out;
-       }
-       if (mlt == ML_SCORE || !strcmp(*w, "score")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "random")) {
-                       mi->random_score = 0;
-                       ret = para_atoi32(*w, &mi->score_arg);
-                       if (ret < 0)
-                               goto out;
-               } else {
-                       mi->random_score = 1;
-                       if (!*(w + 1))
-                       goto success; /* the line "score random" is valid */
-               }
-       } else
-               mi->score_arg = 0;
-       ret = -E_MOOD_SYNTAX;
-       w++;
-       if (!*w)
-               goto out;
-check_for_if:
-       if (!strcmp(*w, "if")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-       }
-       if (!strcmp(*w, "not")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               mi->logical_not = 1;
-       } else
-               mi->logical_not = 0;
-       for (i = 0; mood_methods[i].parser; i++) {
-               if (strcmp(*w, mood_methods[i].name))
-                       continue;
-               break;
-       }
-       ret = -E_MOOD_SYNTAX;
-       if (!mood_methods[i].parser)
-               goto out;
-       ret = mood_methods[i].parser(num_words - 1 - (w - argv), w,
-               &mi->parser_data);
-       if (ret < 0)
-               goto out;
-       mi->method = &mood_methods[i];
-success:
-       if (mlpd->m) {
-               if (mlt == ML_ACCEPT)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->accept_list);
-               else if (mlt == ML_DENY)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->deny_list);
-               else
-                       para_list_add(&mi->mood_item_node, &mlpd->m->score_list);
-       }
-       PARA_DEBUG_LOG("%s entry added, method: %p\n", mlt == ML_ACCEPT? "accept" :
-               (mlt == ML_DENY? "deny" : "score"), mi->method);
-       ret = 1;
-out:
-       free_argv(argv);
-       if (mi && (ret < 0 || !mlpd->m)) { /* mi was not added to any list */
-               free(mi->parser_data);
-               free(mi);
-       }
-       return ret;
-}
-
 static int load_mood(const struct osl_row *mood_row, struct mood **m,
                char **errmsg)
 {
        char *mood_name;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
        int ret;
 
-       *m = NULL;
        ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
        if (ret < 0) {
                if (errmsg)
@@ -425,33 +159,19 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
                return ret;
        }
        assert(*mood_name);
-       mlpd.m = alloc_new_mood(mood_name);
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
-       if (ret < 0) {
-               PARA_INFO_LOG("opening version 2 mood %s\n", mlpd.m->name);
-               ret = mp_init(mood_def.data, mood_def.size, &mlpd.m->parser_context,
-                       errmsg);
-               if (ret < 0)
-                       destroy_mood(mlpd.m);
-       } else {
-               PARA_WARNING_LOG("loaded version 1 mood %s\n", mlpd.m->name);
-               PARA_WARNING_LOG("please convert to version 2\n");
-               ret = 1;
-       }
+       *m = alloc_new_mood(mood_name);
+       PARA_INFO_LOG("opening mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
        osl_close_disk_object(&mood_def);
-       if (ret >= 0)
-               *m = mlpd.m;
        return ret;
 }
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
        struct para_buffer *pb = data;
-       char *mood_name;
+       char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
-
+       struct mood *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
@@ -460,24 +180,15 @@ static int check_mood(struct osl_row *mood_row, void *data)
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
+       m = alloc_new_mood("check");
+       ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
+               &errmsg);
        if (ret < 0) {
-               char *errmsg;
-               struct mood *m = alloc_new_mood("check");
-               ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
-                       &errmsg);
-               if (ret < 0) {
-                       para_printf(pb, "%s: %s\n", mood_name, errmsg);
-                       free(errmsg);
-                       para_printf(pb, "%s\n", para_strerror(-ret));
-               } else
-                       destroy_mood(m);
-       } else {
-               para_printf(pb, "%s: v1 mood, please convert to v2\n",
-                       mood_name);
-
-       }
+               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               free(errmsg);
+               para_printf(pb, "%s\n", para_strerror(-ret));
+       } else
+               destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
@@ -499,6 +210,43 @@ int mood_check_callback(struct afs_callback_arg *aca)
                check_mood));
 }
 
+/*
+ * The normalized num_played and last_played values are defined as
+ *
+ *     nn := -(np - mean_n) / sigma_n and nl := -(lp - mean_l) / sigma_l
+ *
+ *  For a (hypothetical) file with np = 0 and lp = now we thus have
+ *
+ *     nn =  mean_n / sigma_n =: hn > 0
+ *     nl = -(now - mean_l) / sigma_l =: hl < 0
+ *
+ * We design the score function so that both contributions get the same
+ * weight. Define the np and lp score of an arbitrary file as
+ *
+ *     sn := nn * -hl and sl := nl * hn
+ *
+ * Example:
+ *     num_played mean/sigma: 87/14
+ *     last_played mean/sigma: 45/32 days
+ *
+ *     We have hn = 87 / 14 = 6.21 and hl = -45 / 32 = -1.41. Multiplying
+ *     nn of every file with the correction factor 1.41 and nl with
+ *     6.21 makes the weight of the two contributions equal.
+ *
+ * The total score s := sn + sl has the representation
+ *
+ *     s = -cn * (np - mean_n) - cl * (lp - mean_l)
+ *
+ * with positive correction factors
+ *
+ *     cn = (now - mean_l) / (sqrt(ql) * sqrt(qn) / n)
+ *     cl = mean_n / (sqrt(ql) * sqrt(qn) / n)
+ *
+ * where ql and qn are the quadratic deviations stored in the statistics
+ * structure and n is the number of admissible files. To avoid integer
+ * overflows and rounding errors we store the common divisor of the
+ * correction factors separately.
+ */
 static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
 {
        if (!n || !qd)
@@ -506,13 +254,13 @@ static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
        return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
 }
 
-static long compute_score(struct afs_info *afsi, long mood_score)
+static long compute_score(struct afs_info *afsi)
 {
-       mood_score -= normalized_value(afsi->num_played, statistics.num,
+       long score = -normalized_value(afsi->num_played, statistics.num,
                statistics.num_played_sum, statistics.num_played_qd);
-       mood_score -= normalized_value(afsi->last_played, statistics.num,
+       score -= normalized_value(afsi->last_played, statistics.num,
                statistics.last_played_sum, statistics.last_played_qd);
-       return mood_score / 3;
+       return score / 2;
 }
 
 static int add_afs_statistics(const struct osl_row *row)
@@ -556,6 +304,7 @@ static int del_afs_statistics(const struct osl_row *row)
        assert(n);
        if (n == 1) {
                memset(&statistics, 0, sizeof(statistics));
+               statistics.normalization_divisor = 1;
                return 1;
        }
 
@@ -581,63 +330,42 @@ static int del_afs_statistics(const struct osl_row *row)
 
 /*
  * At mood open time we determine the set of admissible files for the given
- * mood. The mood score of each admissible file is computed by adding up all
- * mood item scores. Next, we update the afs statistics and append a struct
- * admissible_file_info to a temporary array.
- *
- * When all files have been processed in this way, the final score of each
- * admissible file is computed by adding the dynamic score (which depends on
- * the afs_statistics and the current time) to the mood score. Finally, all
- * audio files in the temporary array are added to the score table and the
- * array is freed.
+ * mood where each file is identified by a pointer to a row of the audio file
+ * table. In the first pass the pointers are added to a temporary array and
+ * statistics are computed. When all admissible files have been processed in
+ * this way, the score of each admissible file is computed and the (row, score)
+ * pair is added to the score table. This has to be done in a second pass
+ * since the score depends on the statistics. Finally, the array is freed.
  */
-struct admissible_file_info
-{
-       /** The admissible audio file. */
-       struct osl_row *aft_row;
-       /** Its score. */
-       long score;
-};
-
-/** The temporary array of admissible files. */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
        struct mood *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
-       struct admissible_file_info *array;
+       struct osl_row **array;
 };
 
-/**
- * Add an entry to the array of admissible files.
- *
- * \param aft_row The audio file to be added.
- * \param private_data Pointer to a struct admissible_file_info.
- *
- * \return 1 if row admissible, 0 if not, negative on errors.
+/*
+ * Check whether the given audio file is admissible. If it is, add it to array
+ * of admissible files.
  */
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
        int ret;
-       long score = 0;
 
-       ret = row_is_admissible(aft_row, aa->m, &score);
+       ret = row_is_admissible(aft_row, aa->m);
        if (ret <= 0)
                return ret;
        if (statistics.num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
                aa->array = para_realloc(aa->array,
-                       aa->size * sizeof(struct admissible_file_info));
+                       aa->size * sizeof(struct osl_row *));
        }
-       aa->array[statistics.num].aft_row = aft_row;
-       aa->array[statistics.num].score = score;
-       ret = add_afs_statistics(aft_row);
-       if (ret < 0)
-               return ret;
-       return 1;
+       aa->array[statistics.num] = aft_row;
+       return add_afs_statistics(aft_row);
 }
 
 /**
@@ -704,7 +432,7 @@ static int update_afs_statistics(struct afs_info *old_afsi,
        return 1;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
+static int add_to_score_table(const struct osl_row *aft_row)
 {
        long score;
        struct afs_info afsi;
@@ -712,7 +440,7 @@ static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi, mood_score);
+       score = compute_score(&afsi);
        return score_add(aft_row, score);
 }
 
@@ -770,7 +498,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        if (ret < 0)
                return ret;
        was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood, &score);
+       ret = row_is_admissible(aft_row, current_mood);
        if (ret < 0)
                return ret;
        is_admissible = (ret > 0);
@@ -782,7 +510,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                ret = add_afs_statistics(aft_row);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row, score);
+               return add_to_score_table(aft_row);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
@@ -793,7 +521,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                if (ret < 0)
                        return ret;
        }
-       score = compute_score(&afsi, score);
+       score = compute_score(&afsi);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -804,15 +532,11 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        return score_update(aft_row, percent);
 }
 
-static void log_statistics(void)
+/* sse: seconds since epoch. */
+static void log_statistics(int64_t sse)
 {
        unsigned n = statistics.num;
        int mean_days, sigma_days;
-       /*
-        * We can not use the "now" pointer from sched.c here because we are
-        * called before schedule(), which initializes "now".
-        */
-       struct timeval rnow;
 
        assert(current_mood);
        PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
@@ -822,13 +546,18 @@ static void log_statistics(void)
                return;
        }
        PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       clock_get_realtime(&rnow);
-       mean_days = (rnow.tv_sec - statistics.last_played_sum / n) / 3600 / 24;
+       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
        sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
        PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %llu/%llu\n",
-               (long long unsigned)statistics.num_played_sum / n,
-               (long long unsigned)int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
+               statistics.num_played_sum / n,
+               int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
+               statistics.num_played_correction);
+       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
+               statistics.last_played_correction);
+       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
+               statistics.normalization_divisor);
 }
 
 /**
@@ -841,6 +570,25 @@ void close_current_mood(void)
        destroy_mood(current_mood);
        current_mood = NULL;
        memset(&statistics, 0, sizeof(statistics));
+       statistics.normalization_divisor = 1;
+}
+
+static void compute_correction_factors(int64_t sse)
+{
+       struct afs_statistics *s = &statistics;
+
+       if (s->num > 0) {
+               s->normalization_divisor = int_sqrt(s->last_played_qd)
+                       * int_sqrt(s->num_played_qd) / s->num / 100;
+               s->num_played_correction = sse - s->last_played_sum / s->num;
+               s->last_played_correction = s->num_played_sum / s->num;
+       }
+       if (s->num_played_correction == 0)
+               s->num_played_correction = 1;
+       if (s->normalization_divisor == 0)
+               s->normalization_divisor = 1;
+       if (s->last_played_correction == 0)
+               s->last_played_correction = 1;
 }
 
 /**
@@ -869,6 +617,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                .size = 0,
                .array = NULL
        };
+       /*
+        * We can not use the "now" pointer from sched.c here because we are
+        * called before schedule(), which initializes "now".
+        */
+       struct timeval rnow;
 
        if (mood_name) {
                struct mood *m;
@@ -901,9 +654,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        *errmsg = make_message("audio file loop failed");
                return ret;
        }
+       clock_get_realtime(&rnow);
+       compute_correction_factors(rnow.tv_sec);
+       log_statistics(rnow.tv_sec);
        for (i = 0; i < statistics.num; i++) {
-               struct admissible_file_info *a = aa.array + i;
-               ret = add_to_score_table(a->aft_row, a->score);
+               ret = add_to_score_table(aa.array[i]);
                if (ret < 0) {
                        if (errmsg)
                                *errmsg = make_message(
@@ -911,7 +666,6 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        goto out;
                }
        }
-       log_statistics();
        ret = statistics.num;
 out:
        free(aa.array);
diff --git a/mp.c b/mp.c
index 416b4f92065ef79716e7f966c3102e995071a112..b5fa9cacaa6dcf9f3e2ef6f99fd025757f12097c 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -389,6 +389,27 @@ MP_AFHI(frequency)
 MP_AFHI(channels)
 /** \endcond */
 
+/**
+ * Return the duration of the audio file from the afh info structure.
+ *
+ * \param ctx See \ref mp_path().
+ *
+ * The duration is computed by multiplying the number of chunks and the
+ * duration of one chunk.
+ *
+ * \return The approximate number of milliseconds.
+ */
+int64_t mp_duration(struct mp_context *ctx)
+{
+       struct timeval tmp;
+       int ret = get_afhi(ctx);
+
+       if (ret < 0)
+               return 0;
+       tv_scale(ctx->afhi.chunks_total, &ctx->afhi.chunk_tv, &tmp);
+       return tv2ms(&tmp);
+}
+
 /**
  * Define a function which extracts and returns the value of a meta tag.
  *
diff --git a/mp.h b/mp.h
index febbe3240f49ba81b08ca2168b10dd1189fc3f54..891bfb054e2f4cf1a922e7434a8467808f2e474b 100644 (file)
--- a/mp.h
+++ b/mp.h
@@ -139,6 +139,7 @@ bool mp_is_set(const char *attr, struct mp_context *ctx);
 char *mp_path(struct mp_context *ctx);
 int64_t mp_year(struct mp_context *ctx);
 int64_t mp_num_attributes_set(struct mp_context *ctx);
+int64_t mp_duration(struct mp_context *ctx);
 
 /* Generated with MP_AFSI() */
 /** \cond MP_AFSI */
diff --git a/net.c b/net.c
index 91200fc040bcfebafccf9e737fb65514af9ff8f2..e1951e5e8d87501b1ef9096d3f3df64117e904f0 100644 (file)
--- a/net.c
+++ b/net.c
@@ -288,7 +288,7 @@ struct flowopts *flowopt_new(void)
 {
        struct flowopts *new = para_malloc(sizeof(*new));
 
-       INIT_LIST_HEAD(&new->sockopts);
+       init_list_head(&new->sockopts);
        return new;
 }
 
index 311a514dc86ff6bfb01abf53d16a75672827af12..0565167c256a43bfe86a6442f195a320e1458db8 100644 (file)
@@ -199,9 +199,15 @@ static int oss_post_select(__a_unused struct sched *s, void *context)
 
                if (sound_device_is_busy())
                        return 0;
-               get_btr_sample_rate(btrn, &rate);
-               get_btr_channels(btrn, &ch);
-               get_btr_sample_format(btrn, &format);
+               ret = get_btr_sample_rate(btrn, &rate);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_channels(btrn, &ch);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_sample_format(btrn, &format);
+               if (ret < 0)
+                       goto out;
                ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
diff --git a/play.c b/play.c
index ffdc8555ce6686833b846f9610a3d81f6adf03cb..14fac42fd7b6e92566815d4c56b1f4b6b23c88e2 100644 (file)
--- a/play.c
+++ b/play.c
@@ -1102,7 +1102,7 @@ static void session_update_time_string(char *str, unsigned len)
                if (btr_get_input_queue_size(pt->btrn) > 0)
                        return;
        }
-       ie9_print_status_bar(str, len);
+       i9e_print_status_bar(str, len);
 }
 
 /*
diff --git a/sched.c b/sched.c
index a2903940fdaea1b24d6a49cfc2f54766c136070f..aac8efed1bcf8d897e6aef4d07973f2babc8d4f4 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -244,7 +244,7 @@ struct task *task_register(struct task_info *info, struct sched *s)
        assert(info->post_select);
 
        if (!s->task_list.next)
-               INIT_LIST_HEAD(&s->task_list);
+               init_list_head(&s->task_list);
 
        t->info = *info;
        t->name = para_strdup(info->name);
index ea494d9a7b23f5ee87186917d11e883631ec9cbc..9debdfca5a40a7ada404e9cf4a034140e2509a6b 100644 (file)
@@ -136,9 +136,9 @@ void init_sender_status(struct sender_status *ss,
        }
        ss->default_port = default_port;
 
-       INIT_LIST_HEAD(&ss->client_list);
+       init_list_head(&ss->client_list);
        /* Initialize an access control list */
-       INIT_LIST_HEAD(&ss->acl);
+       init_list_head(&ss->acl);
        for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
                const char *arg = lls_string_val(i, acl_opt_result);
                char addr[16];
index 10251ae7c10b734bd906a644e0832e29b9e6bd9c..10379a0e83098f392e8653467b826925376eda15 100644 (file)
--- a/string.h
+++ b/string.h
@@ -67,7 +67,7 @@ int for_each_line(unsigned flags, char *buf, size_t size,
 } \
 )
 
-__must_check __malloc void *para_realloc(void *p, size_t size);
+__must_check void *para_realloc(void *p, size_t size);
 __must_check __malloc void *para_malloc(size_t size);
 __must_check __malloc void *para_calloc(size_t size);
 __must_check __malloc char *para_strdup(const char *s);
index f23545adb68342322fbeaef4c45c73c663a969be..8e9ff2c5de79a6385b6472453f3dec417b9ac5cc 100644 (file)
@@ -103,7 +103,7 @@ static void sync_open(struct filter_node *fn)
        const struct lls_opt_result *r_b;
 
        ctx = fn->private_data = para_calloc(sizeof(*ctx));
-       INIT_LIST_HEAD(&ctx->buddies);
+       init_list_head(&ctx->buddies);
 
        /* create socket to listen for incoming packets */
        ret = makesock(
index 96a25d370a99d3fdcb6782ef9451ae86b1cfc3a4..91550aa8c66836239df07193d46d5d470612767e 100644 (file)
@@ -394,7 +394,7 @@ static void udp_init_target_list(void)
        struct sender_command_data scd;
        int i;
 
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
                if (udp_resolve_target(arg, &scd) < 0)
@@ -427,7 +427,7 @@ static char *udp_help(void)
 /* Initialize the list of udp targets. */
 static void udp_send_init(void)
 {
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        sender_status = SENDER_off;
        udp_init_target_list();
        if (!OPT_GIVEN(UDP_NO_AUTOSTART))
diff --git a/vss.c b/vss.c
index 9969a150ee63291e182f058664af9178eabf77f3..9e2e32ca3b3bd7979b3c564ff1959dcbe7560446 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -1178,7 +1178,7 @@ void vss_init(int afs_socket, struct sched *s)
        vsst->afs_socket = afs_socket;
        ms2tv(announce_time, &vsst->announce_tv);
        PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
-       INIT_LIST_HEAD(&fec_client_list);
+       init_list_head(&fec_client_list);
        FOR_EACH_SENDER(i) {
                PARA_NOTICE_LOG("initializing %s sender\n", senders[i]->name);
                senders[i]->init();
index e863e7b9faa45c4a895355377694c493d5512e5d..8e85f7fc5b260b73b095cbea04f7ff7a72fb2017 100644 (file)
@@ -23,7 +23,7 @@
        [<a href="para_filter.man.html">para_filter</a>]
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
-       [<a href="para_mixer.man.html">para_mixere</a>]
+       [<a href="para_mixer.man.html">para_mixer</a>]
        [<a href="para_play.man.html">para_play</a>]
        [<a href="para_upgrade_db.man.html">para_upgrade_db</a>]
 </p>
index 9ef92b7af654ec06f193ad5dd3941648b82a83b8..b2b1f5fabbcc980cf852c1d5c5c177583ea693f9 100644 (file)
@@ -19,7 +19,7 @@ provided at this point. There are several ways to download the source:
                checkout of any of the four integration branches maint,
                master, next, pu (see the
 
-               <a href="manual.html#Git.branches">Git branches</a>
+               <a href="manual.html#Git-branches">Git branches</a>
 
                section of the manual). All previous releases
                correspond to tagged commits and may be checked out
index 32a99fe27995a7cc6f636ddd7a9e18c0a8ebd57b..94de694470a2c2d5a4f02aab2eb7c9b558fd333c 100644 (file)
@@ -1025,6 +1025,7 @@ Keyword              |    Type | Semantic value
 `bitrate`            | integer | The average bitrate
 `frequency`          | integer | The output sample rate
 `channels`           | integer | The number of channels
+`duration`           | integer | The number of milliseconds
 `is_set("foo")`      | boolean | True if attribute "foo" is set.
 
 [\*] For most audio formats, the year tag is stored as a string. It
diff --git a/write.h b/write.h
index cb0beff812121b338e3749e80f63ad2cb99999db..833cb69a5cb6373bae819d5b7c323e1d567c6104 100644 (file)
--- a/write.h
+++ b/write.h
@@ -66,7 +66,7 @@ const struct writer *writer_get(int wid);
 const char *writer_name(int wid);
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+int get_btr_channels(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result);
 void print_writer_helps(bool detailed);
index 14cc98a4189a5f74907f8bfcf416f62bcc2515c5..41c3eb23728ab11cde71f837d4c58a1ef6908d24 100644 (file)
@@ -174,24 +174,24 @@ void print_writer_helps(bool detailed)
        }
 }
 
-static void get_btr_value(struct btr_node *btrn, const char *cmd,
+static int get_btr_value(struct btr_node *btrn, const char *cmd,
                int32_t *result)
 {
        char *buf = NULL;
        int ret = btr_exec_up(btrn, cmd, &buf);
 
-       if (ret < 0) {
-               /*
-                * This really should not happen. It means one of our parent
-                * nodes died unexpectedly. Proceed with fingers crossed.
-                */
-               PARA_CRIT_LOG("cmd %s: %s\n", cmd, para_strerror(-ret));
-               *result = 0;
-               return;
-       }
+       *result = 0;
+       /*
+        * Errors may happen when the decoder returns EOF before the writer had
+        * a chance to query the buffer tree for the channel count, sample rate
+        * etc.
+        */
+       if (ret < 0)
+               return ret;
        ret = para_atoi32(buf, result);
        assert(ret >= 0);
        free(buf);
+       return ret;
 }
 
 /**
@@ -200,11 +200,11 @@ static void get_btr_value(struct btr_node *btrn, const char *cmd,
  * \param btrn Where to start the search.
  * \param result Filled in by this function.
  *
- * This function is assumed to succeed and terminates on errors.
+ * \return Standard.
  */
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_rate", result);
+       return get_btr_value(btrn, "sample_rate", result);
 }
 
 /**
@@ -212,10 +212,12 @@ void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result See \ref get_btr_sample_rate.
+ *
+ * \return Standard.
  */
-void get_btr_channels(struct btr_node *btrn, int32_t *result)
+int get_btr_channels(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "channels", result);
+       return get_btr_value(btrn, "channels", result);
 }
 
 /**
@@ -223,8 +225,10 @@ void get_btr_channels(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result Contains the sample format as an enum sample_format type.
+ *
+ * \return Standard.
  */
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_format", result);
+       return get_btr_value(btrn, "sample_format", result);
 }
index 2dbe21b779a82eebd79403fa5f60bc56925489dd..4a53db2741754bf6359733af8b42ca60ca255e92 100644 (file)
--- a/yy/mp.lex
+++ b/yy/mp.lex
@@ -74,6 +74,7 @@ lyrics_id {return LYRICS_ID;}
 bitrate {return BITRATE;}
 frequency {return FREQUENCY;}
 channels {return CHANNELS;}
+duration {return DURATION;}
 true {return TRUE;}
 false {return FALSE;}
 
diff --git a/yy/mp.y b/yy/mp.y
index 0f2c9cb8b256a82eeb2fac78114f6026902a24f2..06d76101daf42550ea192d1c963c32d5da6635d2 100644 (file)
--- a/yy/mp.y
+++ b/yy/mp.y
@@ -210,6 +210,9 @@ static int eval_node(struct mp_ast_node *node, struct mp_context *ctx,
        case CHANNELS:
                result->intval= mp_channels(ctx);
                return ST_INTVAL;
+       case DURATION:
+               result->intval= mp_duration(ctx);
+               return ST_INTVAL;
        /* bools */
        case IS_SET:
                arg = node->children[0]->sv.strval;
@@ -327,6 +330,7 @@ bool mp_eval_ast(struct mp_ast_node *root, struct mp_context *ctx)
 %token <node> BITRATE
 %token <node> FREQUENCY
 %token <node> CHANNELS
+%token <node> DURATION
 %token <node> FALSE TRUE
 
 /* keywords without semantic value */
@@ -377,6 +381,7 @@ exp: NUM {$$ = $1;}
        | BITRATE {$$ = mp_new_ast_leaf_node(BITRATE);}
        | FREQUENCY {$$ = mp_new_ast_leaf_node(FREQUENCY);}
        | CHANNELS {$$ = mp_new_ast_leaf_node(CHANNELS);}
+       | DURATION {$$ = mp_new_ast_leaf_node(DURATION);}
 ;
 
 boolexp: IS_SET '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(IS_SET, $3);}