Remove old audio file selector code.
authorAndre Noll <maan@systemlinux.org>
Tue, 23 Oct 2007 00:23:28 +0000 (02:23 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 23 Oct 2007 00:23:28 +0000 (02:23 +0200)
15 files changed:
Makefile.in
afs_common.c [deleted file]
afs_common.h [deleted file]
command.c
configure.ac
error.h
mysql_selector.c [deleted file]
mysql_selector.cmd [deleted file]
playlist_selector.c [deleted file]
playlist_selector.cmd [deleted file]
random_selector.c [deleted file]
random_selector.cmd [deleted file]
server.c
server.ggo
vss.c

index f6df58e..fe5bdb6 100644 (file)
@@ -63,7 +63,7 @@ PICS := $(wildcard pics/paraslash/*.jpg)
 gengetopts := $(wildcard *.ggo)
 gengetopts_c := $(gengetopts:.ggo=.cmdline.c)
 gengetopts_h := $(gengetopts:.ggo=.cmdline.h)
-grutatxt := NEWS README.mysql CREDITS INSTALL README FEATURES
+grutatxt := NEWS CREDITS INSTALL README FEATURES
 grutatxt_html := $(grutatxt:=.html)
 html_in := $(wildcard web/*.in.html)
 gen_html := $(subst web/,web/sync/,$(html_in))
@@ -163,9 +163,7 @@ grab_client.cmdline.h grab_client.cmdline.c: grab_client.ggo
 %_command_list.man: %.cmd
        ./command_util.sh man < $< > $@
 
-server_command_lists = server_command_list.man random_selector_command_list.man \
-       playlist_selector_command_list.man mysql_selector_command_list.man \
-       afs_command_list.man
+server_command_lists = server_command_list.man afs_command_list.man
 man/man1/para_server.1: para_server $(server_command_lists)
        mkdir -p man/man1
        opts="-N `for i in $(server_command_lists); do echo "-i $$i"; done`"; \
@@ -214,9 +212,6 @@ slider.o: slider.c
 krell.o: krell.c
        $(CC) -Wall -O -g -fPIC @GTK_CFLAGS@ -c -o $@ krell.c
 
-mysql_selector.o: mysql_selector.c
-       $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mysql_cppflags@ $<
-
 %.cmdline.o: %.cmdline.c
        $(CC) -c $(CPPFLAGS) $<
 
diff --git a/afs_common.c b/afs_common.c
deleted file mode 100644 (file)
index c45b3d9..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2005-2007 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-
-/** \file afs_common.c Functions common to all audio file selectors. */
-
-#include <sys/types.h>
-#include <dirent.h>
-
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "fd.h"
-#include "server.cmdline.h"
-#include "afh.h"
-#include "afs.h"
-#include "server.h"
-#include "vss.h"
-#include <dirent.h> /* readdir() */
-#include <sys/stat.h> /* stat */
-#include <sys/types.h> /* mode_t */
-
-/**
- * Traverse the given directory recursively.
- *
- * \param dirname The directory to traverse.
- * \param f The function to call for each entry.
- *
- * For each regular file whose filename ends in .yyy, where yyy is a supported
- * audio format, the supplied function \a f is called.  The directory and
- * filename component of the regular file are passed to \a f.
- *
- * \return On success, 1 is returned. Otherwise, this function returns a
- * negative value which indicates the kind of the error.
- */
-int find_audio_files(const char *dirname, int (*f)(const char *, const char *))
-{
-       DIR *dir = NULL;
-       int ret, ret2, cwd_fd;
-       struct dirent *entry;
-
-       ret = para_opendir(dirname, &dir, &cwd_fd);
-       if (ret < 0)
-               return ret;
-       /* scan cwd recursively */
-       while ((entry = readdir(dir))) {
-               mode_t m;
-               char *tmp;
-               struct stat s;
-
-               if (!strcmp(entry->d_name, "."))
-                       continue;
-               if (!strcmp(entry->d_name, ".."))
-                       continue;
-               ret = -E_LSTAT;
-               if (lstat(entry->d_name, &s) == -1)
-                       continue;
-               m = s.st_mode;
-               if (!S_ISREG(m) && !S_ISDIR(m)) /* skip links, sockets, ... */
-                       continue;
-               if (S_ISREG(m)) { /* regular file */
-                       if (guess_audio_format(entry->d_name) < 0)
-                               continue;
-                       ret = f(dirname, entry->d_name);
-                       if (ret < 0)
-                               goto out;
-                       continue;
-               }
-               /* directory */
-               tmp = make_message("%s/%s", dirname, entry->d_name);
-               ret = find_audio_files(tmp, f);
-               free(tmp);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       if (dir)
-               closedir(dir);
-       ret2 = fchdir(cwd_fd);
-       if (ret2 < 0 && ret >= 0)
-               ret = ret2;
-       close(cwd_fd);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
-       return ret;
-}
diff --git a/afs_common.h b/afs_common.h
deleted file mode 100644 (file)
index 82b75d6..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2005-2007 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file afs_common.h data structures common to all audio file selectors */
-
-#include <sys/select.h>
-
-
-int find_audio_files(const char *dirname, int (*f)(const char *, const char *));
-
-/**
- * describes one supported audio file selector
- *
- * There is one such struct for each supported selector.  During the startup
- * part of para_server the \a init() function of the activated selector gets
- * called which fills in all other function pointers.
- *
- */
-struct audio_file_selector {
-/**
- * name name of this selector
- */
-const char *name;
-/**
- * the init routine of the selector
- *
- * It should check its command line options and do all necessary initialization
- * like connecting to a database server.
- *
- * A negative return value indicates an initialization error and means that
- * this selector should be ignored for now (it may later be activated again via
- * the chs command).
- *
- * If \a init() returns success (non-negative return value), it must have
- * initialized in all non-optional function pointers of the given selector
- * struct. Moreover, \a cmd_list must point to a NULL-terminated array which
- * holds the list of all commands that are supported by this selector.
- */
-int (*init)(struct audio_file_selector *self);
-/**
- * list of commands supported by this selector
- */
-struct server_command *cmd_list;
-/**
- * pointer to function returning list of at most \a num audio files to be
- * streamed next
- *
- * \a get_audio_file_list() must return a pointer to a array of at most \a num
- * char* pointers (terminated by a NULL pointer), or NULL on errors.  Both the
- * array and its contents must be dynamically allocated and are freed by the
- * caller.
- *
-*/
-char **(*get_audio_file_list)(unsigned int num);
-/**
- *
- * the update hook
- *
- * The \a update_audio_file pointer is optional and need not be supplied. In this
- * case it is not necessary to init this pointer from within init(). If
- * \a update_audio_file is non-NULL, the function it points to gets called
- * whenever a new audio file was successfully loaded and is going to be
- * streamed by any of paraslash's senders. The full path of the audio file is
- * passed \a update_audio_file().
- *
- */
-void (*update_audio_file)(char *audio_file);
-/**
- *
- * shutdown this selector and free all resources
- *
- * This gets called whenever the audio file selector changes. The reason for
- * this change might be that some user sent the chs command, that para_server
- * receives the HUP signal, or that para_server shuts down. It is assumed to
- * succeed.
- */
-void (*shutdown)(void);
-/**
- *
- * add file descriptors to fd_sets
- *
- * The pre_select function of the activated selector gets called just before
- * para_server enters its main select loop. The selector may add its own file
- * descriptors to the \a rfds or the \a wfds set.
- *
- * \return The highest-numbered file descriptor which was added to either of
- * the two fd sets (or -1 if no file descriptors were added).
- *
- * \sa select(2)
- */
-int (*pre_select)(fd_set *rfds, fd_set *wfds);
-/**
- * handle the file descriptors which are ready for I/O
- *
- * If the pre_select hook added one ore more file descriptors to the read or write
- * set, this is the hook to check the result and do any I/O on those descriptors
- * which are ready for reading/writing.
- */
-void (*post_select)(fd_set *rfds, fd_set *wfds);
-/**
- * each selector has its private data pointer */
-void *private_data;
-};
-
-int mysql_selector_init(struct audio_file_selector*);
-int playlist_selector_init(struct audio_file_selector*);
-int random_selector_init(struct audio_file_selector*);
-
index 7dba2f9..9d902ba 100644 (file)
--- a/command.c
+++ b/command.c
@@ -15,7 +15,6 @@
 #include "para.h"
 #include "error.h"
 #include "server.cmdline.h"
-#include "afs_common.h"
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
index 4d98445..aef9d1e 100644 (file)
@@ -71,8 +71,8 @@ AC_CHECK_FUNCS([atexit dup2 memchr memmove memset \
        strncasecmp strrchr strspn alarm], [],
        [AC_MSG_ERROR([function not found, cannot live without it])])
 
-all_errlist_objs="server mp3_afh afh_common vss command net string signal random_selector time
-daemon stat crypt http_send afs_common close_on_fork playlist_selector ipc dccp
+all_errlist_objs="server mp3_afh afh_common vss command net string signal time
+daemon stat crypt http_send close_on_fork ipc dccp
 dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer
 playlist sha1 rbtree sched audiod grab_client filter_chain wav compress
 http_recv dccp_recv recv_common write_common file_write audiod_command
@@ -87,7 +87,6 @@ recv_ldflags=""
 
 receivers=" http dccp"
 senders=" http dccp"
-selectors=" random playlist"
 
 filter_cmdline_objs="filter.cmdline compress_filter.cmdline"
 filter_errlist_objs="filter_chain wav compress filter string stdin stdout sched fd"
@@ -108,10 +107,9 @@ audiod_errlist_objs="audiod signal string daemon stat net
 audiod_ldflags=""
 audiod_audio_formats=""
 
-server_cmdline_objs="server.cmdline server_command_list random_selector_command_list
-       playlist_selector_command_list afs_command_list"
-server_errlist_objs="server afh_common mp3_afh vss command net string signal random_selector
-       time daemon stat crypt http_send afs_common close_on_fork playlist_selector
+server_cmdline_objs="server.cmdline server_command_list afs_command_list"
+server_errlist_objs="server afh_common mp3_afh vss command net string signal
+       time daemon stat crypt http_send close_on_fork
        ipc dccp dccp_send fd user_list chunk_queue afs osl aft mood score attribute
        blob playlist sha1 rbtree sched"
 server_ldflags=""
@@ -302,44 +300,6 @@ AC_CHECK_LIB([SDL_image], [SDL_Init], [extras="$extras para_sdl_gui"], [
 AC_CHECK_HEADER(SDL/SDL.h, [], 
        [AC_MSG_WARN([SDL/SDL.h not found])])
 
-########################################################################### mysql
-have_mysql="yes"
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LD_FLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-AC_ARG_WITH(mysql_headers, [AC_HELP_STRING(--with-mysql-headers=dir,
-       [look for mysql.h also in dir])])
-if test -n "$with_mysql_headers"; then
-       mysql_cppflags="-I$with_mysql_headers"
-       CPPFLAGS="$CPPFLAGS $mysql_cppflags"
-fi
-AC_ARG_WITH(mysql_libs, [AC_HELP_STRING(--with-mysql-libs=dir,
-       [look for libmysqlclient also in dir])])
-if test -n "$with_mysql_libs"; then
-       mysql_libs="-L$with_mysql_libs"
-       LDFLAGS="$LDFLAGS $mysql_libs"
-fi
-AC_CHECK_HEADER(mysql/mysql.h, [], [
-       have_mysql="no"
-])
-AC_CHECK_LIB([mysqlclient], [mysql_init], [], [
-       have_mysql="no"
-])
-if test "$have_mysql" = "yes"; then
-       selectors="$selectors mysql"
-       server_ldflags="$server_ldflags $mysql_libs -lmysqlclient"
-       server_errlist_objs="$server_errlist_objs mysql_selector"
-       all_errlist_objs="$all_errlist_objs mysql_selector"
-       server_cmdline_objs="$server_cmdline_objs mysql_selector_command_list"
-       AC_SUBST(mysql_cppflags)
-       AC_SUBST(mysql_libs)
-       AC_DEFINE(HAVE_MYSQL, 1, [define to 1 to turn on mysql support])
-else
-       AC_MSG_WARN([cannot build mysql-based audio file selector])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
 ########################################################################### ogg
 have_ogg="yes"
 OLD_CPPFLAGS="$CPPFLAGS"
@@ -708,12 +668,11 @@ AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMAT_ARRAY, $names, array of audio formats sup
 
 AC_OUTPUT
 AC_MSG_NOTICE([creating Makefile.deps])
-gcc -MM -MG $mysql_cppflags $faad_cppflags $mad_cppflags $ortp_cppflags $oggvorbis_cppflags *.c > Makefile.deps
+gcc -MM -MG $faad_cppflags $mad_cppflags $ortp_cppflags $oggvorbis_cppflags *.c > Makefile.deps
 AC_MSG_NOTICE([
 paraslash configuration:
 ~~~~~~~~~~~~~~~~~~~~~~~~
 unix socket credentials: $have_ucred
-audio file selectors supported by para_server: $selectors
 audio formats supported by para_server: $server_audio_formats
 senders supported by para_server/para_send: $senders
 receivers supported by para_audiod/para_recv: $receivers
diff --git a/error.h b/error.h
index 9d1f7bb..56b26d7 100644 (file)
--- a/error.h
+++ b/error.h
@@ -316,10 +316,6 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_AFS_CODE, "received junk from afs"), \
 
 
-#define AFS_COMMON_ERRORS \
-       PARA_ERROR(LSTAT, "lstat error"), \
-
-
 #define CRYPT_ERRORS \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(PUBLIC_KEY, "can not read public key"), \
@@ -334,33 +330,6 @@ extern const char **para_errlist[];
        PARA_ERROR(SEND_QUEUED_CHUNK, "failed to send queued chunk"), \
 
 
-#define RANDOM_SELECTOR_ERRORS \
-       PARA_ERROR(NOTHING_FOUND, "no audio files found"), \
-
-
-#define MYSQL_SELECTOR_ERRORS \
-       PARA_ERROR(MYSQL_SYNTAX, "mysql syntax error"), \
-       PARA_ERROR(NOTCONN, "not connected to mysql server"), \
-       PARA_ERROR(TOOBIG, "mysql: file too large"), \
-       PARA_ERROR(NAMETOOLONG, "mysql: name too long"), \
-       PARA_ERROR(QFAILED, "mysql query failed"), \
-       PARA_ERROR(NOROW, "row is NULL"), \
-       PARA_ERROR(NOATTS, "can not get attributes from mysql table"), \
-       PARA_ERROR(NORESULT, "error while fetching mysql result"), \
-       PARA_ERROR(EMPTY_RESULT, "result is empty"), \
-       PARA_ERROR(ESCAPE, "can not escape string"), \
-       PARA_ERROR(GET_AUDIO_FILE, "can not get current audio file"), \
-       PARA_ERROR(GET_STREAM, "can not get current stream"), \
-       PARA_ERROR(NO_STREAM, "no such stream"), \
-       PARA_ERROR(AUDIO_FILE, "no such audio file"), \
-       PARA_ERROR(GET_QUERY, "can not get query for specified stream"), \
-       PARA_ERROR(TMPFILE, "error while writing temporary file"), \
-       PARA_ERROR(META, "can not get meta data"), \
-       PARA_ERROR(MYSQL_INIT, "can not initialize mysql connection"), \
-       PARA_ERROR(NO_MYSQL_PASSWD, "fatal: no mysql passord given"), \
-       PARA_ERROR(NO_AF_DIR, "fatal: audio file directory not given"), \
-
-
 #define COMMAND_ERRORS \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
        PARA_ERROR(AUTH, "did not receive auth request"), \
@@ -374,10 +343,6 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_USER, "you don't exist. Go away."), \
 
 
-#define PLAYLIST_SELECTOR_ERRORS \
-       PARA_ERROR(LOAD_PLAYLIST, "failed to load playlist"), \
-
-
 #define IPC_ERRORS \
        PARA_ERROR(SEM_GET, "failed to create semaphore"), \
        PARA_ERROR(SEM_REMOVE, "can not remove semaphore"), \
diff --git a/mysql_selector.c b/mysql_selector.c
deleted file mode 100644 (file)
index 5b145dc..0000000
+++ /dev/null
@@ -1,2325 +0,0 @@
-/*
- * Copyright (C) 1999-2007 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file mysql_selector.c para_server's mysql-based audio file selector */
-
-/** \cond some internal constants */
-#define MEDIUM_BLOB_SIZE 16777220 /*  (2**24 + 4) */
-#define BLOB_SIZE 65539 /* (2**16 + 3) */
-/** \endcond */
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "server.cmdline.h"
-#include "afh.h"
-#include "afs.h"
-#include "server.h"
-#include "vss.h"
-#include "afs_common.h"
-#include <mysql/mysql.h>
-#include <mysql/mysql_version.h>
-#include <regex.h>
-#include "net.h"
-#include "list.h"
-#include "user_list.h"
-#include "mysql_selector_command_list.h"
-#include "ipc.h"
-
-/** pointer to the shared memory area */
-extern struct misc_meta_data *mmd;
-
-static void *mysql_ptr = NULL;
-static int mysql_lock;
-
-/**
- * contains name/replacement pairs used by s_a_r_list()
- *
- * \sa s_a_r()
- */
-struct para_macro {
-       /** the name of the macro */
-       const char *name;
-       /** the replacement text */
-       const char *replacement;
-};
-
-static const struct para_macro mysql_macro_list[] = {
-       {       .name = "IS_N_SET",
-               .replacement = "(data.%s != '1')"
-       }, {
-               .name = "IS_SET",
-               .replacement = "(data.%s = '1')"
-       }, {
-               .name = "PICID",
-               .replacement = "%sdata.Pic_Id"
-       }, {
-               .name = "NAME_LIKE",
-               .replacement = "(data.name like '%s')"
-       }, {
-               .name = "LASTPLAYED",
-               .replacement = "%sFLOOR((UNIX_TIMESTAMP(now())"
-                       "-UNIX_TIMESTAMP(data.Lastplayed))/60)"
-       }, {
-               .name = "NUMPLAYED",
-               .replacement = "%sdata.Numplayed"
-       }, {
-               .name = NULL,
-       }
-};
-
-/**
- * Simple search and replace routine.
- *
- * \param src Source String.
- * \param macro_name The name of the macro.
- * \param replacement The replacement format string.
- *
- * In \p src, replace each occurence of \p macro_name(arg) by the string
- * determined by the \p replacement format string. \p replacement may (but
- * needs not) contain a single string conversion specifier (%s) which gets
- * replaced by \p arg.
- *
- * \return A string in which all matches in \p src are replaced, or \p NULL if
- * an error was encountered. Caller must free the result.
- *
- * \sa regcomp(3)
- */
-__must_check __malloc static char *s_a_r(const char *src, const char* macro_name,
-               const char *replacement)
-{
-       regex_t preg;
-       size_t  nmatch = 1;
-       regmatch_t pmatch[1];
-       int eflags = 0;
-       char *dest = NULL;
-       const char *bufptr = src;
-
-       if (!macro_name || !replacement || !src)
-               return para_strdup(src);
-       if (regcomp(&preg, macro_name, 0) != 0)
-               return NULL;
-       while (regexec(&preg,  bufptr, nmatch, pmatch, eflags)
-                       != REG_NOMATCH) {
-               char *tmp, *arg, *o_bracket, *c_bracket;
-
-               o_bracket = strchr(bufptr + pmatch[0].rm_so, '(');
-               c_bracket = o_bracket? strchr(o_bracket, ')') : NULL;
-               if (!c_bracket)
-                       goto out;
-               tmp = para_strdup(bufptr);
-               tmp[pmatch[0].rm_so] = '\0';
-               dest = para_strcat(dest, tmp);
-               free(tmp);
-
-               arg = para_strdup(o_bracket + 1);
-               arg[c_bracket - o_bracket - 1] = '\0';
-               tmp = make_message(replacement, arg);
-               free(arg);
-               dest = para_strcat(dest, tmp);
-               free(tmp);
-               bufptr = c_bracket;
-               bufptr++;
-       }
-       dest = para_strcat(dest, bufptr);
-//     PARA_DEBUG_LOG("%s: returning %s\n", __func__, dest);
-out:
-       regfree(&preg);
-       return dest;
-}
-
-/**
- * replace a string according to a list of macros
- *
- * \param macro_list the array containing a macro/replacement pairs.
- * \param src the source string
- *
- * This function just calls s_a_r() for each element of \p macro_list.
- *
- * \return \p NULL if one of the underlying calls to \p s_a_r returned \p NULL.
- * Otherwise the completely expanded version of \p src is returned.
- */
-__must_check __malloc static char *s_a_r_list(const struct para_macro *macro_list,
-               char *src)
-{
-       const struct para_macro *mp = macro_list;
-       char *ret = NULL, *tmp = para_strdup(src);
-
-       while (mp->name) {
-               ret = s_a_r(tmp, mp->name, mp->replacement);
-               free(tmp);
-               if (!ret) /* syntax error */
-                       return NULL;
-               tmp = ret;
-               mp++;
-       }
-       //PARA_DEBUG_LOG("%s: returning %s\n", __func__, dest);
-       return ret;
-}
-
-static int lockless_real_query(const char *query)
-{
-       if (!mysql_ptr)
-               return -E_NOTCONN;
-       PARA_DEBUG_LOG("%s\n", query);
-       if (mysql_real_query(mysql_ptr, query, strlen(query))) {
-               PARA_ERROR_LOG("real_query error (%s)\n",
-                       mysql_error(mysql_ptr));
-               return -E_QFAILED;
-       }
-       return 1;
-}
-
-static int real_query(const char *query)
-{
-       int ret;
-
-       mutex_lock(mysql_lock);
-       ret = lockless_real_query(query);
-       mutex_unlock(mysql_lock);
-       return ret;
-}
-
-/*
- * Use open connection given by mysql_ptr to query server. Returns a
- * result pointer on succes and NULL on errors
- */
-static struct MYSQL_RES *get_result(const char *query)
-{
-       void *result = NULL;
-
-       mutex_lock(mysql_lock);
-       if (lockless_real_query(query) < 0)
-               goto out;
-       result = mysql_store_result(mysql_ptr);
-       if (!result)
-               PARA_ERROR_LOG("%s", "store_result error\n");
-out:
-       mutex_unlock(mysql_lock);
-       return result;
-}
-/*
- * write input from fd to dynamically allocated char array,
- * but maximal max_size byte. Return size.
- */
-static int fd2buf(int fd, char **buf_ptr, size_t max_size)
-{
-       const size_t chunk_size = 1024;
-       size_t size = 2048;
-       char *buf = para_malloc(size * sizeof(char)), *p = buf;
-       int ret;
-
-       while ((ret = recv_bin_buffer(fd, p, chunk_size)) > 0) {
-               p += ret;
-               if ((p - buf) + chunk_size >= size) {
-                       char *tmp;
-
-                       size *= 2;
-                       if (size > max_size) {
-                               ret = -E_TOOBIG;
-                               goto out;
-                       }
-                       tmp = para_realloc(buf, size);
-                       p = (p - buf) + tmp;
-                       buf = tmp;
-               }
-       }
-       if (ret < 0)
-               goto out;
-       *buf_ptr = buf;
-       ret = p - buf;
-out:
-       if (ret < 0 && buf)
-               free(buf);
-       return ret;
-}
-
-static char *escape_blob(const char* old, size_t size)
-{
-       char *new;
-
-       if (!mysql_ptr)
-               return NULL;
-       new = para_malloc(2 * size * sizeof(char) + 1);
-       mysql_real_escape_string(mysql_ptr, new, old, size);
-       return new;
-}
-
-static char *escape_str(const char* old)
-{
-       return escape_blob(old, strlen(old));
-}
-
-static char *escaped_basename(const char *name)
-{
-       char *esc, *bn = para_basename(name);
-
-       if (!bn)
-               return NULL;
-       esc = escape_str(bn);
-       free(bn);
-       return esc;
-}
-
-/*
- * new attribute
- */
-int com_na(__a_unused int fd, int argc, char * const * argv)
-{
-       char *q, *tmp;
-       int ret;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       q = make_message("alter table data add %s char(1) "
-               "not null default 0", tmp);
-       free(tmp);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * delete attribute
- */
-int com_da(__a_unused int fd, int argc, char * const * argv)
-{
-       char *q, *tmp;
-       int ret;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       q = make_message("alter table data drop %s", tmp);
-       free(tmp);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/* stradd/pic_add */
-static int com_stradd_picadd(int fd, int argc, char * const * argv)
-{
-       char *blob = NULL, *esc_blob = NULL, *q = NULL, *tmp = NULL;
-       const char *fmt, *del_fmt;
-       int ret, stradd = strcmp(argv[0], "picadd");
-       size_t size;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       if (strlen(argv[1]) >= MAXLINE - 1)
-               return -E_NAMETOOLONG;
-       if (!mysql_ptr)
-               return -E_NOTCONN;
-       if (stradd) {
-               size = BLOB_SIZE;
-               fmt = "insert into streams (name, def) values ('%s','%s')";
-               del_fmt="delete from streams where name='%s'";
-       } else {
-               size = MEDIUM_BLOB_SIZE;
-               fmt = "insert into pics (name, pic) values ('%s','%s')";
-               del_fmt="delete from pics where pic='%s'";
-       }
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       q = make_message(del_fmt, tmp);
-       free(tmp);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               return ret;
-       if ((ret = send_buffer(fd, AWAITING_DATA_MSG) < 0))
-               return ret;
-       if ((ret = fd2buf(fd, &blob, size)) < 0)
-               return ret;
-       size = ret;
-       if (stradd)
-               blob[size] = '\0';
-       ret = -E_ESCAPE;
-       esc_blob = escape_blob(blob, size);
-       if (!esc_blob)
-               goto out;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               goto out;
-       q = make_message(fmt, tmp, esc_blob);
-       ret = real_query(q);
-out:
-       free(blob);
-       free(esc_blob);
-       free(tmp);
-       free(q);
-       return ret;
-}
-
-/* stradd */
-int com_stradd(int fd, int argc, char * const * argv)
-{
-       return com_stradd_picadd(fd, argc, argv);
-}
-
-/* pic_add */
-int com_picadd(int fd, int argc, char * const * argv)
-{
-       return com_stradd_picadd(fd, argc, argv);
-}
-
-/*
- * print results to fd
- */
-static int print_results(int fd, void *result,
-               my_ulonglong top, my_ulonglong left,
-               my_ulonglong bottom, my_ulonglong right)
-{
-       unsigned int i,j;
-       int ret;
-       MYSQL_ROW row;
-
-       for (i = top; i <= bottom; i++) {
-               row = mysql_fetch_row(result);
-               if (!row || !row[0])
-                       return -E_NOROW;
-               for (j = left; j <= right; j++) {
-                       ret = send_va_buffer(fd, j == left? "%s" : "\t%s",
-                                       row[j]?  row[j] : "NULL");
-                       if (ret < 0)
-                               return ret;
-               }
-               ret = send_buffer(fd, "\n");
-               if (ret < 0)
-                       return ret;
-       }
-       return 0;
-}
-
-/*
- * verbatim
- */
-int com_verb(int fd, int argc, char * const * argv)
-{
-       void *result = NULL;
-       int ret;
-       my_ulonglong num_rows, num_fields, top = 0, left = 0;
-       char *tmp;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       result = get_result(tmp);
-       free(tmp);
-       if (!result)
-               /* return success, because it's ok to have no results */
-               return 1;
-       num_fields = mysql_field_count(mysql_ptr);
-       num_rows = mysql_num_rows(result);
-       ret = 1;
-       if (num_fields && num_rows)
-               ret = print_results(fd, result, top, left, num_rows - 1,
-                       num_fields - 1);
-       mysql_free_result(result);
-       return ret;
-}
-
-/* returns NULL on errors or if there are no atts defined yet */
-static void *get_all_attributes(void)
-{
-       void *result = get_result("desc data");
-       unsigned int num_rows;
-
-       if (!result)
-               return NULL;
-       num_rows = mysql_num_rows(result);
-       if (num_rows < 5) {
-               mysql_free_result(result);
-               return NULL;
-       }
-       mysql_data_seek(result, (my_ulonglong)4); /* skip Lastplayed, Numplayed... */
-       return result;
-}
-
-/*
- * list all attributes
- */
-int com_laa(int fd, int argc, __a_unused char * const * argv)
-{
-       void *result;
-       int ret;
-       my_ulonglong top = 0, left = 0, bottom, right = 0;
-
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       result = get_all_attributes();
-       if (!result)
-               return -E_NOATTS;
-       bottom = mysql_num_rows(result);
-       if (bottom < 5)
-               return -E_MYSQL_SYNTAX;
-       bottom -= 5;
-       ret = print_results(fd, result, top, left, bottom, right);
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * history
- */
-int com_hist(int fd, int argc, char * const * argv)
-{
-       int ret;
-       void *result = NULL;
-       char *q, *atts;
-       my_ulonglong num_rows, top = 0, left = 0, right = 1;
-
-       if (argc > 3)
-               return -E_MYSQL_SYNTAX;
-       if (argc > 1) {
-               char *tmp = escape_str(argv[1]);
-               if (!tmp)
-                       return -E_ESCAPE;
-               atts = make_message("where %s = '1'", tmp);
-               free(tmp);
-       } else
-               atts = para_strdup(NULL);
-
-       q = make_message("select name, to_days(now()) - to_days(lastplayed) from "
-               "data %s order by lastplayed", atts);
-       free(atts);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return -E_NORESULT;
-       num_rows = mysql_num_rows(result);
-       ret = 1;
-       if (num_rows)
-               ret = print_results(fd, result, top, left, num_rows - 1, right);
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * get last num audio files
- */
-int com_last(int fd, int argc, char * const * argv)
-{
-       void *result = NULL;
-       char *q;
-       int num, ret;
-       my_ulonglong top = 0, left = 0, right = 0;
-
-       if (argc < 2)
-               num = 10;
-       else
-               num = atoi(argv[1]);
-       if (!num)
-               return -E_MYSQL_SYNTAX;
-       q = make_message("select name from data order by lastplayed desc "
-               "limit %u", num);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return -E_NORESULT;
-       ret = print_results(fd, result, top, left, mysql_num_rows(result) - 1,
-               right);
-       mysql_free_result(result);
-       return ret;
-}
-
-int com_mbox(int fd, int argc, char * const * argv)
-{
-       void *result;
-       MYSQL_ROW row;
-       int ret;
-       my_ulonglong num_rows, num_fields, top = 0, left = 0;
-       char *query = para_strdup("select concat('From foo@localhost ', "
-               "date_format(Lastplayed, '%a %b %e %T %Y'), "
-               "'\nReceived: from\nTo: bar\n");
-
-       ret = -E_NOATTS;
-       result = get_all_attributes();
-       if (!result)
-               goto out;
-       ret = -E_NOROW;
-       while ((row = mysql_fetch_row(result))) {
-               char *tmp;
-
-               if (!row[0])
-                       goto out;
-               tmp = make_message("%sX-Attribute-%s: ', %s, '\n", query,
-                       row[0], row[0]);
-               free(query);
-               query = tmp;
-       }
-       query = para_strcat(query,
-               "From: a\n"
-               "Subject: "
-               "', name, '"
-               "\n\n\n"
-               "') from data"
-       );
-       if (argc >= 2) {
-               char *esc = escape_str(argv[1]), *tmp;
-               ret = -E_ESCAPE;
-               if (!esc)
-                       goto out;
-               tmp = make_message("%s where name LIKE '%s'", query, esc);
-               free(esc);
-               free(query);
-               query = tmp;
-       }
-       mysql_free_result(result);
-       ret = -E_NORESULT;
-       result = get_result(query);
-       if (!result)
-               goto out;
-       ret = -E_EMPTY_RESULT;
-       num_fields = mysql_field_count(mysql_ptr);
-       num_rows = mysql_num_rows(result);
-       if (!num_fields || !num_rows)
-               goto out;
-       ret = print_results(fd, result, top, left, num_rows - 1,
-               num_fields - 1);
-out:
-       free(query);
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/*
- * get attributes by name. If verbose is not 0, this function returns a string
- * of the form 'att1="0",att2="1"'... which is used in com_cam() for
- * constructing a mysql update query. Otherwise the space-separated list of all
- * attributes which are set in the audio file given by name is returned.  Never
- * returns NULL in *NON VERBOSE* mode.
- */
-static char *get_atts(char *name, int verbose)
-{
-       char *atts = NULL, *buf, *ebn;
-       void *result = NULL, *result2 = NULL;
-       MYSQL_ROW row, row2;
-       int i;
-       my_ulonglong num_fields, offset = 4; /* skip Lastplayed, Numplayed... */
-
-
-       result2 = get_all_attributes();
-       if (!result2)
-               goto out;
-       ebn = escaped_basename(name);
-       if (!ebn)
-               goto out;
-       buf = make_message("select * from data where name='%s'", ebn);
-       free(ebn);
-       result = get_result(buf);
-       free(buf);
-       if (!result)
-               goto out;
-       num_fields = mysql_num_fields(result);
-       if (num_fields < 5)
-               goto out;
-       mysql_data_seek(result2, offset);
-       row = mysql_fetch_row(result);
-       if (!row)
-               goto out;
-       for (i = 4; i < num_fields; i++) {
-               int is_set = row[i] && !strcmp(row[i], "1");
-               row2 = mysql_fetch_row(result2);
-               if (!row2 || !row2[0])
-                       goto out;
-               if (atts && (verbose || is_set))
-                       atts = para_strcat(atts, verbose? "," : " ");
-               if (is_set || verbose)
-                       atts = para_strcat(atts, row2[0]);
-               if (verbose)
-                       atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\"");
-       }
-out:
-       if (result2)
-               mysql_free_result(result2);
-       if (result)
-               mysql_free_result(result);
-       if (!atts && !verbose)
-               atts = para_strdup("(none)");
-       return atts;
-}
-
-/* never returns NULL in verbose mode */
-static char *get_meta(char *name, int verbose)
-{
-       MYSQL_ROW row;
-       void *result = NULL;
-       char *ebn, *q, *ret = NULL;
-       const char *verbose_fmt =
-               "select concat('lastplayed: ', "
-               "(to_days(now()) - to_days(lastplayed)),"
-               "' day(s). numplayed: ', numplayed, "
-               "', pic: ', pic_id) "
-               "from data where name = '%s'";
-       /* is that really needed? */
-       const char *fmt = "select concat('lastplayed=\\'', lastplayed, "
-               "'\\', numplayed=\\'', numplayed, "
-               "'\\', pic_id=\\'', pic_id, '\\'') "
-               "from data where name = '%s'";
-
-       if (!(ebn = escaped_basename(name)))
-               goto out;
-       q = make_message(verbose? verbose_fmt : fmt, ebn);
-       free(ebn);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               goto out;
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto out;
-       ret = para_strdup(row[0]);
-out:
-       if (result)
-               mysql_free_result(result);
-       if (!ret && verbose)
-               ret = para_strdup("(not yet played)");
-       return ret;
-}
-
-static char *get_dir(char *name)
-{
-       char *ret = NULL, *q, *ebn;
-       void *result;
-       MYSQL_ROW row;
-
-       if (!(ebn = escaped_basename(name)))
-               return NULL;
-       q = make_message("select dir from dir where name = '%s'", ebn);
-       free(ebn);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return NULL;
-       row = mysql_fetch_row(result);
-       if (row && row[0])
-               ret = para_strdup(row[0]);
-       mysql_free_result(result);
-       return ret;
-}
-
-/* never returns NULL */
-static char *get_current_stream(void)
-{
-       char *ret;
-       MYSQL_ROW row;
-       void *result = get_result("select def from streams where "
-               "name = 'current_stream'");
-
-       if (!result)
-               goto err_out;
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto err_out;
-       ret = para_strdup(row[0]);
-       mysql_free_result(result);
-       return ret;
-err_out:
-       if (result)
-               mysql_free_result(result);
-       return para_strdup("(none)");
-}
-
-/*
- * Read stream definition of stream streamname and construct mysql
- * query. Return NULL on errors. If streamname is NULL, use current
- * stream. If that is also NULL, use query that selects everything.
- * If filename is NULL, query will list everything, otherwise only
- * the score of given file.
- */
-static char *get_query(const char *streamname, char *filename, int with_path)
-{
-       char *accept_opts = NULL, *deny_opts = NULL, *score = NULL;
-       char *where_clause, *order, *query;
-       char command[255] = ""; /* buffer for sscanf */
-       void *result;
-       MYSQL_ROW row;
-       char *end, *tmp;
-       char *select_clause = NULL;
-       if (!streamname)
-               tmp = get_current_stream();
-       else {
-               tmp = escape_str(streamname);
-               if (!tmp)
-                       return NULL;
-       }
-       if (!strcmp(tmp, "(none)")) {
-               free(tmp);
-               if (filename) {
-                       char *ret, *ebn = escaped_basename(filename);
-                       if (!ebn)
-                               return NULL;
-                       ret = make_message("select to_days(now()) - "
-                               "to_days(lastplayed) from data "
-                               "where name = '%s'", ebn);
-                       free(ebn);
-                       return ret;
-               }
-               if (with_path)
-                       return make_message(
-                               "select concat(dir.dir, '/', dir.name) "
-                               "from data, dir where dir.name = data.name "
-                               "order by data.lastplayed"
-                       );
-               return make_message(
-                       "select name from data where name is not NULL "
-                       "order by lastplayed"
-               );
-       }
-       free(tmp);
-       query = make_message("select def from streams where name = '%s'",
-               streamname);
-       result = get_result(query);
-       free(query);
-       query = NULL;
-       if (!result)
-               goto out;
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto out;
-       end = row[0];
-       while (*end) {
-               int n;
-               char *arg, *line = end;
-
-               if (!(end = strchr(line, '\n')))
-                       break;
-               *end = '\0';
-               end++;
-               if (sscanf(line, "%200s%n", command, &n) < 1)
-                       continue;
-               arg = line + n;
-               if (!strcmp(command, "accept:")) {
-                       char *tmp2 = s_a_r_list(mysql_macro_list, arg);
-                       if (accept_opts)
-                               accept_opts = para_strcat(
-                                       accept_opts, " or ");
-                       accept_opts = para_strcat(accept_opts, tmp2);
-                       free(tmp2);
-                       continue;
-               }
-               if (!strcmp(command, "deny:")) {
-                       char *tmp2 = s_a_r_list(mysql_macro_list, arg);
-                       if (deny_opts)
-                               deny_opts = para_strcat(deny_opts, " or ");
-                       deny_opts = para_strcat(deny_opts, tmp2);
-                       free(tmp2);
-                       continue;
-               }
-               if (!score && !strcmp(command, "score:"))
-                       score = s_a_r_list(mysql_macro_list, arg);
-       }
-       if (!score) {
-               score = s_a_r_list(mysql_macro_list, conf.mysql_default_score_arg);
-               if (!score)
-                       goto out;
-       }
-       if (filename) {
-               char *ebn = escaped_basename(filename);
-               if (!ebn)
-                       goto out;
-               select_clause = make_message("select %s from data ", score);
-               free(score);
-               where_clause = make_message( "where name = '%s' ", ebn);
-               free(ebn);
-               order = para_strdup("");
-               goto write_query;
-       }
-       select_clause = para_strdup(with_path?
-               "select concat(dir.dir, '/', dir.name) from data, dir "
-               "where dir.name = data.name "
-               :
-               "select name from data where name is not NULL");
-       order = make_message("order by -(%s)", score);
-       free(score);
-       if (accept_opts && deny_opts) {
-               where_clause = make_message("and ((%s) and not (%s)) ",
-                       accept_opts, deny_opts);
-               goto write_query;
-       }
-       if (accept_opts && !deny_opts) {
-               where_clause = make_message("and (%s) ", accept_opts);
-               goto write_query;
-       }
-       if (!accept_opts && deny_opts) {
-               where_clause = make_message("and not (%s) ", deny_opts);
-               goto write_query;
-       }
-       where_clause = para_strdup("");
-write_query:
-       query = make_message("%s %s %s", select_clause, where_clause, order);
-       free(order);
-       free(select_clause);
-       free(where_clause);
-out:
-       if (accept_opts)
-               free(accept_opts);
-       if (deny_opts)
-               free(deny_opts);
-       if (result)
-               mysql_free_result(result);
-       return query;
-}
-
-
-
-/*
- * This is called from server and from some commands. Name must not be NULL
- * Never returns NULL.
- */
-static char *get_selector_info(char *name)
-{
-       char *meta, *atts, *info, *dir, *query, *stream;
-       void *result = NULL;
-       MYSQL_ROW row = NULL;
-
-       if (!name)
-               return para_strdup("(none)");
-       stream = get_current_stream();
-       meta = get_meta(name, 1);
-       atts = get_atts(name, 0);
-       dir = get_dir(name);
-       /* get score */
-       query = get_query(stream, name, 0); /* FIXME: pass stream == NULL instead? */
-       if (!query)
-               goto write;
-       result = get_result(query);
-       free(query);
-       if (result)
-               row = mysql_fetch_row(result);
-write:
-       info = make_message("dbinfo1:dir: %s\n"
-               "dbinfo2:stream: %s, %s, score: %s\n"
-               "dbinfo3:%s\n",
-               dir? dir : "(not contained in table)",
-               stream, meta, 
-               (result && row && row[0])? row[0] : "(no score)",
-               atts);
-       free(dir);
-       free(meta);
-       free(atts);
-       free(stream);
-       if (result)
-               mysql_free_result(result);
-       return info;
-}
-
-/* might return NULL */
-static char *get_current_audio_file(void)
-{
-       char *name;
-       mmd_lock();
-       name = para_basename(mmd->filename);
-       mmd_unlock();
-       return name;
-}
-
-/* If called as child, mmd_lock must be held */
-static void update_mmd(char *info)
-{
-       PARA_DEBUG_LOG("%s", "updating shared memory area\n");
-       strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1);
-       mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
-}
-
-static void refresh_selector_info(void)
-{
-       char *name = get_current_audio_file();
-       char *info;
-
-       if (!name)
-               return;
-       info = get_selector_info(name);
-       free(name);
-       mmd_lock();
-       update_mmd(info);
-       mmd_unlock();
-       free(info);
-}
-
-/* list attributes / print database info */
-static int com_la_info(int fd, int argc, char * const * argv)
-{
-       char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
-       int ret, la = strcmp(argv[0], "info");
-
-       if (argc < 2) {
-               ret = -E_GET_AUDIO_FILE;
-               if (!(name = get_current_audio_file()))
-                       goto out;
-               ret = send_va_buffer(fd, "%s\n", name);
-               if (ret < 0)
-                       goto out;
-       } else {
-               ret = -E_ESCAPE;
-               if (!(name = escaped_basename(argv[1])))
-                       goto out;
-       }
-       meta = get_meta(name, 1);
-       atts = get_atts(name, 0);
-       dir = get_dir(name);
-       if (la)
-               ret = send_va_buffer(fd, "%s\n", atts);
-       else
-               ret = send_va_buffer(fd, "dir: %s\n" "%s\n" "attributes: %s\n",
-                       dir? dir : "(not contained in table)", meta, atts);
-out:
-       free(meta);
-       free(atts);
-       free(dir);
-       free(name);
-       return ret;
-}
-
-/* list attributes */
-int com_la(int fd, int argc, char * const * argv)
-{
-       return com_la_info(fd, argc, argv);
-}
-
-/* print database info */
-int com_info(int fd, int argc, char * const * argv)
-{
-       return com_la_info(fd, argc, argv);
-}
-
-static int change_stream(const char *stream)
-{
-       char *query;
-       int ret;
-       query = make_message("update streams set def='%s' "
-               "where name = 'current_stream'", stream);
-       ret = real_query(query);
-       free(query);
-       return ret;
-}
-
-static int get_pic_id_by_name(char *name)
-{
-       char *q, *ebn;
-       void *result = NULL;
-       int ret;
-       MYSQL_ROW row;
-
-       if (!(ebn = escaped_basename(name)))
-               return -E_ESCAPE;
-       q = make_message("select pic_id from data where name = '%s'", ebn);
-       free(ebn);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return -E_NORESULT;
-       row = mysql_fetch_row(result);
-       ret = -E_NOROW;
-       if (row && row[0])
-               ret = atoi(row[0]);
-       mysql_free_result(result);
-       return ret;
-}
-
-static int remove_entry(const char *name)
-{
-       char *q, *ebn = escaped_basename(name);
-       int ret = -E_ESCAPE;
-
-       if (!ebn)
-               goto out;
-       q = make_message("delete from data where name = '%s'", ebn);
-       real_query(q); /* ignore errors */
-       free(q);
-       q = make_message("delete from dir where name = '%s'", ebn);
-       real_query(q); /* ignore errors */
-       free(q);
-       ret = 1;
-out:
-       free(ebn);
-       return ret;
-}
-
-static int add_entry(const char *name)
-{
-       char *q, *dn, *ebn = NULL, *edn = NULL;
-       int ret;
-
-       if (!name || !*name)
-               return -E_MYSQL_SYNTAX;
-       ebn = escaped_basename(name);
-       if (!ebn)
-               return -E_ESCAPE;
-       ret = -E_MYSQL_SYNTAX;
-       dn = para_dirname(name);
-       if (!dn)
-               goto out;
-       ret = -E_ESCAPE;
-       edn = escape_str(dn);
-       free(dn);
-       if (!edn || !*edn)
-               goto out;
-       q = make_message("insert into data (name, pic_id) values "
-                       "('%s', '%s')", ebn, "1");
-       ret = real_query(q);
-//     ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
-       free(q);
-       if (ret < 0)
-               goto out;
-       q = make_message("insert into dir (name, dir) values "
-                       "('%s', '%s')", ebn, edn);
-//     ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
-       ret = real_query(q);
-       free(q);
-out:
-       if (ebn)
-               free(ebn);
-       if (edn)
-               free(edn);
-       return ret;
-}
-
-/*
- * remove/add entries
- */
-static int com_rm_ne(__a_unused int fd, int argc, char * const * argv)
-{
-       int ne = !strcmp(argv[0], "ne");
-       int i, ret;
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       for (i = 1; i < argc; i++) {
-               ret = remove_entry(argv[i]);
-               if (ret < 0)
-                       return ret;
-               if (!ne)
-                       continue;
-               ret = add_entry(argv[i]);
-               if (ret < 0)
-                       return ret;
-       }
-       return 1;
-}
-
-/*
- * rm
- */
-int com_rm(int fd, int argc, char * const * argv)
-{
-       return com_rm_ne(fd, argc, argv);
-}
-
-/*
- * ne
- */
-int com_ne(int fd, int argc, char * const * argv)
-{
-       return com_ne(fd, argc, argv);
-}
-
-/*
- * mv: rename entry
- */
-int com_mv(__a_unused int fd, int argc, char * const * argv)
-{
-       char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
-       int ret;
-
-       if (argc != 3)
-               return -E_MYSQL_SYNTAX;
-       ret = -E_ESCAPE;
-       ebn1 = escaped_basename(argv[1]);
-       ebn2 = escaped_basename(argv[2]);
-       if (!ebn1 || !ebn2 || !*ebn1 || !*ebn2)
-               goto out;
-       ret = -E_MYSQL_SYNTAX;
-       if (!strcmp(ebn1, ebn2))
-               goto update_dir;
-       remove_entry(argv[2]); /* no need to escape, ignore error */
-       q = make_message("update data set name = '%s' where name = '%s'",
-               ebn2, ebn1);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               goto out;
-       ret = -E_AUDIO_FILE;
-       if (!mysql_affected_rows(mysql_ptr))
-               goto out;
-       q = make_message("update dir set name = '%s' where name = '%s'",
-               ebn2, ebn1);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               goto out;
-update_dir:
-       ret = 1;
-       dn = para_dirname(argv[2]);
-       if (!dn)
-               goto out;
-       ret = -E_ESCAPE;
-       edn = escape_str(dn);
-       free(dn);
-       if (!edn)
-               goto out;
-       ret = 1;
-       if (!*edn)
-               goto out;
-       q = make_message("update dir set dir = '%s' where name = '%s'",
-               edn, ebn2);
-       ret = real_query(q);
-       free(q);
-out:
-       free(edn);
-       free(ebn1);
-       free(ebn2);
-       return ret;
-}
-
-/*
- * set field
- */
-static int com_set(__a_unused int fd, int argc, char * const * argv)
-{
-       char *q, *ebn;
-       long unsigned id;
-       int i, ret;
-       const char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
-
-       if (argc < 3)
-               return -E_MYSQL_SYNTAX;
-       id = atol(argv[1]);
-       for (i = 2; i < argc; i++) {
-               ebn = escaped_basename(argv[i]);
-               if (!ebn)
-                       return -E_ESCAPE;
-               q = make_message("update data set %s = %lu "
-                               "where name = '%s'", field, id, ebn);
-               free(ebn);
-               ret = real_query(q);
-               free(q);
-               if (ret < 0)
-                       return ret;
-       }
-       return 1;
-}
-
-/*
- * snp: set numplayed
- */
-int com_picass(int fd, int argc, char * const * argv)
-{
-       return com_set(fd, argc, argv);
-}
-
-/*
- * snp: set numplayed
- */
-int com_snp(int fd, int argc, char * const * argv)
-{
-       int ret = com_set(fd, argc, argv);
-       if (ret >= 0)
-               refresh_selector_info();
-       return ret;
-}
-
-/*
- * picch: change entry's name in pics table
- */
-int com_picch(__a_unused int fd, int argc, char * const * argv)
-{
-       int ret;
-       long unsigned id;
-       char *q, *tmp;
-
-       if (argc != 3)
-               return -E_MYSQL_SYNTAX;
-       id = atol(argv[1]);
-       ret = -E_ESCAPE;
-       tmp = escape_str(argv[2]);
-       if (!tmp)
-               return -E_ESCAPE;
-       q = make_message("update pics set name = '%s' where id = %lu", tmp, id);
-       free(tmp);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * piclist: print list of pics in db
- */
-int com_piclist(__a_unused int fd, int argc, __a_unused char * const * argv)
-{
-       void *result = NULL;
-       MYSQL_ROW row;
-       unsigned long *length;
-       int ret;
-
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       result = get_result("select id,name,pic from pics order by id");
-       if (!result)
-               return -E_NORESULT;
-       while ((row = mysql_fetch_row(result))) {
-               length = mysql_fetch_lengths(result);
-               if (!row || !row[0] || !row[1] || !row[2])
-                       continue;
-               ret = send_va_buffer(fd, "%s\t%lu\t%s\n", row[0], length[2], row[1]);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * picdel: delete picture from database
- */
-int com_picdel(int fd, int argc, char * const * argv)
-{
-       char *q;
-       long unsigned id;
-       my_ulonglong aff;
-       int i, ret;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       for (i = 1; i < argc; i++) {
-               id = atol(argv[i]);
-               q = make_message("delete from pics where id = %lu", id);
-               ret = real_query(q);
-               free(q);
-               if (ret < 0)
-                       return ret;
-               aff = mysql_affected_rows(mysql_ptr);
-               if (!aff) {
-                       ret = send_va_buffer(fd, "No such id: %lu\n", id);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               q = make_message("update data set pic_id = 1 where pic_id = %lu", id);
-               ret = real_query(q);
-               free(q);
-       }
-       return 1;
-}
-/*
- * pic: get picture by name or by number
- */
-int com_pic(int fd, int argc, char * const * argv)
-{
-       void *result = NULL;
-       MYSQL_ROW row;
-       unsigned long *length, id;
-       int ret;
-       char *q, *name = NULL;
-
-       if (argc < 2) {
-               ret = -E_GET_AUDIO_FILE;
-               name = get_current_audio_file();
-       } else {
-               ret = -E_ESCAPE;
-               name = escaped_basename(argv[1]);
-       }
-       if (!name)
-               return ret;
-       if (*name == '#')
-               id = atoi(name + 1);
-       else
-               id = get_pic_id_by_name(name);
-       free(name);
-       if (id <= 0)
-               return id;
-       q = make_message("select pic from pics where id = '%lu'", id);
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return -E_NORESULT;
-       row = mysql_fetch_row(result);
-       ret = -E_NOROW;
-       if (!row || !row[0])
-               goto out;
-       length = mysql_fetch_lengths(result);
-       ret = send_bin_buffer(fd, row[0], *length);
-out:
-       mysql_free_result(result);
-       return ret;
-}
-
-/* strdel */
-int com_strdel(__a_unused int fd, int argc, char * const * argv)
-{
-       char *q, *tmp;
-       int ret;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       q = make_message("delete from streams where name='%s'", tmp);
-       free(tmp);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * ls
- */
-int com_ls(int fd, int argc, char * const * argv)
-{
-       char *q;
-       void *result;
-       int ret;
-       my_ulonglong num_rows, top = 0, left = 0, right = 0;
-
-       if (argc > 2)
-               return -E_MYSQL_SYNTAX;
-       if (argc > 1) {
-               char *tmp = escape_str(argv[1]);
-               if (!tmp)
-                       return -E_ESCAPE;
-               q = make_message("select name from data where name like '%s'",
-                       tmp);
-               free(tmp);
-       } else
-               q = para_strdup("select name from data");
-       result = get_result(q);
-       free(q);
-       if (!result)
-               return -E_NORESULT;
-       num_rows = mysql_num_rows(result);
-       ret = 1;
-       if (num_rows)
-               ret = print_results(fd, result, top, left, num_rows - 1, right);
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * summary
- */
-int com_summary(__a_unused int fd, int argc, __a_unused char * const * argv)
-{
-       MYSQL_ROW row;
-       MYSQL_ROW row2;
-       void *result;
-       void *result2 = NULL;
-       const char *fmt = "select count(name) from data where %s='1'";
-       int ret = -E_NORESULT;
-
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       result = get_all_attributes();
-       if (!result)
-               goto out;
-       while ((row = mysql_fetch_row(result))) {
-               char *buf;
-
-               ret = -E_NOROW;
-               if (!row[0])
-                       goto out;
-               ret = -E_NORESULT;
-               buf = make_message(fmt, row[0]);
-               result2 = get_result(buf);
-               free(buf);
-               if (!result2)
-                       goto out;
-               ret = -E_NOROW;
-               row2 = mysql_fetch_row(result2);
-               if (!row2 || !row2[0])
-                       goto out;
-               ret = send_va_buffer(fd, "%s\t%s\n", row[0], row2[0]);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       if (result2)
-               mysql_free_result(result2);
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-static int get_numplayed(char *name)
-{
-       void *result;
-       MYSQL_ROW row;
-       const char *fmt = "select numplayed from data where name = '%s'";
-       char *buf = make_message(fmt, name);
-       int ret = -E_NORESULT;
-
-       result = get_result(buf);
-       free(buf);
-       if (!result)
-               goto out;
-       ret = -E_NOROW;
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto out;
-       ret = atoi(row[0]);
-out:
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-static int update_audio_file(const char *name)
-{
-       int ret;
-       const char *fmt1 = "update data set lastplayed = now() where name = '%s'";
-       const char *fmt2 = "update data set numplayed = %i where name = '%s'";
-       char *q;
-       char *ebn = escaped_basename(name);
-
-       ret = -E_ESCAPE;
-       if (!ebn)
-               goto out;
-       q = make_message(fmt1, ebn);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               goto out;
-       ret = get_numplayed(ebn);
-       if (ret < 0)
-               goto out;
-       q = make_message(fmt2, ret + 1, ebn);
-       ret = real_query(q);
-       free(q);
-out:
-       free(ebn);
-       return ret;
-}
-
-static void update_audio_file_server_handler(char *name)
-{
-       char *info;
-       info = get_selector_info(name);
-       update_mmd(info);
-       free(info);
-       update_audio_file(name);
-}
-
-int com_us(__a_unused int fd, int argc, char * const * argv)
-{
-       char *tmp;
-       int ret;
-
-       if (argc != 2)
-               return -E_MYSQL_SYNTAX;
-       tmp = escape_str(argv[1]);
-       if (!tmp)
-               return -E_ESCAPE;
-       ret = update_audio_file(argv[1]);
-       free(tmp);
-       if (ret >= 0)
-               refresh_selector_info();
-       return ret;
-}
-
-/* select previous / next stream */
-static int com_ps_ns(__a_unused int fd, int argc, char * const * argv)
-{
-       char *query, *stream = get_current_stream();
-       void *result = get_result("select name from streams");
-       MYSQL_ROW row;
-       int ret;
-       my_ulonglong num_rows, match, i;
-
-       ret = -E_MYSQL_SYNTAX;
-       if (argc != 1)
-               goto out;
-       ret = -E_NORESULT;
-       if (!result)
-               goto out;
-       num_rows = mysql_num_rows(result);
-       ret = -E_EMPTY_RESULT;
-       if (num_rows < 2)
-               goto out;
-       ret = -E_NOROW;
-       for (i = 0; i < num_rows; i++) {
-               row = mysql_fetch_row(result);
-               if (!row || !row[0])
-                       goto out;
-               if (!strcmp(row[0], "current_stream"))
-                       continue;
-               if (!strcmp(row[0], stream))
-                       break;
-       }
-       ret = -E_NO_STREAM;
-       if (i == num_rows)
-               goto out;
-       match = i;
-       if (!strcmp(argv[0], "ps"))
-               i = match > 0? match - 1 : num_rows - 1;
-       else
-               i = match < num_rows - 1? match + 1 : 0;
-       ret = -E_NOROW;
-       mysql_data_seek(result, i);
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto out;
-       if (!strcmp(row[0], "current_stream")) {
-               if (!strcmp(argv[0], "ps")) {
-                       i = match < 2? match + num_rows - 2 : match - 2;
-               } else {
-                       i = match + 2;
-                       i = i > num_rows - 1? i - num_rows : i;
-               }
-               mysql_data_seek(result, i);
-               row = mysql_fetch_row(result);
-               if (!row || !row[0])
-                       goto out;
-       }
-       query = make_message("update streams set def='%s' where name = "
-               "'current_stream'", row[0]);
-       ret = real_query(query);
-       free(query);
-       refresh_selector_info();
-out:
-       free(stream);
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/* select previous stream */
-int com_ps(int fd, int argc, char * const * argv)
-{
-       return com_ps_ns(fd, argc, argv);
-}
-
-/* select next stream */
-int com_ns(int fd, int argc, char * const * argv)
-{
-       return com_ps_ns(fd, argc, argv);
-}
-
-/* streams */
-int com_streams(int fd, int argc, __a_unused char * const * argv)
-{
-       unsigned int num_rows;
-       int i, ret = -E_NORESULT;
-       void *result;
-       MYSQL_ROW row;
-
-       if (argc > 1 && strcmp(argv[1], "current_stream"))
-               return -E_MYSQL_SYNTAX;
-       if (argc > 1) {
-               char *cs = get_current_stream();
-               ret = send_va_buffer(fd, "%s\n", cs);
-               free(cs);
-               return ret;
-       }
-       result = get_result("select name from streams");
-       if (!result)
-               goto out;
-       num_rows = mysql_num_rows(result);
-       ret = 1;
-       if (!num_rows)
-               goto out;
-       ret = -E_NOROW;
-       for (i = 0; i < num_rows; i++) {
-               row = mysql_fetch_row(result);
-               if (!row || !row[0])
-                       goto out;
-               if (strcmp(row[0], "current_stream"))
-                       send_va_buffer(fd, "%s\n", row[0]);
-       }
-       ret = 1;
-out:
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/* query stream definition */
-int com_strq(int fd, int argc, char * const * argv)
-{
-       MYSQL_ROW row;
-       char *query, *name;
-       void *result;
-       int ret;
-
-       if (argc < 2) {
-               ret = -E_GET_STREAM;
-               name = get_current_stream();
-       } else {
-               ret = -E_ESCAPE;
-               name = escaped_basename(argv[1]);
-       }
-       if (!name)
-               return ret;
-       ret = -E_NORESULT;
-       query = make_message("select def from streams where name='%s'", name);
-       free(name);
-       result = get_result(query);
-       free(query);
-       if (!result)
-               goto out;
-       ret = -E_NOROW;
-       row = mysql_fetch_row(result);
-       if (!row || !row[0])
-               goto out;
-       /* no '\n' needed */
-       ret = send_buffer(fd, row[0]);
-out:
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/* change stream / change stream and play */
-static int com_cs_csp(int fd, int argc, char * const * argv)
-{
-       int ret, stream_change;
-       char *query, *stream = NULL;
-       char *old_stream = get_current_stream();
-       int csp = !strcmp(argv[0], "csp");
-
-       ret = -E_MYSQL_SYNTAX;
-       if (argc > 2)
-               goto out;
-       if (argc == 1) {
-               if (csp)
-                       goto out;
-               ret = send_va_buffer(fd, "%s\n", old_stream);
-               goto out;
-       }
-       ret = -E_GET_QUERY;
-       /* test if stream is valid, no need to escape argv[1] */
-       query = get_query(argv[1], NULL, 0);
-       if (!query)
-               goto out;
-       free(query);
-       /* stream is ok */
-       stream = escape_str(argv[1]);
-       if (!stream)
-               goto out;
-       stream_change = strcmp(stream, old_stream);
-       if (stream_change) {
-               ret = change_stream(stream);
-               if (ret < 0)
-                       goto out;
-               refresh_selector_info();
-       }
-       if (csp) {
-               mmd_lock();
-               mmd->new_vss_status_flags |= VSS_PLAYING;
-               if (stream_change)
-                       mmd->new_vss_status_flags |= VSS_NEXT;
-               mmd_unlock();
-       }
-       ret = 1;
-out:
-       free(old_stream);
-       free(stream);
-       return ret;
-}
-
-/* change stream */
-int com_cs(int fd, int argc, char * const * argv)
-{
-       return com_cs_csp(fd, argc, argv);
-}
-
-/* change stream and play */
-int com_csp(int fd, int argc, char * const * argv)
-{
-       return com_cs_csp(fd, argc, argv);
-}
-
-/* score list / skip */
-static int com_sl_skip(int fd, int argc, char * const * argv)
-{
-       void *result = NULL;
-       MYSQL_ROW row;
-       int ret, i, skip = !strcmp(argv[0], "skip");
-       char *query, *stream, *tmp;
-       unsigned int num_rows, num;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       num = atoi(argv[1]);
-       if (!num)
-               return -E_MYSQL_SYNTAX;
-       if (argc == 2) {
-               stream = get_current_stream();
-               if (!stream)
-                       return -E_GET_STREAM;
-       } else {
-               stream = escape_str(argv[2]);
-               if (!stream)
-                       return -E_ESCAPE;
-       }
-       tmp = get_query(stream, NULL, 0);
-       free(stream);
-       if (!tmp)
-               return -E_GET_QUERY;
-       query = make_message("%s limit %d", tmp, num);
-       free(tmp);
-       ret = -E_NORESULT;
-       result = get_result(query);
-       free(query);
-       if (!result)
-               goto out;
-       ret = -E_EMPTY_RESULT;
-       num_rows = mysql_num_rows(result);
-       if (!num_rows)
-               goto out;
-       for (i = 0; i < num_rows && i < num; i++) {
-               row = mysql_fetch_row(result);
-               if (skip) {
-                       send_va_buffer(fd, "Skipping %s\n", row[0]);
-                       update_audio_file(row[0]);
-               } else
-                       send_va_buffer(fd, "%s\n", row[0]? row[0]: "BUG");
-       }
-       ret = 1;
-out:
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/* score list */
-int com_sl(int fd, int argc, char * const * argv)
-{
-       return com_sl_skip(fd, argc, argv);
-}
-
-/* skip */
-int com_skip(int fd, int argc, char * const * argv)
-{
-       return com_sl_skip(fd, argc, argv);
-}
-
-/*
- * update attributes of name
- */
-static int update_atts(int fd, const char *name, char *atts)
-{
-       int ret;
-       char *ebn, *q, *old, *new = NULL;
-
-       if (!mysql_ptr)
-               return -E_NOTCONN;
-       ebn = escaped_basename(name);
-       if (!ebn)
-               return -E_ESCAPE;
-       q = make_message("update data set %s where name = '%s'", atts, ebn);
-       old = get_atts(ebn, 0);
-       send_va_buffer(fd, "old: %s\n", old);
-       free(old);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               goto out;
-       new = get_atts(ebn, 0);
-       ret = send_va_buffer(fd, "new: %s\n", new);
-       free(new);
-out:
-       free(ebn);
-       return ret;
-}
-
-/*
- * set attributes
- */
-int com_sa(int fd, int argc, char * const * argv)
-{
-       int i, ret;
-       char *atts = NULL, *name;
-
-       if (argc < 2)
-               return -E_MYSQL_SYNTAX;
-       for (i = 1; i < argc; i++) {
-               int unset = 0;
-               char *esc, *tmp, *p;
-               int len = strlen(argv[i]);
-
-               if (!len)
-                       continue;
-               switch (argv[i][len - 1]) {
-                       case '+':
-                               unset = 0;
-                               break;
-                       case '-':
-                               unset = 1;
-                               break;
-                       default:
-                               goto no_more_atts;
-               }
-               p = para_strdup(argv[i]);
-               p[len - 1] = '\0';
-               esc = escape_str(p);
-               free(p);
-               if (!esc)
-                       return -E_ESCAPE;
-               tmp = make_message("%s%s='%s'", atts? "," : "", esc,
-                       unset? "0" : "1");
-               free(esc);
-               atts = para_strcat(atts, tmp);
-               free(tmp);
-       }
-no_more_atts:
-       if (!atts)
-               return -E_NOATTS;
-       if (i >= argc) { /* no name given, use current af */
-               ret = -E_GET_AUDIO_FILE;
-               if (!(name = get_current_audio_file()))
-                       goto out;
-               ret = update_atts(fd, name, atts);
-               free(name);
-       } else {
-               ret = 1;
-               for (; argv[i] && ret >= 0; i++)
-                       ret = update_atts(fd, argv[i], atts);
-       }
-       refresh_selector_info();
-out:
-       free(atts);
-       return ret;
-}
-
-/*
- * copy attributes
- */
-int com_cam(int fd, int argc, char * const * argv)
-{
-       char *name = NULL, *meta = NULL, *atts = NULL;
-       int i, ret;
-
-       if (argc < 3)
-               return -E_MYSQL_SYNTAX;
-       if (!(name = escaped_basename(argv[1])))
-               return -E_ESCAPE;
-       ret = -E_NOATTS;
-       if (!(atts = get_atts(name, 1)))
-               goto out;
-       ret = -E_META;
-       if (!(meta = get_meta(name, 0)))
-               goto out;
-       for (i = 2; i < argc; i++) {
-               char *ebn, *q;
-               ret = -E_ESCAPE;
-               if (!(ebn = escaped_basename(argv[i])))
-                       goto out;
-               ret = send_va_buffer(fd, "updating %s\n", ebn);
-               if (ret < 0) {
-                       free(ebn);
-                       goto out;
-               }
-               q = make_message("update data set %s where name = '%s'",
-                       meta, ebn);
-               if ((ret = update_atts(fd, ebn, atts)) >= 0)
-                       ret = real_query(q);
-               free(ebn);
-               free(q);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       if (name)
-               free(name);
-       if (meta)
-               free(meta);
-       if (atts)
-               free(atts);
-       return ret;
-}
-
-/*
- * verify / clean
- */
-static int com_vrfy_clean(int fd, int argc, __a_unused char * const * argv)
-{
-       char *query;
-       int ret, vrfy_mode = strcmp(argv[0], "clean");
-       void *result = NULL;
-       MYSQL_ROW row;
-       char *escaped_name;
-       my_ulonglong num_rows, top = 0, left = 0, right = 0;
-
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       ret = -E_NORESULT;
-       result = get_result("select data.name from data left join dir on "
-               "dir.name = data.name where dir.name is NULL");
-       if (!result)
-               goto out;
-       num_rows = mysql_num_rows(result);
-       if (!num_rows) {
-               ret = send_buffer(fd, "No invalid entries\n");
-               goto out;
-       }
-       if (vrfy_mode) {
-               send_va_buffer(fd, "found %lli invalid entr%s\n", num_rows,
-                       num_rows == 1? "y" : "ies");
-               ret = print_results(fd, result, top, left, num_rows - 1, right);
-               goto out;
-       }
-       while ((row = mysql_fetch_row(result))) {
-               ret = -E_NOROW;
-               if (!row[0])
-                       goto out;
-               ret = -E_ESCAPE;
-               escaped_name = escape_str(row[0]);
-               if (!escaped_name)
-                       goto out;
-               send_va_buffer(fd, "deleting %s\n", escaped_name);
-               query = make_message("delete from data where name = '%s'",
-                       escaped_name);
-               ret = real_query(query);
-               free(query);
-               if (ret < 0)
-                       goto out;
-       }
-
-out:
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/*
- * verify
- */
-int com_vrfy(int fd, int argc, char * const * argv)
-{
-       return com_vrfy_clean(fd, argc, argv);
-}
-
-/*
- * clean
- */
-int com_clean(int fd, int argc, char * const * argv)
-{
-       return com_vrfy_clean(fd, argc, argv);
-}
-
-static FILE *out_file;
-
-static int mysql_write_tmp_file(const char *dir, const char *name)
-{
-       int ret = -E_TMPFILE;
-       char *msg = make_message("%s\t%s\n", dir, name);
-       if (fputs(msg, out_file) != EOF)
-               ret = 1;
-       free(msg);
-       return ret;
-}
-
-/*
- * update database
- */
-int com_upd(int fd, int argc, __a_unused char * const * argv)
-{
-       char *tempname = NULL, *query = NULL;
-       int ret, out_fd = -1, num = 0;
-       void *result = NULL;
-       unsigned int num_rows;
-       MYSQL_ROW row;
-
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       out_file = NULL;
-       tempname = para_strdup("/tmp/mysql.tmp.XXXXXX");
-       ret = para_mkstemp(tempname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-       if (ret < 0)
-               goto out;
-       out_fd = ret;
-       out_file = fdopen(out_fd, "w");
-       if (!out_file) {
-               close(out_fd);
-               goto out;
-       }
-       if (find_audio_files(conf.mysql_audio_file_dir_arg, mysql_write_tmp_file) < 0)
-               goto out;
-       num = ftell(out_file);
-       /*
-        * we have to make sure the file hit the disk before we call
-        * real_query
-        */
-       fclose(out_file);
-       out_file = NULL;
-       PARA_DEBUG_LOG("wrote tempfile %s (%d bytes)\n", tempname, num);
-       if (!num)
-               goto out;
-       if ((ret = real_query("delete from dir")) < 0)
-               goto out;
-       query = make_message("load data infile '%s' ignore into table dir "
-               "fields terminated by '\t' lines terminated by '\n' "
-               "(dir, name)", tempname);
-       ret = real_query(query);
-       free(query);
-       if (ret < 0)
-               goto out;
-       result = get_result("select dir.name from dir left join data on "
-               "data.name = dir.name where data.name is NULL");
-       ret = -E_NORESULT;
-       if (!result)
-               goto out;
-       num_rows = mysql_num_rows(result);
-       if (!num_rows) {
-               ret = send_buffer(fd, "no new entries\n");
-               goto out;
-       }
-       while ((row = mysql_fetch_row(result))) {
-               char *erow;
-               ret = -E_NOROW;
-               if (!row[0])
-                       goto out;
-               send_va_buffer(fd, "new entry: %s\n", row[0]);
-               erow = escape_str(row[0]);
-               if (!erow)
-                       goto out;
-               query = make_message("insert into data (name, pic_id) values "
-                       "('%s','%s')", erow, "1");
-               free(erow);
-               ret = real_query(query);
-               free(query);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       if (out_fd >= 0)
-               unlink(tempname);
-       free(tempname);
-       if (out_file)
-               fclose(out_file);
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-static char **server_get_audio_file_list(unsigned int num)
-{
-       char **list = para_malloc((num + 1) * sizeof(char *));
-       char *tmp, *query, *stream = get_current_stream();
-       void *result = NULL;
-       unsigned int num_rows;
-       int i = 0;
-       MYSQL_ROW row;
-
-       tmp = get_query(stream, NULL, 1);
-       free(stream);
-       if (!tmp)
-               goto err_out;
-       query = make_message("%s limit %d", tmp, num);
-       free(tmp);
-       result = get_result(query);
-       free(query);
-       if (!result)
-               goto err_out;
-       num_rows = mysql_num_rows(result);
-       if (!num_rows)
-               goto err_out;
-       for (i = 0; i < num_rows && i < num; i++) {
-               row = mysql_fetch_row(result);
-               if (!row || !row[0])
-                       goto err_out;
-               list[i] = para_strdup(row[0]);
-       }
-       list[i] = NULL;
-       goto success;
-err_out:
-       while (i > 0) {
-               i--;
-               free(list[i]);
-       }
-       free(list);
-       list = NULL;
-success:
-       if (result)
-               mysql_free_result(result);
-       return list;
-}
-
-/*
- * connect to mysql server, return mysql pointer on success, -E_NOTCONN
- * on errors. Called from parent on startup and also from com_cdb().
- */
-static int init_mysql_server(void)
-{
-       char *u = conf.mysql_user_arg? conf.mysql_user_arg : para_logname();
-       unsigned int port;
-
-       mysql_ptr = mysql_init(NULL);
-       if (!mysql_ptr) {
-               PARA_CRIT_LOG("%s", "mysql init error\n");
-               return -E_NOTCONN;
-       }
-       if (conf.mysql_port_arg < 0)
-               return -E_MYSQL_SYNTAX;
-       port = conf.mysql_port_arg;
-       PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg, port);
-       if (!conf.mysql_user_arg)
-               free(u);
-       /*
-         * If host is NULL a connection to the local host is assumed,
-         * If user is NULL, the current user is assumed
-        */
-       if (!(mysql_ptr = mysql_real_connect(mysql_ptr,
-                       conf.mysql_host_arg,
-                       conf.mysql_user_arg,
-                       conf.mysql_passwd_arg,
-                       conf.mysql_database_arg,
-                       port, NULL, 0))) {
-               PARA_CRIT_LOG("%s", "connect error\n");
-               return -E_NOTCONN;
-       }
-       PARA_INFO_LOG("%s", "success\n");
-       return 1;
-}
-
-/* mmd lock must be held */
-static void write_msg2mmd(int success)
-{
-       sprintf(mmd->selector_info, "dbinfo1:%s\ndbinfo2:mysql-%s\ndbinfo3:\n",
-               success < 0? PARA_STRERROR(-success) :
-                       "successfully connected to mysql server",
-               success < 0? "" : mysql_get_server_info(mysql_ptr));
-}
-
-/* create database */
-int com_cdb(int fd, int argc, char * const * argv)
-{
-       char *query;
-       int ret;
-
-       if (mysql_ptr) {
-               PARA_INFO_LOG("%s", "closing database\n");
-               mysql_close(mysql_ptr);
-       }
-       /* dont use any database */
-       conf.mysql_database_arg = NULL; /* leak? */
-       ret = -E_MYSQL_INIT;
-       if (init_mysql_server() < 0 || !mysql_ptr)
-               goto out;
-       if (argc < 2)
-               conf.mysql_database_arg = para_strdup("paraslash");
-       else {
-               ret = -E_ESCAPE;
-               conf.mysql_database_arg = escape_str(argv[1]);
-               if (!conf.mysql_database_arg)
-                       goto out;
-       }
-       query = make_message("create database %s", conf.mysql_database_arg);
-       ret = real_query(query);
-       free(query);
-       if (ret < 0)
-               goto out;
-       /* reconnect with database just created */
-       mysql_close(mysql_ptr);
-       ret = -E_MYSQL_INIT;
-       if (init_mysql_server() < 0 || !mysql_ptr)
-               goto out;
-       mmd_lock();
-       write_msg2mmd(1);
-       mmd_unlock();
-       ret = -E_QFAILED;
-       if (real_query("create table data (name varchar(255) binary not null "
-                       "primary key, "
-                       "lastplayed datetime not null default "
-                               "'1970-01-01', "
-                       "numplayed int not null default 0, "
-                       "pic_id bigint unsigned not null default 1)") < 0)
-               goto out;
-       if (real_query("create table dir (name varchar(255) binary not null "
-                       "primary key, dir varchar(255) default null)") < 0)
-               goto out;
-       if (real_query("create table pics ("
-                       "id bigint(20) unsigned not null primary key "
-                       "auto_increment, "
-                       "name varchar(255) binary not null, "
-                       "pic mediumblob not null)") < 0)
-               goto out;
-       if (real_query("create table streams ("
-                       "name varchar(255) binary not null primary key, "
-                       "def blob not null)") < 0)
-               goto out;
-       if (real_query("insert into streams (name, def) values "
-                       "('current_stream', '(none)')") < 0)
-               goto out;
-       ret = send_va_buffer(fd, "successfully created database %s\n",
-               conf.mysql_database_arg);
-out:
-       return ret;
-}
-
-static void shutdown_connection(void)
-{
-       int ret;
-
-       if (mysql_ptr) {
-               PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
-               mysql_close(mysql_ptr);
-               mysql_ptr = NULL;
-       }
-       if (mysql_lock) {
-               ret = mutex_destroy(mysql_lock);
-               if (ret < 0)
-                       PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
-               mysql_lock = 0;
-       }
-}
-
-/**
- * the init function of the mysql-based audio file selector
- *
- * \param db pointer to the struct to initialize
- *
- * Check the command line options and initialize all function pointers of \a
- * db.  Connect to the mysql server and initialize the info string.
- *
- * \return This function returns success even if it could not connect
- * to the mysql server. This is because the connect is expected to fail
- * if there the paraslash database is not yet created. This gives the
- * user a chance to send the "cdb" to create the database.
- *
- * \sa struct audio_file_selector, misc_meta_data::selector_info,
- * random_selector.c
- */
-int mysql_selector_init(struct audio_file_selector *db)
-{
-       int ret;
-
-       if (!conf.mysql_passwd_given)
-               return -E_NO_MYSQL_PASSWD;
-       if (!conf.mysql_audio_file_dir_given)
-               return -E_NO_AF_DIR;
-       db->name = "mysql";
-       db->cmd_list = mysql_selector_cmds;
-       db->get_audio_file_list = server_get_audio_file_list;
-       db->update_audio_file = update_audio_file_server_handler;
-       db->shutdown = shutdown_connection;
-       ret = mutex_new();
-       if (ret < 0)
-               return ret;
-       mysql_lock = ret;
-       ret = init_mysql_server();
-       if (ret < 0)
-               PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));
-       write_msg2mmd(ret);
-       return 1;       /* return success even if connect failed to give the
-                        * user the chance to exec com_cdb
-                        */
-}
diff --git a/mysql_selector.cmd b/mysql_selector.cmd
deleted file mode 100644 (file)
index a49d6d1..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-OF: mysql_selector_command_list
-SF: mysql_selector.c
-HC: prototypes for the commands of the mysql audio file selector
-CC: array of commands for the mysql audio file selector
-AT: server_command
-IN: para error string afh afs server list user_list
-SN: list of mysql selector commands
----
-N: cam
-P: AFS_READ | AFS_WRITE
-D: copy all metadata
-U: cam source dest1 [dest2 ...]
-H: Copy attributes and other meta data from source file to destination
-H: file(s). Useful for files that have been renamed.
----
-N: cdb
-P: AFS_READ | AFS_WRITE
-D: create database
-U: cdb [name]
-H:
-H: Create database name containing the initial columns for basic
-H: interoperation with server. This command has to be used only once
-H: when you use the mysql audio file selector for the very first time.
-H:
-H: The optional name defaults to 'paraslash' if not given.
----
-N: clean
-P: AFS_READ | AFS_WRITE
-D: nuke invalid entries in database
-U: clean
-H: If the vrfy command shows you any invalid entries in your database,
-H: you can get rid of them with clean. Always run 'upd' and 'vrfy'
-H: before running this command. Use with caution!
----
-N: cs
-P: VSS_WRITE | AFS_READ | AFS_WRITE
-D: change stream
-U: cs [s]
-H: Selects stream s or prints current stream when s was not given.
----
-N: csp
-P: VSS_WRITE | AFS_READ
-D: change stream and play
-U: csp s
-H: Select stream s and start playing. If this results in a
-H: stream-change, skip rest of current audio file.
----
-N: da
-P: AFS_READ | AFS_WRITE
-D: drop attribute from database
-U: da att
-H: Use with caution. All info on attribute att will be lost.
----
-N: hist
-P: AFS_READ
-D: print history
-U: hist
-H: Print list of all audio files together with number of days since
-H: each file was last played.
----
-N: info
-P: AFS_READ
-D: print database info
-U: info [af]
-H: print database informations for audio file af. Current audio file
-H: is used if af is not given.
----
-N: la
-P: AFS_READ
-D: list attributes
-U: la [af]
-H: List attributes of audio file af or of current audio file when
-H: invoked without arguments.
----
-N: laa
-P: AFS_READ
-D: list available attributes
-U: laa
-H: print list of all attributes defined in the database
----
-N: last
-P: AFS_READ
-D: print list of audio files, ordered by lastplayed time
-U: last [n]
-H: The optional number n defaults to 10 if not specified.
----
-N: ls
-P: AFS_READ
-D: list all audio files that match a LIKE pattern
-U: ls [pattern]
-H: If pattern was not given, print list of all audio files known
-H: to the mysql selector. See the documentation of mysql
-H: for the definition of LIKE patterns.
----
-N: mbox
-P: AFS_READ
-D: dump audio file list in mbox format
-U: mbox [p]
-H: Dump list of audio files in mbox format (email) to stdout. If
-H: the optional pattern p is given, only those audio files,
-H: whose basename match p are going to be included. Otherwise,
-H: all files are selected.
-H:
-H: EXAMPLE
-H: The mbox command can be used together with your favorite
-H: mailer (this example uses mutt) for browsing the audio file
-H: collection:
-H:
-H:     para_client mbox > ~/para_mbox
-H:
-H:     mutt -F ~/.muttrc.para -f ~/para_mbox
-H:
-H: For playlists, you can use mutt's powerful pattern matching
-H: language to select files. If you like to tag all files
-H: containing the pattern 'foo', type 'T', then '~s foo'.
-H:
-H: When ready with the list, type ';|' (i.e., hit the semicolon
-H: key to apply the next mutt command to all tagged messages,
-H: then the pipe key) to pipe the selected mails to a
-H: suitable script which adds a paraslash stream where exactly
-H: these files are admissable or does whatever thou wilt.
----
-N: mv
-P: AFS_READ | AFS_WRITE
-D: rename entry in database
-U: mv oldname newname
-H: Rename oldname to newname. This updates the data table to reflect
-H: the new name. All internal data (numplayed, lastplayed, picid,..)
-H: is kept.  If newname is a full path, the dir table is updated as
-H: well.
----
-N: na
-P: AFS_READ | AFS_WRITE
-D: add new attribute to database
-U: na att
-H: This adds a column named att to your mysql database. att should
-H: only contain letters and numbers, in paricular, '+' and '-' are
-H: not allowed.
----
-N: ne
-P: AFS_READ | AFS_WRITE
-D: add new database entries
-U: ne file1 [file2 [...]]
-H: Add the given filename(s) to the database, where file1,... must
-H: be full path names. This command might be much faster than 'upd'
-H: if the number of given files is small.
----
-N: ns
-P: VSS_WRITE | AFS_READ | AFS_WRITE
-D: change to next stream
-U: ns
-H: Cycle forwards through stream list.
----
-N: pic
-P: AFS_READ
-D: get picture by name or by identifier
-U: pic [name]
-H: Dump jpg image that is associated to given audio file (current
-H: audio file if not specified) to stdout. If name starts with
-H: '#' it is interpreted as an identifier instead and the picture
-H: having that identifier is dumped to stdout.
-H:
-H: EXAMPLE
-H:
-H:     para_client pic '#123' > pic123.jpg
----
-N: picadd
-P: AFS_READ | AFS_WRITE
-D: add picture to database
-U: picadd [picname]
-H: Read jpeg file from stdin and store it as picname in database.
-H:
-H: EXAMPLE
-H:
-H:     para_client picadd foo.jpg < foo.jpg
----
-N: picass
-P: AFS_READ | AFS_WRITE
-D: associate a picture to file(s)
-U: picass pic_id file1 [file2...]
-H: Associate the picture given by pic_id to all given files.
----
-N: picch
-P: AFS_READ | AFS_WRITE
-D: change name of picture
-U: picch id new_name
-H: Asign new_name to picture with identifier id.
----
-N: picdel
-P: AFS_READ | AFS_WRITE
-D: delete picture from database
-U: picdel id1 [id2...]
-H: Delete each given picture from database.
----
-N: piclist
-P: AFS_READ
-D: print list of pictures
-U: piclist
-H: Print id, name and length of each picture contained in the
-H: database.
----
-N: ps
-P: VSS_WRITE | AFS_READ | AFS_WRITE
-D: change to previous stream
-U: ps
-H: Cycle backwards through stream list.
----
-N: rm
-P: AFS_READ | AFS_WRITE
-D: remove entries from database
-U: rm name1 [name2 [...]]
-H: Remove name1, name2, ... from the data table. Use with caution.
----
-N: sa
-P: AFS_READ | AFS_WRITE
-D: set/unset attributes
-U: sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]
-H: Set ('+') or unset ('-') attribute at1, at2 etc. for given list of
-H: audio files. If no audio files were given the current audio file is
-H: used. Example:
-H:
-H:     sa rock+ punk+ classic- LZ__Waldsterben.mp3
-H:
-H: sets the 'rock' and the 'punk' attribute but unsets the 'classic'
-H: attribute.
----
-N: skip
-P: AFS_READ | AFS_WRITE
-D: skip subsequent audio files(s)
-U: skip n [s]
-H: Skip the next n audio files of stream s. This is equivalent to the
-H: command 'sl n s', followed by 'us name' for each name the output of
-H: sl.
----
-N: sl
-P: AFS_READ
-D: print score list
-U: sl n [s]
-H: Print sorted list of maximal n lines. Each line is an admissible
-H: entry with respect to stream s. The list is sorted by score-value
-H: which is given by the definition of s. If s is not given, the
-H: current stream is used. Example:
-H:
-H:     sl 1
-H:
-H: shows you the audio file the server would select right now.
----
-N: snp
-P: AFS_READ | AFS_WRITE
-D: set numplayed
-U: snp number af1 [af2 ...]
-H: Update the numplayed field in the data table for all given audio
-H: files.
----
-N: stradd
-P: AFS_READ | AFS_WRITE
-D: add stream
-U: stradd s
-H: Add stream s to the list of available streams. The stream
-H: definition for s is read from stdin and is then sent to
-H: para_server. Example:
-H:
-H:     echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna
-H:
-H: adds the new stream 'no_madonna' to the list of available streams.
-H: A given audio file is admissible for this stream iff its basename
-H: does not contain the string 'Madonna'.
----
-N: strdel
-P: AFS_READ | AFS_WRITE
-D: delete stream
-U: strdel s
-H: Remove stream s from database.
----
-N: streams
-P: AFS_READ
-D: list streams
-U: streams
-H: Print list of available streams. Use 'cs' to switch to any of
-H: these.
----
-N: strq
-P: AFS_READ
-D: query stream definition
-U: strq [s]
-H: Print definition of stream s to stdout. Use current stream if s was
-H: not given.
----
-N: summary
-P: AFS_READ
-D: list attributes
-U: summary
-H: Print a list of attributes together with number of audio
-H: files having that attribute set.
----
-N: upd
-P: AFS_READ | AFS_WRITE
-D: update database
-U: upd
-H: This command uses the --audio_file_dir option of para_server to
-H: locate your audio files. New files are then added to the mysql
-H: database. Use this command if you got new files or if you have
-H: moved some files around.
----
-N: us
-P: AFS_READ | AFS_WRITE
-D: update lastplayed time
-U: us name
-H: Update lastplayed time without actually playing the thing.
----
-N: verb
-P: AFS_READ | AFS_WRITE
-D: send verbatim sql query
-U: verb cmd
-H: Send cmd to mysql server. For expert/debugging only. Note that cmd
-H: usually must be escaped. Use only if you know what you are doing!
----
-N: vrfy
-P: AFS_READ
-D: list invalid entries in database
-U: vrfy
-H: Show what clean would delete. Run 'upd' before this command to make
-H: sure your database is up to date.
----
diff --git a/playlist_selector.c b/playlist_selector.c
deleted file mode 100644 (file)
index 2d076ee..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file playlist_selector.c The playlist audio file selector of paraslash  */
-
-#include <signal.h>
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "afh.h"
-#include "afs.h"
-#include "server.h"
-#include "afs_common.h"
-#include "net.h"
-#include "ipc.h"
-#include "list.h"
-#include "user_list.h"
-#include "playlist_selector_command_list.h"
-
-/**
- * structure used for transmission of the playlist
- *
- * There's one such struct which gets initialized during startup. It lives in
- * shared memory and is used by com_lpl().
- */
-struct pls_client_data {
-/** allocated and set by com_lpl() (child) */
-       int shm_id;
-/** the size of the shared memory area identified by \a shm_id */
-       size_t size;
-/** initially locked, gets unlocked by parent when it is done */
-       int mutex;
-/** return value, set by parent */
-       int retval;
-};
-
-/** data specific to the playlist selector */
-struct private_pls_data {
-/** guards against concurrent client access */
-       int client_mutex;
-/** guards against concurrent parent-child access */
-       int server_mutex;
-/** pointer to the client data */
-       struct pls_client_data *client_data;
-/** id of the shm corresponding to \a client_data */
-       int client_data_shm_id;
-};
-
-/** we refuse to load playlists bigger than that */
-#define MAX_PLAYLIST_BYTES (1024 * 1024)
-
-static unsigned playlist_len, playlist_size, current_playlist_entry;
-static char **playlist;
-static struct audio_file_selector *self;
-
-extern struct misc_meta_data *mmd;
-
-static int playlist_add(char *path, __a_unused void *data)
-{
-       if (playlist_len >= playlist_size) {
-               playlist_size = 2 * playlist_size + 1;
-               playlist = para_realloc(playlist, playlist_size * sizeof(char *));
-       }
-       PARA_DEBUG_LOG("adding #%d/%d: %s\n", playlist_len, playlist_size, path);
-       playlist[playlist_len++] = para_strdup(path);
-       return 1;
-}
-
-static int send_playlist_to_server(const char *buf, size_t size)
-{
-       struct private_pls_data *ppd = self->private_data;
-       int ret, shm_mutex = -1, shm_id = -1;
-       void *shm = NULL;
-
-       PARA_DEBUG_LOG("new playlist (%zd bytes)\n", size);
-
-       ret = mutex_new();
-       if (ret < 0)
-               return ret;
-       shm_mutex = ret;
-
-       ret = shm_new(size);
-       if (ret < 0)
-               goto out;
-       shm_id = ret;
-
-       ret = shm_attach(shm_id, ATTACH_RW, &shm);
-       if (ret < 0)
-               goto out;
-       mutex_lock(shm_mutex);
-       memcpy(shm, buf, size);
-       mutex_lock(ppd->client_mutex);
-       mutex_lock(ppd->server_mutex);
-       ppd->client_data->size = size;
-       ppd->client_data->shm_id = shm_id;
-       ppd->client_data->mutex = shm_mutex;
-       kill(getppid(), SIGUSR1); /* wake up the server */
-       mutex_unlock(ppd->server_mutex);
-       mutex_lock(shm_mutex); /* wait until server is done */
-       mutex_unlock(shm_mutex);
-       ret = ppd->client_data->retval;
-       mutex_unlock(ppd->client_mutex);
-       shm_detach(shm);
-out:
-       if (shm_id >= 0)
-               shm_destroy(shm_id);
-       mutex_destroy(shm_mutex);
-       PARA_DEBUG_LOG("returning %d\n", ret);
-       return ret;
-}
-
-int com_lpl(int fd, __a_unused int argc, __a_unused char * const * const argv)
-{
-       unsigned loaded = 0;
-       size_t bufsize = 4096; /* guess that's enough */
-       char *buf = para_malloc(bufsize);
-       ssize_t ret;
-       ret = send_buffer(fd, AWAITING_DATA_MSG);
-       if (ret < 0)
-               goto out;
-again:
-       ret = recv_bin_buffer(fd, buf + loaded, bufsize - loaded);
-       if (ret < 0)
-               goto out;
-       if (!ret) {
-               ret = send_playlist_to_server(buf, loaded);
-               goto out;
-       }
-       loaded += ret;
-       ret = -E_LOAD_PLAYLIST;
-       if (loaded >= MAX_PLAYLIST_BYTES)
-               goto out;
-       if (loaded >= bufsize) {
-               bufsize *= 2;
-               buf = para_realloc(buf, bufsize);
-       }
-       goto again;
-out:
-       free(buf);
-       return ret;
-}
-
-int com_ppl(int fd, __a_unused int argc, __a_unused char * const * const argv)
-{
-       unsigned i;
-
-       PARA_DEBUG_LOG("sending playlist to client (%d entries)\n", playlist_len);
-       for (i = 0; i < playlist_len; i++) {
-               int ret = send_va_buffer(fd, "%s\n", playlist[
-                       (i + current_playlist_entry) % playlist_len]);
-               if (ret < 0)
-                       return ret;
-       }
-       return 1;
-}
-
-static char **pls_get_audio_file_list(unsigned int num)
-{
-       char **file_list;
-       unsigned i;
-
-       num = PARA_MIN(num, playlist_len);
-       if (!num)
-               return NULL;
-       file_list = para_malloc((num + 1) * sizeof(char *));
-       for (i = 0; i < num; i++) {
-               unsigned j = (current_playlist_entry + i + 1) % playlist_len;
-               file_list[i] = para_strdup(playlist[j]);
-       }
-       file_list[i] = NULL;
-       return file_list;
-}
-
-static void free_playlist_contents(void)
-{
-       int i;
-
-       PARA_DEBUG_LOG("freeing playlist (%d entries)\n", playlist_len);
-       for (i = 0; i < playlist_len; i++)
-               free(playlist[i]);
-       current_playlist_entry = 0;
-       playlist_len = 0;
-}
-
-static void pls_shutdown(void)
-{
-       struct private_pls_data *ppd = self->private_data;
-
-       shm_detach(ppd->client_data);
-       shm_destroy(ppd->client_data_shm_id);
-       mutex_destroy(ppd->server_mutex);
-       mutex_destroy(ppd->client_mutex);
-       free(ppd);
-       free_playlist_contents();
-       free(playlist);
-       playlist = NULL;
-       playlist_len = 0;
-       playlist_size = 0;
-}
-
-static void pls_post_select(__a_unused fd_set *rfds, __a_unused fd_set *wfds)
-{
-       struct private_pls_data *ppd = self->private_data;
-       struct pls_client_data *pcd = ppd->client_data;
-       int ret;
-       void *shm;
-
-       mutex_lock(ppd->server_mutex);
-       if (!pcd->size)
-               goto out;
-       free_playlist_contents();
-       ret = shm_attach(pcd->shm_id, ATTACH_RW, &shm);
-       if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
-               goto out;
-       }
-       PARA_DEBUG_LOG("loading new playlist (%zd bytes)\n", pcd->size);
-       ret = for_each_line((char *)shm, pcd->size, &playlist_add, NULL);
-       shm_detach(shm);
-       PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
-       sprintf(mmd->selector_info, "dbinfo1:new playlist: %d files\n"
-               "dbinfo2:\ndbinfo3:\n", playlist_len);
-       pcd->retval = 1;
-       pcd->size = 0;
-       mutex_unlock(pcd->mutex);
-out:
-       mutex_unlock(ppd->server_mutex);
-}
-
-static size_t string_offset(const char *str, size_t max)
-{
-       size_t l = strlen(str);
-
-       if (l <= max)
-               return 0;
-       return l - max;
-}
-
-static void pls_update_audio_file(char *audio_file)
-{
-       unsigned i;
-       char *dir = para_dirname(audio_file),
-               *prev = playlist[current_playlist_entry % playlist_len];
-       size_t dir_off = string_offset(dir, 50),
-               prev_off = string_offset(prev, 70);
-
-       for (i = 1; i <= playlist_len; i++) {
-               char *next;
-               size_t next_off;
-               unsigned j = (current_playlist_entry + i) % playlist_len;
-               if (strcmp(playlist[j], audio_file))
-                       continue;
-               current_playlist_entry = j;
-               next = playlist[(j + 1) %playlist_len];
-               next_off = string_offset(next, 70);
-               snprintf(mmd->selector_info, MMD_INFO_SIZE,
-                       "dbinfo1: %d files, current dir: %s%s\n"
-                       "dbinfo2: prev: %s%s\n"
-                       "dbinfo3: next: %s%s\n",
-                       playlist_len,
-                       dir_off? "... " : "", dir + dir_off,
-                       prev_off? "... " : "", prev + prev_off,
-                       next_off? "... " : "", next + next_off
-               );
-               break;
-       }
-       free(dir);
-}
-
-/**
- * the init function for the playlist selector
- *
- * \param afs pointer to the struct to initialize
- *
- * Init all function pointers of \a afs
- *
- * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
- * random_selector.c.
- */
-int playlist_selector_init(struct audio_file_selector *afs)
-{
-       int ret;
-       struct private_pls_data *ppd = NULL;
-       void *shm = NULL;
-
-       self = afs;
-       afs->cmd_list = playlist_selector_cmds;
-       afs->get_audio_file_list = pls_get_audio_file_list;
-       afs->shutdown = pls_shutdown;
-       afs->post_select = pls_post_select;
-       afs->update_audio_file = pls_update_audio_file;
-       ppd = para_calloc(sizeof(struct private_pls_data));
-       afs->private_data = ppd;
-
-       ppd->client_mutex = -1;
-       ppd->server_mutex = -1;
-       ppd->client_data_shm_id = -1;
-       ppd->client_data = NULL;
-
-       ret = mutex_new();
-       if (ret < 0)
-               goto err_out;
-       ppd->client_mutex = ret;
-
-       ret = mutex_new();
-       if (ret < 0)
-               goto err_out;
-       ppd->server_mutex = ret;
-
-       ret = shm_new(sizeof(struct pls_client_data));
-       if (ret < 0)
-               goto err_out;
-       ppd->client_data_shm_id = ret;
-
-       ret = shm_attach(ppd->client_data_shm_id, ATTACH_RW, &shm);
-       if (ret < 0)
-               goto err_out;
-       ppd->client_data = shm;
-       ppd->client_data->size = 0;
-       sprintf(mmd->selector_info, "dbinfo1: Welcome to the playlist "
-               "selector\ndbinfo2: no playlist loaded\ndbinfo3:\n");
-       return 1;
-err_out:
-       if (ppd->client_data_shm_id >= 0)
-               shm_destroy(ppd->client_data_shm_id);
-       if (ppd->client_mutex >= 0)
-               mutex_destroy(ppd->client_mutex);
-       if (ppd->server_mutex >= 0)
-               mutex_destroy(ppd->server_mutex);
-       free(ppd);
-       return ret;
-}
diff --git a/playlist_selector.cmd b/playlist_selector.cmd
deleted file mode 100644 (file)
index b819025..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-OF: playlist_selector_command_list
-SF: playlist_selector.c
-HC: prototypes for the commands of the playlist audio file selector
-CC: array of commands for the playlist audio file selector
-AT: server_command
-IN: para error string afh afs server list user_list
-SN: list of playlist selector commands
----
-N: ppl
-P: AFS_READ
-D: print playlist
-U: ppl
-H: Print out the current playlist
----
-N: lpl
-P: AFS_WRITE
-D: load playlist
-U: lpl
-H: Read a new playlist from stdin. Example:
-H: find /audio -name '*.mp3' | para_client lpl
----
diff --git a/random_selector.c b/random_selector.c
deleted file mode 100644 (file)
index ac82058..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2004-2007 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file random_selector.c An audio file selector which chooses files by random */
-
-#include <sys/time.h> /* gettimeofday */
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "server.cmdline.h"
-#include "afh.h"
-#include "afs.h"
-#include "server.h"
-#include "afs_common.h"
-#include "net.h"
-#include "random_selector_command_list.h"
-
-extern struct misc_meta_data *mmd;
-
-static unsigned int num_audio_files, audio_file_count;
-static char **audio_file_list;
-
-static int count_audio_files(__a_unused const char *dir, __a_unused const char *name)
-{
-       num_audio_files++;
-       return 1;
-}
-
-static int remember_file(const char *dir, const char *name)
-{
-       if (audio_file_count < num_audio_files) {
-               audio_file_list[audio_file_count] =
-                       make_message("%s/%s", dir, name);
-               audio_file_count++;
-       }
-       return 1;
-}
-
-int com_random_info(int fd, __a_unused int argc, __a_unused char * const * const argv)
-{
-       return send_buffer(fd, "Don't use for huge directories as it is "
-               "very inefficient in this case.\n");
-}
-
-/*
- * Load a list of all audio files into memory and chose num of them randomly.
- * Called by server to determine next audio file to be streamed.
- */
-static char **random_get_audio_file_list(unsigned int num)
-{
-       int i, ret;
-       unsigned int len;
-       char **ret_list = NULL; /* what we are going to return */
-
-       audio_file_list = NULL;
-       num_audio_files = 0;
-       /* first run, just count all audio files. dopey */
-       ret = find_audio_files(conf.random_dir_arg, count_audio_files);
-       if (ret < 0)
-               goto out;
-       ret = -E_NOTHING_FOUND;
-       if (!num_audio_files)
-               goto out;
-       /* yeah, that doesn't scale, also dopey */
-       audio_file_list = para_malloc(num_audio_files * sizeof(char *));
-       audio_file_count = 0;
-       /* second run (hot dentry cache, hopefully), fill audio_file_list */
-       ret = find_audio_files(conf.random_dir_arg, remember_file);
-       if (ret < 0)
-               goto out;
-       /* careful, files might got deleted underneath */
-       num_audio_files = audio_file_count; /* can only decrease */
-       len = PARA_MIN(num, num_audio_files);
-       ret = -E_NOTHING_FOUND;
-       if (!len) /* nothing found, return NULL */
-               goto out;
-       /* success, return NULL-terminated list */
-       ret_list = para_calloc((len + 1) * sizeof(char *));
-       for (i = 0; i < len; i++) { /* choose randomly */
-               int r = (int) ((num_audio_files + 0.0) * (rand()
-                       / (RAND_MAX + 1.0)));
-               ret_list[i] = para_strdup(audio_file_list[r]);
-       }
-       ret = 1;
-out:
-       if (audio_file_list) {
-               for (i = 0; i < num_audio_files; i++)
-                       free(audio_file_list[i]);
-               free(audio_file_list);
-       }
-       if (ret > 0) {
-       } else
-               sprintf(mmd->selector_info, "dbinfo1:%s\n", PARA_STRERROR(-ret));
-       return ret_list;
-}
-
-static void random_update_audio_file(char *audio_file)
-{
-       char *dn = para_dirname(audio_file);
-       snprintf(mmd->selector_info, MMD_INFO_SIZE - 1,
-               "dbinfo1:current dir: %s\n"
-               "dbinfo2:random_dir: %s\n"
-               "dbinfo3:%d files available\n",
-               dn, conf.random_dir_arg, num_audio_files);
-       free(dn);
-       mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
-}
-static void random_shutdown(void)
-{
-}
-
-/**
- * the init function for the random audio file selector
- *
- * \param s pointer ro the struct to iniitalize
- *
- * Init all function pointers of \a s, init the info text and seed the PRNG.
- *
- * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
- */
-int random_selector_init(struct audio_file_selector *s)
-{
-       struct timeval now;
-       unsigned int seed;
-
-       PARA_INFO_LOG("%s", "registering random handlers ;)\n");
-       gettimeofday(&now, NULL);
-       seed = now.tv_usec;
-       srand(seed);
-       s->cmd_list = random_selector_cmds;
-       s->get_audio_file_list = random_get_audio_file_list;
-       s->shutdown = random_shutdown;
-       s->update_audio_file = random_update_audio_file;
-       snprintf(mmd->selector_info, MMD_INFO_SIZE - 1,
-               "dbinfo1: Welcome to the random selector\n"
-               "dbinfo2: random_dir: %s\n", conf.random_dir_arg);
-       mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
-       return 1;
-}
diff --git a/random_selector.cmd b/random_selector.cmd
deleted file mode 100644 (file)
index 67f07db..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-OF: random_selector_command_list
-SF: random_selector.c
-HC: prototypes for the commands of the random audio file selector
-CC: array of commands for the random audio file selector
-AT: server_command
-IN: para error string afh afs server list user_list
-SN: list of random selector commands
----
-N: random_info
-P: 0
-D: about the random audio file selector
-U: random_info
-H: The random selector chooses files randomly out of the given directory.
index ab88b07..bf4ed37 100644 (file)
--- a/server.c
+++ b/server.c
@@ -22,7 +22,6 @@
 #include "para.h"
 #include "error.h"
 #include "server.cmdline.h"
-#include "afs_common.h"
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
index b64aaa3..f6ebc17 100644 (file)
@@ -169,88 +169,6 @@ loads the playlist named 'foo'."
        string typestr="name"
        optional
 
-section "mysql selector"
-#~~~~~~~~~~~~~~~~~~~~~~~
-
-
-option "mysql_host" -
-#~~~~~~~~~~~~~~~~~~~~
-
-"mysql server"
-
-       string typestr="ip or hostname"
-       default="localhost"
-       optional
-
-option "mysql_port" -
-#~~~~~~~~~~~~~~~~~~~~
-
-"where mysql is listening"
-
-       int typestr="portnumber"
-       default="3306"
-       optional
-
-
-option "mysql_user" -
-#~~~~~~~~~~~~~~~~~~~~
-
-"default value: username from /etc/passwd"
-
-       string typestr="username"
-       optional
-
-option "mysql_passwd" -
-#~~~~~~~~~~~~~~~~~~~~~~
-
-"(required)"
-
-       string
-       optional
-
-option "mysql_database" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-
-"name of mysql database"
-
-       string
-       default="paraslash"
-       optional
-
-option "mysql_audio_file_dir"  -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"dir to search for audio files (required)"
-
-       string typestr="dirname"
-       optional
-
-option "mysql_default_score" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"scoring rule to use if stream definition
-does not contain explicit score definition"
-
-       string
-       default="(LASTPLAYED() / 1440 - 1000 / (LASTPLAYED() + 1) - sqrt(NUMPLAYED()))"
-       optional
-
-
-
-
-section "random selector"
-#~~~~~~~~~~~~~~~~~~~~~~~~
-
-
-option "random_dir" -
-#~~~~~~~~~~~~~~~~~~~~
-
-"dir to search for audio files"
-       string typestr="dirname"
-       default="/home/music"
-       optional
-
-
 
 
 section "http sender"
diff --git a/vss.c b/vss.c
index ab5cd1b..0bef15d 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -24,7 +24,6 @@
 #include "server.h"
 #include "net.h"
 #include "server.cmdline.h"
-#include "afs_common.h"
 #include "vss.h"
 #include "send.h"
 #include "ipc.h"