]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'dccp'
authorAndre <maan@p133.(none)>
Sat, 11 Mar 2006 16:45:20 +0000 (17:45 +0100)
committerAndre <maan@p133.(none)>
Sat, 11 Mar 2006 16:45:20 +0000 (17:45 +0100)
Fix up merge conflicts by hand.
Conflicts:

configure.ac
error.h
server.ggo

40 files changed:
FEATURES
INSTALL
Makefile.in
NEWS
README
README.mysql
afs.c
audiod.c
command.c
configure.ac
db.c
db.h
dbadm.c
error.h
filter.c
filter.ggo
filter_chain.c
gui_theme.c
http_recv.c
index.html [deleted file]
mysql.c [deleted file]
mysql_selector.c [new file with mode: 0644]
ogg.c
para.h
playlist_selector.c [new file with mode: 0644]
plm_dbtool.c [deleted file]
random_dbtool.c [deleted file]
random_selector.c [new file with mode: 0644]
scripts/demo-script
sdl_gui.c
server.c
server.ggo
server.h
slider.c
stat.c
versions/paraslash-0.2.11.tar.bz2 [new file with mode: 0644]
versions/paraslash-0.2.11.tar.bz2.asc [new file with mode: 0644]
web/documentation.in.html
web/header2.html [new file with mode: 0644]
web/index.in.html

index 17d5051c81647e982e4c713ca09e65b36eb503d0..3551ee74dfd3e4cb08f8d470716969b787aeb7a5 100644 (file)
--- a/FEATURES
+++ b/FEATURES
@@ -1,8 +1,8 @@
 Features
 ========
 
-configurable audio streaming software
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+configurable audio streaming software:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        para_server streams binary audio data (mp3/ogg files) over
        local or remote networks. It contains two built-in streamers:
        the http streamer and the ortp streamer.
@@ -23,11 +23,10 @@ configurable audio streaming software
 
 configurable audio file selectors:
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       There are three audio file selectors (aka "database tools")
-       available:
+       There are three audio file selectors available:
 
                - random
-               - plm (playlist manager)
+               - playlist
                - mysql
 
        The first two of these are rather simple, and they are always
@@ -46,9 +45,9 @@ configurable audio file selectors:
 small memory footprint:
 ~~~~~~~~~~~~~~~~~~~~~~~
        paraslash is lightweight. The stripped binary of para_server
-       with all its features compiled in (mysql/random dbtool,
-       mp3/ogg support, http/ortp support) is about 110K on i386
-       under Linux. para_audiod is even smaller.
+       with all its features compiled in (mysql/random/playlist
+       selector, mp3/ogg support, http/ortp support) is about 110K
+       on i386 under Linux. para_audiod is even smaller.
 
 command line interface, including shell:
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/INSTALL b/INSTALL
index 9bf20154454ecd0c083ca4555488fcacda176b65..04bf53fda45ea1892ec417a139d2a8f7704b6bf1 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -76,28 +76,29 @@ example) and try
 to retrieve the list of available commands and some server info.
 
 
-Choose your database tool (dbtool)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You have two options:
+Choose an audio file selector
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+paraslash has three different audio file selectors: random (default),
+playlist and mysql.
 
-       1. Use the mysql dbtool which comes with paraslash and requires
-       mysql.
+       The random selector chooses files randomly from the given
+       directory.
 
-       2. If you can not use the mysql dbtool and you just want
-       to quickly make paraslash working, use the random dbtool.
-       The directory which is searched for audio files can be given
-       via the server option --random_dbtool_dir.
+       The playlist selector allows to send a playlist to para_server
+       via the lpl (load playlist) command. para_server will choose
+       files from the loaded playlist in sequential order.
 
-       Note, however, that this database tool is really dopey. It
-       scans the given directory on every audio file change and
-       chooses one randomly. There is no further functionality.
+       The mysql selector stores information about your audio
+       files in a mysql database. It is much more involved than
+       the other two selectors and lets you chose files in many
+       interesting ways. If you like to use the mysql selector,
+       read README.mysql and follow the instructions given there.
+       Return to this document when ready.
 
-The current database tool can be changed at runtime via
+The current audio file selector can be changed at runtime via
 
-       para_client cdt new_dbtool
+       para_client cdt new_selector
 
-If you have choosen 1. above, read README.mysql and follow the
-instructions given there.  Return to this document when ready.
 
 
 Start streaming manually
index 91fd3a140e947962aa2fb9cab03cd3938f9dd78f..6d3d39816497aebdfc169790246dc6850df0beb4 100644 (file)
@@ -21,7 +21,7 @@ build_date = $(shell date)
 system = $(shell uname -rs)
 cc_version = $(shell $(CC) --version | head -n 1)
 version = @PACKAGE_VERSION@
-codename = atomic duality
+codename = oriented abstraction
 
 DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
 
@@ -87,7 +87,7 @@ web_misc := $(patsubst %,web/sync/%,$(web_misc))
 misc := bash_completion
 headers := para.h server.h SFont.h crypt.h list.h http.h send.h ortp.h rc4.h \
        close_on_fork.h afs.h db.h gcc-compat.h recv.h filter.h audiod.h \
-       grab_client.h error.h net.h ringbuffer.h daemon.h string.h
+       grab_client.h error.h net.h ringbuffer.h daemon.h string.h ipc.h
 scripts := install-sh configure
 autocrap := Makefile.in config.h.in configure.ac autogen.sh
 tarball := web/sync/doc pics fonts $(c_sources) $(sample_conf) $(headers) \
@@ -211,7 +211,8 @@ distclean: clean
 maintainer-clean: distclean
        rm -f $(gengetopts_c) $(gengetopts_h) *.tar.bz2 \
                $(grutatxt_html) ChangeLog* config.h configure \
-               config.h.in
+               config.h.in skencil/*.pdf skencil/*.ps
+       rm -rf doc
 
 install: all
        umask 022 && \
diff --git a/NEWS b/NEWS
index 15621e315baeb2ad8a7326dc65aba109b4a7c024..4ac066d6315a3c744ce107f2217287e2283aed85 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,20 +1,37 @@
 NEWS
 ====
 
-0.?.? (to be announced) "atomic duality"
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       o update to gengetopt-2.16
+0.?.? (to be announced) "oriented abstraction"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+0.2.11 (2006-03-11) "atomic duality"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here it is, the first paraslash release developed with git. There
+are fairly many user-visible changes in this release. As two out of
+the three "database tools" of paraslash don't use a database at all,
+they are now called "audio file selectors" instead.
+
+
+       o the cdt command (change database tool) becomes chs (change
+       selector)
+
+       o no more colon separators: The syntax of some options of
+       para_audiod and para_filter have changed. Use --help for
+       more info (and some examples).
+
+       o update to gengetopt-2.16 (thanks to Lorenzo Bettini)
 
-       o switch from cvs to git
+       o switch from cvs to git (should've done that earlier)
 
        o the new ipc subsystem
 
-       o new database tool: plm, the playlist manager
+       o new audio file selector: playlist
 
-       o para_server: the dopey file selector is now called "random",
-       and it is selected by default.  Use the --dbtool option to
-       choose another dbtool at startup, or the cdt command to switch
-       between the supported dbtools.
+       o para_server: the dopey selector is now called "random",
+       and is the default selector. Use the --selector option to
+       choose another selector at startup, or the chs command to
+       change the selector at runtime.
 
        o X86_64 fixes (thanks to Steffen Klassert)
 
diff --git a/README b/README
index b51a8d39cd8ca153c7996f8a6e9dd94f41bd2b03..872a0fa0b5461bad3f45c19915005c39758cb050 100644 (file)
--- a/README
+++ b/README
@@ -24,15 +24,15 @@ It contains the following programs:
        All senders have the same set of commands that allow to
        control the access permissions of the stream.
 
-       para_server needs a "database tool" to work, mainly to
-       determine which song to stream next. There are three such
-       tools available: random, plm and mysql. The former chooses
-       audio files randomly and plm, the playlist manager, can handle
+       para_server needs an  "audio file selector" to work, mainly
+       to determine which song to stream next. There are three
+       selectors available: random, playlist and mysql. The former
+       chooses audio files randomly and  playlist can handle, well,
        playlists. Both are always supported.
 
-       The (optional) mysql database tool connects to a mysql server
-       which holds information on your audio files. It has several
-       unusual features, see README.mysql for details.
+       The optional mysql selector connects to a mysql server which
+       holds information on your audio files. It has several unusual
+       features, see README.mysql for details.
 
 - para_client (obligatory):
 
@@ -80,20 +80,21 @@ It contains the following programs:
 
 - para_gui (optional, but recommended):
 
-        Themable ncurses-based gui. It calls para_audioc and presents
-        the obtained information in an ncurses window. para_gui
-        provides key-bindings for the most common commands and new
-        key-bindings can be added easily.
+       Themable ncurses-based gui. It calls para_audioc and presents
+       the obtained information in an ncurses window. para_gui
+       provides key-bindings for the most common commands and new
+       key-bindings can be added easily.
 
 - para_sdl_gui (optional):
 
-        SDL-based gui. Similar to para_gui but presents its output in
-        an X window (fullscreen mode is also available) and can display
-        jpg images on a per song basis. para_sdl_gui provides an input
-        prompt to enter arbitrary commands. However, it can also be used
-        non-interactively (e.g. as a screen saver) via the -i switch.
+       SDL-based gui. Similar to para_gui but presents its output
+       in an X window (fullscreen mode is also available) and can
+       display jpg images on a per song basis. para_sdl_gui provides
+       an input prompt to enter arbitrary commands. However, it
+       can also be used non-interactively (e.g. as a screen saver)
+       via the -i switch.
 
-- para_krell (optional, only useful in conjunction with the mysql dbtool):
+- para_krell (optional, only useful in conjunction with the mysql selector):
 
        A plugin for gkrellm which shows small pictures of the
        current song. It allows you to launch 27 different commands
@@ -104,13 +105,13 @@ It contains the following programs:
 
        A (Linux-only) alarm clock and volume-fader.
 
-- para_dbadm (optional, only useful in conjunction with the mysql dbtool):
+- para_dbadm (optional, only useful in conjunction with the mysql selector):
 
        Very simple curses-based frontend which uses libmenu. Useful
        for quickly changing the attributes of the current song
        (e.g. from para_gui as an external command).
 
-- para_slider (optional, only useful in conjunction with the mysql dbtool):
+- para_slider (optional, only useful in conjunction with the mysql selector):
 
        A small X application which shows a scrollbar for each
        attribute defined in the mysql database. It creates a stream
@@ -141,7 +142,7 @@ In any case you need
 
        - software mixing, e.g. ALSA and the direct mixing plugin (dmix)
 
-If you want to use the mysql-based dbtool (recommended), you also need
+If you want to use the mysql-based audio file selector, you also need
 
        - mysql-server
        - mysql-client
@@ -211,9 +212,9 @@ Distribution of paraslash is covered by the GNU GPL. See file COPYING.
 
 LIMITATIONS:
 ~~~~~~~~~~~~
-The mysql database tool assumes that the basenames of your audio files
-are unique. If this is not the case, don't use this database tool,
-rename your files, or create your own database tool.
+The mysql selector assumes that the basenames of your audio files are
+unique. If this is not the case, don't use this selector, rename your
+files, or create your own one.
 
 THE AUTHOR:
 ~~~~~~~~~~~
index 7af828073e3621385ac3db5cf69e3bb63b491bc7..92c8c6dc425b03113f5b3e1f31668a57e89ac186 100644 (file)
@@ -1,8 +1,8 @@
 README.mysql
 ============
 
-This file describes how to use the mysql database tool which comes
-with the paraslash package.
+This file describes how to use the mysql audio file selector which
+comes with the paraslash package.
 
 It assumes you have already installed mysql and paraslash as described
 in INSTALL, so read README and INSTALL before proceeding.
@@ -41,39 +41,36 @@ effect you'll need to do
 
 Or, restart the server.
 
-Switch to the mysql dbtool
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Switch to the mysql audio file selector
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Actually, the mysql database tool should already be selected (just
-ignore the warning about the missing database). To verify that it is
-indeed activated, type
+The command
 
-       para_client cdt
+       para_client chs
 
-which prints the name of the current database tool. If the mysql
-dbtool is not selected, try
+prints the name of the current selector. Try
 
-       para_client cdt mysql
+       para_client chs mysql
 
-If this doesn't work either, it means that some required config options
-were not specified (check the log for more info) or that para_server
-was built without mysql support. Type
+to switch to the mysql selector. If this doesn't work, it means that
+some required config options were not specified (check the log for
+more info) or that para_server was built without mysql support. Type
 
        para_client si
 
-to find out. If mysql is not mentioned as a supported database tool,
+to find out. If mysql is not mentioned as a supported selector,
 you'll have to recompile.
 
 
 Create a new database
 ~~~~~~~~~~~~~~~~~~~~~
 
-Once the mysql database tool is selected, create the database:
+Once the mysql selector is activated, create the database:
 
        para_client cdb
-       para_client cdt mysql
+       para_client chs mysql
 
-The second command forces para_server to re-init the mysql dbtool.
+The second command forces para_server to re-init the mysql selector.
 Check the log. There should not be any warnings or errors.
 
 
@@ -91,7 +88,7 @@ The command
 
        para_client ls
 
-prints the list of all files known by the mysql dbtool. If the list
+prints the list of all files known by the mysql selector. If the list
 is empty, double check the mysql_audio_file_dir option.
 
 
@@ -120,7 +117,7 @@ You should now be able to start streaming with
 Attribute usage
 ~~~~~~~~~~~~~~~
 
-An attribute is simply a bit which can be set for each sound file
+An attribute is simply a bit which can be set for each audio file
 individually. You may have as many attributes as you like. A new
 attribute "test" is created by
 
@@ -130,30 +127,30 @@ and
        para_client laa
 
 lists all available attributes. You can set the "test" attribute for
-the current song by executing
+the current audio file by executing
 
        para_client sa test+
 
-or for any particular song by
+or for any particular audio file by
 
        para_client sa test+ filename
 
-You can unset the attribute "test" for the current song with
+Unset the attribute "test" for the current audio file with
 
-        para_client sa test-
+       para_client sa test-
 
-and you can drop the test attribute entirely from the database with
+and drop the test attribute entirely from the database with
 
        para_client da test
 
 Stream usage
 ~~~~~~~~~~~~
 
-A stream is a pair of expressions in terms of attributes and other
-meta data contained in the database. The first, boolian, expression
-determines the set of songs which are permitted in this stream. The
-second, integer, expression determines the order in which permitted
-songs are going to be fed to the audio file sender(s).
+A stream is a pair of expressions in terms of attributes and other data
+contained in the database. The first, boolian, expression determines
+the set of audio files which are admissible in this stream. The second,
+integer, expression determines the order in which admissible files
+are going to be fed to the audio file sender(s).
 
 To create a new stream called "my_stream", put arbitrary many (including
 none) accept or deny lines and one or zero score lines into some
@@ -163,7 +160,7 @@ expression. The command
 
        para_client stradd my_stream < tmpfile
 
-adds the stream "my_stream" to dbtool's stream database.
+adds the stream "my_stream" to the table of streams.
 
 If the stream definition is really short, you may also just pipe it to
 the client rather than using temporary files. Like this:
@@ -173,9 +170,9 @@ the client rather than using temporary files. Like this:
 
 Example:
 
-        Assume you already have an attribute "test" and you'd like to
-        to restrict the set of songs being played to those having the
-        "test" attribute set. Define a new stream "only_test" by
+       Assume you already have an attribute "test" and you'd like to
+       to restrict the set of songs being played to those having the
+       "test" attribute set. Define a new stream "only_test" by
 
                echo 'accept: IS_SET(test)' | para_client stradd only_test
 
@@ -186,7 +183,7 @@ Example:
        only the desired songs are going to be played.
 
 There is no need to keep the temporary files containing the stream
-definition since you can always query the database to get it back:
+definition since you can always use the strq command to get it back:
 
        para_client strq only_test
 
@@ -279,32 +276,32 @@ definition or as the default scoring rule in the config file) include:
 
        LASTPLAYED()/1440
 
-This means that each song's score is just the number of days that went
-by since this song has been played (one day is 1440 minutes). This
-is fine in many cases since the dbtool then always chooses that
-admissible song, which wasn't played for the longest time.
+This means that the score of an audio file is just the number of days
+that went by since it has been played the last time (one day is 1440
+minutes). In other words, the mysql selector choses that admissible
+file which wasn't played for the longest time.
 
-However, one disadvantage of this scoring sheme is that new songs,
+However, one disadvantage of this scoring sheme is that new files,
 once played, are going to be deferred for a possibly very long period
-depending on the size of your collection of (admissible) songs. Hence
+depending on the size of your collection of (admissible) files. Hence
 the following scoring rule comes into mind:
 
-        score: -NUMPLAYED()
+       score: -NUMPLAYED()
 
-since this gives newer songs, i.e. songs to which you haven't listen to
+since this gives newer files, i.e. files to which you haven't listen to
 that often, a higher score than older songs you already know by heart.
 
 You can also use a combination of these two methods:
 
-        score: LASTPLAYED()/1440 - 10 * NUMPLAYED()
+       score: LASTPLAYED()/1440 - 10 * NUMPLAYED()
 
 which subtracts 10 score points for each time paraslash has played
-this song.
+this file.
 
-Another useful feature for scoring is due to the fact that "true"
-expands to one and "false" to zero. So you can also use the
-IS_SET/IS_N_SET/NAME_LIKE macros in a score line to give
-your favorite band "bar" some extra points:
+Another useful feature for scoring is due to the fact that
+"true" expands to one and "false" to zero. So you can also use the
+IS_SET/IS_N_SET/NAME_LIKE macros in a score line to give your favorite
+band "bar" some extra points:
 
        score: 40 * IS_SET(foo) + 20 * NAME_LIKE(%bar%) + LASTPLAYED()/1440
 
@@ -312,9 +309,9 @@ your favorite band "bar" some extra points:
 Pictures
 ~~~~~~~~
 
-dbtool can also magage images that, when associated with certain songs,
-can be displayed by para_sdl_gui and para_krell when one of these songs
-is playing. It is also possible to just retrieve the current image via
+The mysql selector can also magage images that, when associated
+with one or more audio files, can be displayed by para_sdl_gui and
+para_krell. It is also possible to just retrieve the current image via
 
        para_client pic > filename
 
diff --git a/afs.c b/afs.c
index cb6c3882266876d240ddaaf1fb857d7cda75dbeb..0712cc2720683b3fa8f08194eba95fd5691ab996 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -18,8 +18,8 @@
 
 /** \file afs.c audio file sending functions
  *
- * This contains the audio sending part of para_server which is independent
- * of the current audio format, database tool and of the activated senders.
+ * This contains the audio sending part of para_server which is independent of
+ * the current audio format, audio file selector and of the activated senders.
  */
 
 #include <sys/time.h> /* gettimeofday */
@@ -38,7 +38,7 @@ static struct timeval data_send_barrier;
 static struct timeval eof_barrier;
 
 extern struct misc_meta_data *mmd;
-extern struct dbtool dblist[];
+extern struct audio_file_selector dblist[];
 extern struct sender senders[];
 extern struct gengetopt_args_info conf;
 
@@ -233,7 +233,7 @@ static int update_mmd(void)
 
 static void get_song(void)
 {
-       char **sl = dblist[mmd->dbt_num].get_audio_file_list(10);
+       char **sl = dblist[mmd->selector_num].get_audio_file_list(10);
        int i;
 
        if (!sl)
@@ -253,8 +253,8 @@ static void get_song(void)
                        continue;
                }
                mmd->num_played++;
-               if (dblist[mmd->dbt_num].update_audio_file)
-                       dblist[mmd->dbt_num].update_audio_file(sl[i]);
+               if (dblist[mmd->selector_num].update_audio_file)
+                       dblist[mmd->selector_num].update_audio_file(sl[i]);
                PARA_DEBUG_LOG("%s", "success\n");
                mmd->new_afs_status_flags &= (~AFS_NEXT);
                gettimeofday(&now, NULL);
@@ -353,7 +353,7 @@ static void afs_eof(struct audio_format *af)
        free(tmp);
        tmp  = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_DBINFO1],
                status_item_list[SI_DBINFO2], status_item_list[SI_DBINFO3]);
-       strcpy(mmd->dbinfo, tmp);
+       strcpy(mmd->selector_info, tmp);
        free(tmp);
        mmd->filename[0] = '\0';
        mmd->size = 0;
index ccc3f9b962b5d7627d16026b5820b0f9ab9a882f..cd43f24f47fa31beb27184a39f6dcd560fe3fb3e 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -569,7 +569,7 @@ static void close_stat_pipe(void)
        stat_pipe = -1;
        kill_all_decoders();
        for (i = 0; i < RINGBUFFER_SIZE; i++)
-               ringbuffer_add(stat_item_ringbuf, para_strdup(NULL));
+               free(ringbuffer_add(stat_item_ringbuf, para_strdup(NULL)));
        dump_empty_status();
        length_seconds = 0;
        offset_seconds = 0;
@@ -577,9 +577,8 @@ static void close_stat_pipe(void)
        playing = 0;
        msg = make_message("%s:no connection to para_server\n",
                status_item_list[SI_STATUS_BAR]);
-       ringbuffer_add(stat_item_ringbuf, msg);
+       free(ringbuffer_add(stat_item_ringbuf, msg));
        stat_client_write(msg);
-       free(msg);
 }
 
 static void __noreturn clean_exit(int status, const char *msg)
@@ -806,7 +805,7 @@ static void check_stat_line(char *line)
 
        if (!line)
                return;
-       ringbuffer_add(stat_item_ringbuf, line);
+       free(ringbuffer_add(stat_item_ringbuf, para_strdup(line)));
        stat_client_write(line);
        itemnum = stat_line_valid(line);
        if (itemnum < 0)
index 5a4dcaeac6995cb2700982af112038188bdb3178..d317c345314908b4629d7452e2f5fec4cb172f67 100644 (file)
--- a/command.c
+++ b/command.c
@@ -18,7 +18,6 @@
 
 /** \file command.c does client authentication and executes server commands */
 
-
 #include <malloc.h> /* mallinfo */
 #include <sys/time.h> /* gettimeofday */
 #include "crypt.h"
@@ -45,7 +44,7 @@ static unsigned char rc4_buf[2 * RC4_KEY_LEN];
 extern const char *status_item_list[NUM_STAT_ITEMS];
 extern struct misc_meta_data *mmd;
 extern struct gengetopt_args_info conf;
-extern struct dbtool dblist[];
+extern struct audio_file_selector dblist[];
 extern struct audio_format afl[];
 extern struct sender senders[];
 extern char *user_list;
@@ -64,7 +63,7 @@ static int com_stop(int, int, char **);
 static int com_pause(int, int, char **);
 static int com_next(int, int, char **);
 static int com_nomore(int, int, char **);
-static int com_cdt(int, int, char **);
+static int com_chs(int, int, char **);
 static int com_ff(int, int, char **);
 static int com_jmp(int, int, char **);
 static int com_sender(int, int, char **);
@@ -73,14 +72,14 @@ static int com_sender(int, int, char **);
 /* commands that are handled by the server itself */
 static struct server_command cmd_struct[] = {
 {
-.name = "cdt",
-.handler = com_cdt,
+.name = "chs",
+.handler = com_chs,
 .perms = DB_READ | DB_WRITE,
-.description = "change database tool",
-.synopsis = "cdt [name_of_new_dbtool]",
+.description = "change the current audio file selector",
+.synopsis = "chs [new_selector]",
 .help =
-"Deactivate current dbtool and activate name_of_new_dbtool. If no\n"
-"argument was given, print the current database tool.\n"
+"Shutdown the current selector and activate new_selector. If no\n"
+"argument was given, print the name of the current selector.\n"
 },
 
 {
@@ -334,7 +333,6 @@ static char *afs_status_tohuman(unsigned int flags)
                return para_strdup("paused");
 }
 
-
 /*
  * return human readable permission string. Never returns NULL.
  */
@@ -428,11 +426,11 @@ static char *get_status(struct misc_meta_data *nmmd)
                status_item_list[SI_STATUS_BAR], bar ? bar : "(none)",
                status_item_list[SI_STATUS], status,
                status_item_list[SI_STATUS_FLAGS], flags,
-               status_item_list[SI_DBTOOL], dblist[nmmd->dbt_num].name,
+               status_item_list[SI_SELECTOR], dblist[nmmd->selector_num].name,
 
                status_item_list[SI_OFFSET], offset,
                status_item_list[SI_FORMAT], audio_format_name(nmmd->audio_format),
-               nmmd->dbinfo,
+               nmmd->selector_info,
                nmmd->audio_file_info,
 
                status_item_list[SI_UPTIME], ut,
@@ -561,15 +559,15 @@ static int com_si(int fd, int argc, __unused char **argv)
 {
        int i, ret;
        char *ut;
-       char *dbtools = NULL, *sender_info = NULL, *sender_list = NULL;
+       char *selectors = NULL, *sender_info = NULL, *sender_list = NULL;
        struct mallinfo mi = mallinfo();
 
        if (argc)
                return -E_COMMAND_SYNTAX;
        mmd_lock();
        for (i = 0; dblist[i].name; i++) {
-               dbtools = para_strcat(dbtools, dblist[i].name);
-               dbtools = para_strcat(dbtools, " ");
+               selectors = para_strcat(selectors, dblist[i].name);
+               selectors = para_strcat(selectors, " ");
        }
        for (i = 0; senders[i].name; i++) {
                char *info = senders[i].info();
@@ -584,7 +582,7 @@ static int com_si(int fd, int argc, __unused char **argv)
                "mallinfo: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
                "current loglevel: %i\n"
-               "supported database tools: %s\n"
+               "supported audio file selectors: %s\n"
                "supported audio formats: %s\n"
                "supported senders: %s\n"
                "%s",
@@ -595,14 +593,14 @@ static int com_si(int fd, int argc, __unused char **argv)
                mmd->num_commands,
                mmd->num_connects,
                conf.loglevel_arg,
-               dbtools,
+               selectors,
                SUPPORTED_AUDIO_FORMATS,
                sender_list,
                sender_info
        );
        mmd_unlock();
        free(ut);
-       free(dbtools);
+       free(selectors);
        free(sender_list);
        free(sender_info);
        return ret;
@@ -678,7 +676,6 @@ static int com_sb(int socket_fd, int argc, char **argv)
 /* stat */
 static int com_stat(int socket_fd, int argc, char **argv)
 {
-//     char *old_stat = NULL, *old_dbinfo = NULL;
        int ret, num = 0;/* status will be printed that many
                          * times. num <= 0 means: print forever
                          */
@@ -734,11 +731,12 @@ static struct server_command *get_cmd_ptr(char *name, char **handler)
                                *handler = para_strdup("para_server"); /* server commands */
                        return cmd;
                }
-       /* not found, look for dbtool commands */
+       /* not found, look for commands supported by the current selector */
        mmd_lock();
        if (handler)
-               *handler = make_message("the %s database tool", dblist[mmd->dbt_num].name);
-       cmd = dblist[mmd->dbt_num].cmd_list;
+               *handler = make_message("the %s selector",
+                       dblist[mmd->selector_num].name);
+       cmd = dblist[mmd->selector_num].cmd_list;
        mmd_unlock();
        for (; cmd->name; cmd++)
                if (!strcmp(cmd->name, name))
@@ -758,8 +756,8 @@ static int com_help(int fd, int argc, char **argv)
                if ((ret = send_description(fd, cmd_struct, "server", 0)) < 0)
                        return ret;
                mmd_lock();
-               handler = para_strdup(dblist[mmd->dbt_num].name);
-               cmd = dblist[mmd->dbt_num].cmd_list;
+               handler = para_strdup(dblist[mmd->selector_num].name);
+               cmd = dblist[mmd->selector_num].cmd_list;
                mmd_unlock();
                ret = send_description(fd, cmd, handler, 0);
                free(handler);
@@ -850,29 +848,29 @@ static int com_pause(__unused int socket_fd, int argc, __unused char **argv)
        return 1;
 }
 
-static int com_cdt(int fd, int argc, char **argv)
+static int com_chs(int fd, int argc, char **argv)
 {
        int i, ret;
 
        if (!argc) {
-               char *dbtool;
+               char *selector;
                mmd_lock();
-               dbtool = para_strdup(dblist[mmd->dbt_num].name);
+               selector = para_strdup(dblist[mmd->selector_num].name);
                mmd_unlock();
-               ret = send_va_buffer(fd, "%s\n", dbtool);
-               free(dbtool);
+               ret = send_va_buffer(fd, "%s\n", selector);
+               free(selector);
                return ret;
        }
        for (i = 0; dblist[i].name; i++) {
                if (strcmp(dblist[i].name, argv[1]))
                        continue;
                mmd_lock();
-               mmd->dbt_change = i;
+               mmd->selector_change = i;
                mmd->events++;
                mmd_unlock();
                return 1;
        }
-       return -E_BAD_DBTOOL;
+       return -E_BAD_SELECTOR;
 }
 
 /* next */
@@ -987,7 +985,7 @@ static struct server_command *parse_cmd(const char *cmdstr)
 
        sscanf(cmdstr, "%200s%n", buf, &n);
        if (!n)
-               return NULL;
+               return NULL;
        buf[n] = '\0';
        return get_cmd_ptr(buf, NULL);
 }
@@ -997,7 +995,6 @@ long int para_rand(long unsigned max)
        return (long int) ((max + 0.0) * (random() / (RAND_MAX + 1.0)));
 }
 
-
 /* Open user_list file, returns pointer to opened file on success,
  * NULL on errors
  */
@@ -1074,22 +1071,20 @@ static void init_rc4_keys(void)
        PARA_DEBUG_LOG("rc4 keys initialized (%u:%u)\n",
                (unsigned char) rc4_buf[0],
                (unsigned char) rc4_buf[RC4_KEY_LEN]);
-        RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf);
-        RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
+       RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf);
+       RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
 }
 
 static void rc4_recv(unsigned long len, const unsigned char *indata, unsigned char *outdata)
 {
-        RC4(&rc4_recv_key, len, indata, outdata);
+       RC4(&rc4_recv_key, len, indata, outdata);
 }
+
 static void rc4_send(unsigned long len, const unsigned char *indata, unsigned char *outdata)
 {
-        RC4(&rc4_send_key, len, indata, outdata);
+       RC4(&rc4_send_key, len, indata, outdata);
 }
 
-
-
-
 int handle_connect(int fd, struct sockaddr_in *addr)
 {
        int numbytes, ret, argc, use_rc4 = 0;
index 224d840aed872e2cd26f75e0d46a7e7ae50f765d..fd34ed900790d90cd0eee4da239699ebcc00bb9e 100644 (file)
@@ -72,8 +72,8 @@ audiod_errlist_objs="audiod exec close_on_fork signal string daemon stat net
 audiod_ldflags=""
 
 server_cmdline_objs="server.cmdline"
-server_errlist_objs="server mp3 afs command net string signal random_dbtool time daemon stat
-       crypt http_send dccp dccp_send db close_on_fork plm_dbtool ipc"
+server_errlist_objs="server mp3 afs command net string signal random_selector
+       time daemon stat crypt http_send db close_on_fork playlist_selector ipc dccp dccp_send"
 server_ldflags=""
 
 ########################################################################### ssl
@@ -150,10 +150,10 @@ AC_CHECK_LIB([mysqlclient], [mysql_init], [], [
 ])
 if test "$have_mysql" = "yes"; then
        server_ldflags="$server_ldflags -lmysqlclient"
-       server_errlist_objs="$server_errlist_objs mysql"
+       server_errlist_objs="$server_errlist_objs mysql_selector"
        AC_DEFINE(HAVE_MYSQL, 1, [define to 1 to turn on mysql support])
 else
-       AC_MSG_WARN([no libmysqlclient, cannot build mysql-based dbtool])
+       AC_MSG_WARN([cannot build mysql-based audio file selector])
 fi
 ########################################################################### ogg
 have_ogg="yes"
diff --git a/db.c b/db.c
index d97a6e0e917ffd8b3662ba96d13bbcd37aadefd3..a1cc1aebc09e18c01d2685919bd6c407447d4c8b 100644 (file)
--- a/db.c
+++ b/db.c
@@ -17,7 +17,7 @@
  */
 
 
-/** \file db.c functions common to all database tools. */
+/** \file db.c functions common to all audio file selectors */
 
 #include "server.cmdline.h"
 #include "server.h"
diff --git a/db.h b/db.h
index 3347d3ad239d5fd2d364c99083278f0b0b967a8e..b136869952eace5d5b05886f2179f2c4a2280245 100644 (file)
--- a/db.h
+++ b/db.h
  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  */
 
-/** \file db.h data structures common to all database tools */
+/** \file db.h data structures common to all audio file selectors */
 
 #include <sys/select.h>
 
-enum supported_dbtools {DBT_DOPEY,
-#ifdef HAVE_MYSQL
-       DBT_MYSQL,
-#endif
-       NUM_DBTOOLS
-};
 
 int find_audio_files(const char *dirname, int (*f)(const char *, const char *));
 
 /**
- * describes one of para_server's supported database tools
+ * describes one supported audio file selector
  *
- * There is exactly one such struct for each supported database tool.  During
- * the startup part of para_server the \a init() function of the activated
- * database tool gets called which fills in the other members.
+ * 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 dbtool {
+struct audio_file_selector {
 /**
- * name name of this database tool
+ * name name of this selector
  */
 const char *name;
 /**
- * the database init routine
+ * 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 database tool should be ignored for now (it may later be activated
- * again via the cdt command).
+ * 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 dbtool
+ * 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 database tool.
+ * holds the list of all commands that are supported by this selector.
  */
-int (*init)(struct dbtool *self);
+int (*init)(struct audio_file_selector *self);
 /**
- * list of commands supported by this dbtool
+ * list of commands supported by this selector
  */
 struct server_command *cmd_list;
 /**
@@ -90,20 +84,21 @@ char **(*get_audio_file_list)(unsigned int num);
 void (*update_audio_file)(char *audio_file);
 /**
  *
- * shutdown this database tool and free all resources
+ * shutdown this selector and free all resources
  *
- * This gets called whenever the database tool changes (via the cdt command),
- * or when para_server receives the HUP signal, or when para_server shuts down.
- * It is assumed to succeed.
+ * 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 database tool gets called just
- * before para_server enters its main select loop. The dbtool may add its own
- * file descriptors to the \a rfds or the \a wfds set.
+ * 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.
  *
  * If a file descriptor was added, \a max_fileno must be increased by
  * this function, if neccessary.
@@ -120,11 +115,11 @@ int (*pre_select)(fd_set *rfds, fd_set *wfds);
  */
 void (*post_select)(fd_set *rfds, fd_set *wfds);
 /**
- * each dbtool has its private data pointer */
+ * each selector has its private data pointer */
 void *private_data;
 };
 
-int mysql_dbtool_init(struct dbtool*);
-int plm_dbtool_init(struct dbtool*);
-int random_dbtool_init(struct dbtool*);
+int mysql_selector_init(struct audio_file_selector*);
+int playlist_selector_init(struct audio_file_selector*);
+int random_selector_init(struct audio_file_selector*);
 
diff --git a/dbadm.c b/dbadm.c
index 5f807eb2625d3e54baf33849ae659a8e2fcf1092..e7d2c33862e32a81e530ea434e2541f769cfcabc 100644 (file)
--- a/dbadm.c
+++ b/dbadm.c
@@ -16,7 +16,7 @@
  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  */
 
-/** \file dbadm.c simple attribute setting utility for the mysql dbtool */
+/** \file dbadm.c simple attribute setting utility for the mysql selector */
 
 #include "para.h"
 #include <menu.h>
diff --git a/error.h b/error.h
index 9ed59c15564d50da538528c6b931f15d6be5323d..4b3cce28bf4f67b69d4d5abe8749a8d8e4846daa 100644 (file)
--- a/error.h
+++ b/error.h
 /** \file error.h list of error messages for all subsystems */
 
 /** \cond list of all subsystems that support the shiny new error facility */
-enum para_subsystem {SS_RECV,
-       SS_NET, SS_ORTP_RECV, SS_AUDIOD, SS_EXEC, SS_CLOSE_ON_FORK, SS_SIGNAL,
-       SS_STRING, SS_DAEMON, SS_STAT, SS_TIME, SS_GRAB_CLIENT, SS_HTTP_RECV,
-       SS_RECV_COMMON, SS_FILTER_CHAIN, SS_WAV, SS_COMPRESS, SS_OGGDEC, SS_FILTER,
-       SS_COMMAND, SS_RANDOM_DBTOOL, SS_PLM_DBTOOL, SS_CRYPT, SS_HTTP_SEND, SS_ORTP_SEND, SS_DB, SS_OGG,
-       SS_MP3, SS_MP3DEC, SS_SERVER, SS_AFS, SS_MYSQL, SS_IPC, SS_DCCP, SS_DCCP_RECV,
-       SS_DCCP_SEND, SS_RINGBUFFER};
+enum para_subsystem {
+       SS_RECV,
+       SS_NET,
+       SS_ORTP_RECV,
+       SS_AUDIOD,
+       SS_EXEC,
+       SS_CLOSE_ON_FORK,
+       SS_SIGNAL,
+       SS_STRING,
+       SS_DAEMON,
+       SS_STAT,
+       SS_TIME,
+       SS_GRAB_CLIENT,
+       SS_HTTP_RECV,
+       SS_RECV_COMMON,
+       SS_FILTER_CHAIN,
+       SS_WAV,
+       SS_COMPRESS,
+       SS_OGGDEC,
+       SS_FILTER,
+       SS_COMMAND,
+       SS_RANDOM_SELECTOR,
+       SS_PLAYLIST_SELECTOR,
+       SS_CRYPT,
+       SS_HTTP_SEND,
+       SS_ORTP_SEND,
+       SS_DB,
+       SS_OGG,
+       SS_MP3,
+       SS_MP3DEC,
+       SS_SERVER,
+       SS_AFS,
+       SS_MYSQL_SELECTOR,
+       SS_IPC,
+       SS_DCCP,
+       SS_DCCP_RECV,
+       SS_DCCP_SEND,
+       SS_RINGBUFFER};
+
 #define NUM_SS (SS_RINGBUFFER + 1)
 extern const char **para_errlist[];
 /** \endcond */
@@ -186,12 +218,12 @@ extern const char **para_errlist[];
        PARA_ERROR(WRITE_OK, "can not check whether fd is writable"), \
 
 
-#define RANDOM_DBTOOL_ERRORS \
+#define RANDOM_SELECTOR_ERRORS \
        PARA_ERROR(FILE_COUNT, "audio file count exceeded"), \
        PARA_ERROR(NOTHING_FOUND, "no audio files found"), \
 
 
-#define MYSQL_ERRORS \
+#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"), \
@@ -216,7 +248,7 @@ extern const char **para_errlist[];
 #define COMMAND_ERRORS \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
        PARA_ERROR(AUTH, "did not receive auth request"), \
-       PARA_ERROR(BAD_DBTOOL, "no such database tool"), \
+       PARA_ERROR(BAD_SELECTOR, "no such audio file selector"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
        PARA_ERROR(BAD_CMD, "invalid command"), \
        PARA_ERROR(PERM, "permission denied"), \
@@ -225,7 +257,7 @@ extern const char **para_errlist[];
        PARA_ERROR(LOCK, "lock error"), \
        PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
 
-#define PLM_DBTOOL_ERRORS \
+#define PLAYLIST_SELECTOR_ERRORS \
        PARA_ERROR(LOAD_PLAYLIST, "failed to load playlist"), \
 
 
@@ -360,13 +392,13 @@ SS_ENUM(OGG);
 SS_ENUM(SERVER);
 SS_ENUM(AFS);
 SS_ENUM(COMMAND);
-SS_ENUM(RANDOM_DBTOOL);
-SS_ENUM(PLM_DBTOOL);
+SS_ENUM(RANDOM_SELECTOR);
+SS_ENUM(PLAYLIST_SELECTOR);
 SS_ENUM(CRYPT);
 SS_ENUM(HTTP_SEND);
 SS_ENUM(ORTP_SEND);
 SS_ENUM(DB);
-SS_ENUM(MYSQL);
+SS_ENUM(MYSQL_SELECTOR);
 SS_ENUM(IPC);
 SS_ENUM(DCCP);
 SS_ENUM(DCCP_RECV);
index 37c1c4305e7c82b8140bc4791046f777e9e88662..46b48251d757c91360126d927469deab08769e88 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -95,7 +95,7 @@ static void open_filters(void)
 static int parse_config(int argc, char *argv[])
 {
        static char *cf; /* config file */
-        struct stat statbuf;
+       struct stat statbuf;
        int i;
 
        if (cmdline_parser(argc, argv, &conf))
@@ -105,7 +105,7 @@ static int parse_config(int argc, char *argv[])
                cf = make_message("%s/.paraslash/filter.conf", home);
                free(home);
        }
-        if (!stat(cf, &statbuf)) {
+       if (!stat(cf, &statbuf)) {
                if (cmdline_parser_configfile(cf, &conf, 0, 0, 0))
                        return -E_FILTER_SYNTAX;
        }
@@ -113,8 +113,10 @@ static int parse_config(int argc, char *argv[])
                return 1;
        printf("available filters: ");
        for (i = 0; filters[i].name; i++)
-               printf("%s%s", i? " " : "", filters[i].name);
-       printf("\nTry para_filter -f<filtername>:-h for help on <filtername>\n");
+               printf("%s%s%s", i? " " : "", filters[i].name,
+                       filters[i].parse_config? "*": "");
+       printf("\nFilters marked with \"*\" have further command line options. Try\n"
+               "\tpara_filter -f '<filtername> -h'\nfor more information.\n");
        exit(EXIT_SUCCESS);
 }
 
index 4157835cda085b7d7757765a18841bfe0dc749d7..725a90302a7b0dca0b078c5450d2568728c90bb2 100644 (file)
@@ -7,11 +7,10 @@ way.  The same filter may appear more than once,
 order matters.
 
 Filter options may be specified for each '-f'
-option separately. Insinde these options ':'
-must be used as the separator instead of white
-space. Example:
+option separately. Note that you will have to
+quote these options like this:
 
-       -f compress:--anticlip:--volume:2
+       -f 'compress --anticlip --volume 2'
 "
 string typestr="filter_spec" no multiple
 
index 6265f19c931dc26b53a76eecfd887164961c928d..42db37830718f3618bcb0d34c25cdb04bca60e05 100644 (file)
@@ -211,7 +211,7 @@ static int parse_filter_args(int filter_num, char *options, void **conf)
 /**
  * check the filter command line options
  *
- * \param fa the command line options (values separated by colons)
+ * \param fa the command line options
  * \param conf points to the filter configuration upon successful return
  *
  * Check if \a fa starts with a the name of a supported filter, followed by
index 87d8e050cf92ebe864b512016b572ad57dfbdcba..029e043a867c32f13897baf9c19a1546f6b2ffd3 100644 (file)
@@ -148,14 +148,14 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_STATUS_FLAGS].y = 17;
        d[SI_STATUS_FLAGS].len = 10;
 
-       d[SI_DBTOOL].prefix = "dbtool: ";
-       d[SI_DBTOOL].postfix = "";
-       d[SI_DBTOOL].fg = COLOR_RED;
-       d[SI_DBTOOL].bg = COLOR_BLACK;
-       d[SI_DBTOOL].align = CENTER;
-       d[SI_DBTOOL].x = 21;
-       d[SI_DBTOOL].y = 17;
-       d[SI_DBTOOL].len = 20;
+       d[SI_SELECTOR].prefix = "selector: ";
+       d[SI_SELECTOR].postfix = "";
+       d[SI_SELECTOR].fg = COLOR_RED;
+       d[SI_SELECTOR].bg = COLOR_BLACK;
+       d[SI_SELECTOR].align = CENTER;
+       d[SI_SELECTOR].x = 21;
+       d[SI_SELECTOR].y = 17;
+       d[SI_SELECTOR].len = 20;
 
        d[SI_FORMAT].prefix = "format: ";
        d[SI_FORMAT].postfix = "";
index 04f872133b1d5d8844691a2ef29caf07ce97f2f5..2e1d467b0a20b4b5dccf7f23b3724f7d3c942d4b 100644 (file)
@@ -175,7 +175,6 @@ static int http_recv_open(struct receiver_node *rn)
        rn->buf = para_calloc(BUFSIZE);
        rn->private_data = para_calloc(sizeof(struct private_http_recv_data));
        phd = rn->private_data;
-       optind = 0;
        ret = -E_HOST_INFO;
        if (!(he = get_host_info(conf->host_arg)))
                goto err_out;
diff --git a/index.html b/index.html
deleted file mode 100644 (file)
index acb329c..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-       <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
-       <title>Paraslash</title>
-       <style type="text/css">
-               body {
-                       background-color: #000000;
-                       color: #cccccc;
-               }
-               :link { color: #990000 }
-               :visited { color: #990000 }
-               td.c2 {font-family: arial, helvetica, sans-serif; font-size: 80%}
-               td.c1 {font-family: lucida, helvetica; font-size: 248%}
-               a:hover {background:#ff0;}
-               a:hover img {background:#fff;}
-       </style>
-       <link rel="shortcut icon" href="paraslash.ico">
-</head>
-<body>
-       <basefont face="lucida, helvetica, arial" size="3">
-       <table border="0" cellpadding="0" cellspacing="0">
-       <tr>
-               <td>
-                       <a href="/"><IMG SRC="paraslash.png" alt="paraslash" border="0"></a><BR>
-               </td>
-               <td>
-                       <h3>Paraslash: Play, archive, rate and stream
-                       large audio sets happily</h3>
-
-                       A set of tools for doing just what its name
-                       suggests.
-               </td>
-       </tr>
-       <tr>
-               <td valign="TOP">
-                       <br><a href="#feature_list">Feature list</a>
-                       <br><a href="#screenshots">Screenshots</a>
-                       <br><a href="#download">Download</a>
-                       <br><a href="#live_demo">Live Demo</a>
-                       <br><a href="#changes">Changes</a> <br><a
-                       href="#documentation">Documentation</a>
-                       <br><a href="#license">License</a> <br><a
-                       href="#author">Author</a>
-               </td>
-               <td Valign="TOP">
-       <hr>
-                       <h3>Events</h3>
-                       <ul>
-                               <li>2005-12-27: paraslash-0.2.7 "transparent invariance"</li>
-                               <li>2005-10-29: paraslash-0.2.6 "recursive compensation"</li>
-                               <li>2005-10-27: <a href="doc/html/index.html">manual pages</a> online</li>
-                               <li>2005-10-13: paraslash-0.2.5 "aggressive resolution"</li>
-                               <li>2005-09-21: paraslash-0.2.4 "toxic anticipation"</li>
-                               <li>2005-09-01: paraslash-0.2.3 "hydrophilic movement"</li>
-                               <li>2005-08-19: paraslash-0.2.2 "tangential excitation"</li>
-                               <li>2005-08-15: paraslash-0.2.1 "surreal experience"</li>
-                               <li>2005-08-06: overview.pdf</li>
-                               <li>2005-08-06: paraslash-0.2.0 "distributed diffusion"</li>
-                               <li>2005-08-01: paraslash live stream</li>
-                               <li>2005-04-18: paraslash-0.1.7 "melting penetration"</li>
-                               <li>2005-03-05: paraslash-0.1.6 "asymptotic balance"</li>
-                               <li>2004-12-31: paraslash-0.1.5 "opaque eternity"</li>
-                               <li>2004-12-19: paraslash-0.1.4 "tunneling transition"</li>
-                               <li>2004-12-10: paraslash-0.1.3 "vanishing inertia"</li>
-                               <li>2004-11-28: paraslash-0.1.2 "spherical fluctuation"</li>
-                               <li>2004-11-05: paraslash-0.1.1 "floating atmosphere"</li>
-                               <li>2004-10-22: paraslash-0.1.0 "rotating cortex"</li>
-                       </ul>
-                       <hr>
-                       <h3><a name="feature_list">Feature list</a></h3>
-                       <ul>
-                               <li>network audio streaming software</li>
-                               <li>client/server tcp-networking</li>
-                               <li>command line interface</li>
-                               <li> <a href="http://www.openssl.org/">openssl</a> user authentication</li>
-                               <li>several grafical user interfaces</li>
-                               <li> <a href="http://www.mysql.com">mysql</a>-based audio file selector</li>
-                       </ul>
-                       See <a href="FEATURES.html">FEATURES</a> for a more detailed list.
-                       <hr>
-
-       <h3><a name="screenshots">Screenshots</a></h3>
-       Everybody loves screenshots, so
-               <a href="screenshots/">here</a>
-       we go.
-       <hr>
-
-       <h3><a name="download">Download</a></h3>
-
-               <p> Only <a href="versions/">source</a> is available,
-               including the <a href="versions/paraslash-cvs.tar.bz2">
-               nightly cvs snapshot</a>. All regular releases are
-               <a href="PUBLIC_KEY">cryptographically signed</a>.
-               Anonymous (read-only) cvs access is also
-               available. Checkout a copy with </p>
-
-               <p> cvs -d
-               :pserver:anonymous@cvs.systemlinux.org:/var/lib/cvs
-               login </p>
-               <p>(empty passwd)</p>
-               <p> cvs -d :pserver:anonymous@cvs.systemlinux.org:/var/lib/cvs co paraslash </p>
-
-
-               <p> Finally, you can <a href="HTML/index.html">RTFS online</a>.
-       <hr>
-
-       <h3><a name="live_demo">Live Demo</a></h3>
-
-       <p> There is a public paraslash stream at www.paraslash.org,
-       streaming
-
-               <a href="http://www.digitalvibes.de/">the music of Henri Petterson</a>.
-
-
-       You can listen to the stream with any mp3 player that supports
-       http streaming. Both </p>
-
-               <p>mpg123 http://www.paraslash.org:8009/</p>
-
-       and
-
-               <p>xmms http://www.paraslash.org:8009/</p>
-
-       <p> are known to work.</p>
-
-       <p> Moreover, there is an anonymous paraslash account
-       available which you can use to have a look at paraslash
-       without configuring and running para_server on your own box.
-       Just download and run
-
-               <a href="demo-script">this shell script</a>
-
-       on your Unix system. If you prefer to do things manually,
-       simply cut-and-paste the instructions given below verbatim
-       to your shell. No root-privileges are required.</p>
-
-<ul>
-               <li>
-                       Check that both aplay and mpg123 are installed on your system
-       </li>
-
-               <li>
-                       <a href="versions">Download </a>
-                       a recent paraslash package. You
-                       you need paraslash-0.2.0 or later
-                       for the demo, paraslash-0.1.x will not work.
-               </li>
-
-               <li>
-
-                       Install the neccessary paraslash binaries
-                       (you can safely ignore any warnings about
-                       missing software):
-
-                       <ul>
-                               <li>tar xjf paraslash-cvs.tar.bz2</li>
-                               <li>cd paraslash-cvs</li>
-                               <li>bin="para_client para_audioc para_audiod para_gui" # all we need
-                               <li>(./configure --prefix="$HOME" && make $bin) > /dev/null</li>
-                               <li> mkdir -p $HOME/bin; cp $bin $HOME/bin
-                               <li> export PATH=$HOME/bin:$PATH
-
-                       </ul>
-                       There should be no errors.
-               </li>
-               <li>
-                       Get the key for the anonymous account on
-                       www.paraslash.org:
-
-                       <ul>
-                               <li> dir="$HOME/.paraslash"; server=www.paraslash.org</li>
-                               <li> mkdir -p $dir</li>
-                               <li> wget --directory-prefix=$dir http://$server/key.anonymous</li>
-                       </ul>
-               </li>
-               <li>
-                       Tell para_client that we want to connect to
-                       www.paraslash.org as user anonymous:
-
-                       <ul>
-                       <li> conf="$dir/client.conf"; socket="$dir/socket"</li>
-                       <li> echo user \"anonymous\" > $conf</li>
-                       <li> echo hostname \"$server\" >> $conf</li>
-                       <li> echo key_file \"$dir/key.anonymous\" >> $conf</li>
-                       <li> echo socket \"$socket\" >> $dir/audioc.conf</li>
-                       </ul>
-               </li>
-               <li>
-                       Start para_audiod
-                       <ul> <li>
-
-                               para_audiod -d --stream_read_cmd
-                               "mp3:mpg123 -s http://$server:8009/"
-                               --stream_write_cmd "mp3:aplay -fcd" -L $dir/audiod.log
-                               --socket=$socket
-
-                       </li> </ul>
-               </li><li>
-                       Start para_gui
-                       <ul> <li>
-                               para_gui
-                       </li> </ul>
-               </li>
-       </ul> <hr>
-
-
-       <h3><a name="changes">Changes</a></h3>
-       Read the complete
-               <a href="ChangeLog.html">ChangeLog</a>
-       or the file
-               <a href="NEWS.html">NEWS</a>
-       containing a brief summary of the changes for each version.
-       <hr>
-
-
-       <h3><a name="documentation">Documentation</a></h3>
-       Have a look at this
-               <a href="overview.pdf">overview</a>,
-       a pdf file containing a sketch which illustrates how the pieces of paraslash work
-       together. Read
-               <a href="README.html">README</a>
-       for general information,
-               <a href="INSTALL.html">INSTALL</a>
-       for installation notes, and
-               <a href="README.mysql.html">README.mysql</a>
-       for instructions on how to use the mysql database tool
-       shipped with paraslash. There is also an online version
-       of paraslash's
-               <a href="doc/html/index.html">manual pages</a>.
-       <hr>
-
-       <h3><a name="license">License</a></h3>
-       Distribution of Paraslash is covered by the GNU GPL. See file
-               <a href="COPYING.html">COPYING</a>.
-       <hr>
-
-       <h3><a name="author">Author</a></h3>
-       Andr&eacute; Noll,
-               <a href="mailto:maan@systemlinux.org"
-               >maan@systemlinux.org</a>
-       <p>
-       Several people helped by reporting bugs, improving documentation,
-       constructive discussions, or, last but not least, by writing
-       free software on which this project is based on. See
-               <a href="CREDITS.html">CREDITS</a>
-       for an incomplete list of people.
-       <hr>
-
-
-       Last modified:
-       <!--#flastmod virtual="" -->
-       <p>
-       <a href="http://validator.w3.org/check?uri=referer"><img border="0"
-         src="http://www.w3.org/Icons/valid-html401"
-         alt="Valid HTML 4.01!" height="31" width="88"></a>
-       </p>
-
-       </td>
-       </table>
-</body>
-</html>
diff --git a/mysql.c b/mysql.c
deleted file mode 100644 (file)
index 9760f08..0000000
--- a/mysql.c
+++ /dev/null
@@ -1,2539 +0,0 @@
-/*
- * Copyright (C) 1999-2006 Andre Noll <maan@systemlinux.org>
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, write to the Free Software
- *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-/** \file mysql.c para_server's mysql-based database tool */
-
-/** \cond some internal constants */
-#define MEDIUM_BLOB_SIZE 16777220 /*  (2**24 + 4) */
-#define BLOB_SIZE 65539 /* (2**16 + 3) */
-/** \endcond */
-#include "server.cmdline.h"
-#include "server.h"
-#include "afs.h"
-#include "db.h"
-#include <mysql/mysql.h>
-#include <mysql/mysql_version.h>
-#include "error.h"
-#include "net.h"
-#include "string.h"
-
-extern struct gengetopt_args_info conf;
-/** pointer to the shared memory area */
-extern struct misc_meta_data *mmd;
-
-static void *mysql_ptr = NULL;
-
-
-static int com_cam(int, int, char **);
-static int com_cdb(int, int, char **);
-static int com_cs(int, int, char **);
-static int com_da(int, int, char **);
-static int com_hist(int, int, char **);
-static int com_info(int, int, char **);
-static int com_laa(int, int, char **);
-static int com_last(int, int, char **);
-static int com_ls(int, int, char **);
-static int com_mbox(int, int, char **);
-static int com_mv(int, int, char **);
-static int com_na(int, int, char **);
-static int com_pic(int, int, char **);
-static int com_picch(int, int, char **);
-static int com_picdel(int, int, char **);
-static int com_piclist(int, int, char **);
-static int com_ps(int, int, char **);
-static int com_rm_ne(int, int, char **);
-static int com_sa(int, int, char **);
-static int com_set(int, int, char **);
-static int com_sl(int, int, char **);
-static int com_stradd_picadd(int, int, char **);
-static int com_streams(int, int, char **);
-static int com_strdel(int, int, char **);
-static int com_strq(int, int, char **);
-static int com_summary(int, int, char **);
-static int com_upd(int, int, char **);
-static int com_us(int, int, char **);
-static int com_verb(int, int, char **);
-static int com_vrfy(int, int, char **);
-
-static struct server_command cmds[] = {
-{
-.name = "cam",
-.handler = com_cam,
-.perms = DB_READ|DB_WRITE,
-.description = "copy all metadata",
-.synopsis = "cam source dest1 [dest2 ...]",
-.help =
-
-"Copy attributes and other meta data from source file to destination\n"
-"file(s). Useful for files that have been renamed.\n"
-
-},
-{
-.name = "cdb",
-.handler = com_cdb,
-.perms = DB_READ|DB_WRITE,
-.description = "create database",
-.synopsis = "cdb [name]",
-.help =
-
-"\tCreate database name containing the initial columns for basic\n"
-"\tinteroperation with server. This command has to be used only once\n"
-"\twhen you use the mysql database tool for the very first time.\n"
-"\n"
-"\tThe optional name defaults to 'paraslash' if not given.\n"
-
-},
-{
-.name = "clean",
-.handler = com_vrfy,
-.perms = DB_READ | DB_WRITE,
-.description = "nuke invalid entries in database",
-.synopsis = "clean",
-.help =
-
-"If the vrfy command shows you any invalid entries in your database,\n"
-"you can get rid of them with clean. Always run 'upd' and 'vrfy'\n"
-"before running this command. Use with caution!\n"
-
-},
-{
-.name = "cs",
-.handler = com_cs,
-.perms = AFS_WRITE | DB_READ | DB_WRITE,
-.description = "change stream",
-.synopsis = "cs [s]",
-.help =
-
-"Selects stream s or prints current stream when s was not given.\n"
-
-},
-{
-.name = "csp",
-.handler = com_cs,
-.perms = AFS_WRITE | DB_READ,
-.description = "change stream and play",
-.synopsis = "csp s",
-.help =
-
-"Select stream s and start playing. If this results in a stream-change,\n"
-"skip rest of current audio file.\n"
-
-},
-{
-.name = "da",
-.handler = com_da,
-.perms = DB_READ | DB_WRITE,
-.description  = "drop attribute from database",
-.synopsis = "da att",
-.help =
-
-"Use with caution. All info on attribute att will be lost.\n"
-
-},
-{
-.name = "hist",
-.handler = com_hist,
-.perms = DB_READ,
-.description = "print history",
-.synopsis = "hist",
-.help =
-
-"Print list of all audio files together with number of days since each\n"
-"file was last played.\n"
-
-},
-{
-.name = "info",
-.handler = com_info,
-.perms = DB_READ,
-.description = "print database info",
-.synopsis = "info [af]",
-.help =
-
-"print database informations for audio file af. Current audio file is\n"
-"used if af is not given.\n"
-
-},
-{
-.name = "la",
-.handler = com_info,
-.perms = DB_READ,
-.description = "list attributes",
-.synopsis = "la [af]",
-.help =
-
-"List attributes of audio file af or of current audio file when invoked\n"
-"without arguments.\n"
-
-},
-{
-.name = "laa",
-.handler = com_laa,
-.perms = DB_READ,
-.description = "list available attributes",
-.synopsis = "laa",
-.help = 
-
-"What should I say more?\n"
-
-},
-{
-.name = "last",
-.handler = com_last,
-.perms = DB_READ,
-.description = "print list of audio files, ordered by lastplayed time",
-.synopsis = "last [n]",
-.help =
-
-"The optional number n defaults to 10 if not specified.\n"
-
-},
-{
-.name = "ls",
-.handler = com_ls,
-.perms = DB_READ,
-.description = "list all audio files that match a LIKE pattern",
-.synopsis = "ls [pattern]",
-.help =
-
-"\tIf pattern was not given, print list of all audio files known\n"
-"\tto the mysql database tool. See the documentation of mysql\n"
-"\tfor the definition of LIKE patterns.\n"
-
-},
-{
-.name = "mbox",
-.handler = com_mbox,
-.perms = DB_READ,
-.description = "dump audio file list in mbox format",
-.synopsis = "mbox [p]",
-.help =
-
-"\tDump list of audio files in mbox format (email) to stdout. If\n"
-"\tthe optional pattern p is given, only those audio files,\n"
-"\twhose basename match p are going to be included. Otherwise,\n"
-"\tall files are selected.\n"
-"\n"
-"EXAMPLE\n"
-"\tThe mbox command can be used together with your favorite\n"
-"\tmailer (this example uses mutt) for browsing the audio file\n"
-"\tcollection:\n"
-"\n"
-"\t\tpara_client mbox > ~/para_mbox\n"
-"\n"
-"\t\tmutt -F ~/.muttrc.para -f ~/para_mbox\n"
-"\n"
-"\tFor playlists, you can use mutt's powerful pattern matching\n"
-"\tlanguage to select files. If you like to tag all files\n"
-"\tcontaining the pattern 'foo', type 'T', then '~s foo'.\n"
-"\n"
-"\tWhen ready with the list, type ';|' (i.e., hit the semicolon\n"
-"\tkey to apply the next mutt command to all tagged messages,\n"
-"\tthen the pipe key) to pipe the selected \"mails\" to a\n"
-"\tsuitable script which adds a paraslash stream where exactly\n"
-"\tthese files are admissable or does whatever thou wilt.\n"
-
-},
-{
-.name = "mv",
-.handler = com_mv,
-.perms = DB_READ | DB_WRITE,
-.description = "rename entry in database",
-.synopsis = "mv oldname newname",
-.help =
-
-"Rename oldname to newname. This updates the data table to reflect the\n"
-"new name. All internal data (numplayed, lastplayed, picid,..) is kept.\n"
-"If newname is a full path, the dir table is updated as well.\n"
-
-},
-{
-.name = "na",
-.handler = com_na,
-.perms = DB_READ | DB_WRITE,
-.description = "add new attribute to database",
-.synopsis = "na att",
-.help =
-
-"This adds a column named att to your mysql database. att should only\n"
-"contain letters and numbers, in paricular, '+' and '-' are not allowed.\n"
-
-},
-{
-.name = "ne",
-.handler = com_rm_ne,
-.perms = DB_READ | DB_WRITE,
-.description = "add new database entries",
-.synopsis = "ne file1 [file2 [...]]",
-.help =
-
-"Add the given filename(s) to the database, where file1,... must\n"
-"be full path names. This command might be much faster than 'upd'\n"
-"if the number of given files is small.\n"
-
-},
-{
-.name = "ns",
-.handler = com_ps,
-.perms = AFS_WRITE | DB_READ | DB_WRITE,
-.description = "change to next stream",
-.synopsis = "ns",
-.help =
-
-"Cycle forwards through stream list.\n"
-
-},
-{
-.name = "pic",
-.handler = com_pic,
-.perms = DB_READ,
-.description = "get picture by name or by identifier",
-.synopsis = "pic [name]",
-.help =
-
-"\tDump jpg image that is associated to given audio file (current\n"
-"\taudio file if not specified) to stdout. If name starts with\n"
-"\t'#' it is interpreted as an identifier instead and the picture\n"
-"\thaving that identifier is dumped to stdout.\n"
-"\n"
-"EXAMPLE\n"
-"\n"
-"\tpara_client pic '#123' > pic123.jpg\n"
-
-},
-{
-.name = "picadd",
-.handler = com_stradd_picadd,
-.perms = DB_READ | DB_WRITE,
-.description = "add picture to database",
-.synopsis = "picadd [picname]",
-.help =
-
-"\tRead jpeg file from stdin and store it as picname in database.\n"
-"\n"
-"EXAMPLE\n"
-"\n"
-"\tpara_client picadd foo.jpg < foo.jpg\n"
-
-},
-{
-.name = "picass",
-.handler = com_set,
-.perms = DB_READ | DB_WRITE,
-.description = "associate a picture to file(s)",
-.synopsis = "picass pic_id file1 [file2...]",
-.help =
-
-"Associate the picture given by pic_id to all given files.\n"
-
-},
-{
-.name = "picch",
-.handler = com_picch,
-.perms = DB_READ | DB_WRITE,
-.description = "change name of picture",
-.synopsis = "picch id new_name",
-.help =
-
-"Asign new_name to picture with identifier id.\n"
-
-},
-{
-.name = "picdel",
-.handler = com_picdel,
-.perms = DB_READ | DB_WRITE,
-.description = "delete picture from database",
-.synopsis = "picdel id1 [id2...]",
-.help =
-
-"Delete each given picture from database.\n"
-
-},
-{
-.name = "piclist",
-.handler = com_piclist,
-.perms = DB_READ,
-.description = "print list of pictures",
-.synopsis = "piclist",
-.help =
-
-"Print id, name and length of each picture contained in the database.\n"
-
-},
-{
-.name = "ps",
-.handler = com_ps,
-.perms = AFS_WRITE | DB_READ | DB_WRITE,
-.description = "change to previous stream",
-.synopsis = "ps",
-.help =
-
-"Cycle backwards through stream list.\n"
-
-},
-{
-.name = "rm",
-.handler = com_rm_ne,
-.perms = DB_READ | DB_WRITE,
-.description = "remove entries from database",
-.synopsis = "rm name1 [name2 [...]]",
-.help =
-
-"Remove name1, name2, ... from the data table. Use with caution\n"
-
-},
-{
-.name = "sa",
-.handler = com_sa,
-.perms = DB_READ | DB_WRITE,
-.description = "set/unset attributes",
-.synopsis = "sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]",
-//.synopsis = "foo",
-.help =
-
-"Set ('+') or unset ('-') attribute at1, at2 etc. for given list of\n"
-"audio files. If no audio files were given the current audio file is\n"
-"used. Example:\n"
-"\n"
-"sa rock+ punk+ classic- LZ__Waldsterben.mp3\n"
-"\n"
-"sets the 'rock' and the 'punk' attribute but unsets the 'classic'\n"
-"attribute.\n"
-
-},
-{
-.name = "skip",
-.handler = com_sl,
-.perms = DB_READ | DB_WRITE,
-.description = "skip subsequent audio files(s)",
-.synopsis = "skip n [s]",
-.help =
-
-"Skip the next n audio files of stream s. This is equivalent to the\n"
-"command 'sl n s', followed by 'us name' for each name the output of sl.\n"
-
-},
-{
-.name = "sl",
-.handler = com_sl,
-.perms = DB_READ,
-.description = "print score list",
-.synopsis = "sl n [s]",
-.help =
-
-"Print sorted list of maximal n lines. Each line is an admissible entry\n"
-"with respect to stream s. The list is sorted by score-value which is\n"
-"given by the definition of s. If s is not given, the current stream\n"
-"is used. Example:\n"
-"\n"
-"      sl 1\n"
-"\n"
-"shows you the audio file the server would select right now.\n"
-
-},
-{
-.name = "snp",
-.handler = com_set,
-.perms = DB_READ | DB_WRITE,
-.description = "set numplayed",
-.synopsis = "snp number af1 [af2 ...]",
-.help =
-
-"Update the numplayed field in the data table for all given audio files.\n"
-
-},
-{
-.name = "stradd",
-.handler = com_stradd_picadd,
-.perms = DB_READ | DB_WRITE,
-.description = "add stream",
-.synopsis = "stradd s",
-.help =
-
-"Add stream s to the list of available streams. The stream definition\n"
-"for s is read from stdin and is then sent to para_server. Example:\n"
-"\n"
-"      echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna\n"
-"\n"
-"adds the new stream 'no_madonna' to the list of available streams. A given\n"
-"audio file is admissible for this stream iff its basename does not contain the\n"
-"string 'Madonna'.\n"
-
-
-},
-{
-.name = "strdel",
-.handler = com_strdel,
-.perms = DB_READ | DB_WRITE,
-.description = "delete stream",
-.synopsis = "strdel s",
-.help =
-
-"Remove stream s from database.\n"
-
-},
-{
-.name = "streams",
-.handler = com_streams,
-.perms = DB_READ,
-.description = "list streams",
-.synopsis = "streams",
-.help =
-
-"Print list of available streams. Use 'cs' to switch to any of these.\n"
-
-},
-{
-.name = "strq",
-.handler = com_strq,
-.perms = DB_READ,
-.description = "query stream definition",
-.synopsis = "strq [s]",
-.help =
-
-"Print definition of stream s to stdout. Use current stream if s was\n"
-"not given.\n"
-
-},
-{
-.name = "summary",
-.handler = com_summary,
-.perms = DB_READ,
-.description = "list attributes",
-.synopsis = "summary",
-.help =
-
-"\tPrint a list of attributes together with number of audio\n"
-"\tfiles having that attribute set.\n"
-
-},
-{
-.name = "upd",
-.handler = com_upd,
-.perms = DB_READ | DB_WRITE,
-.description = "update database",
-.synopsis = "upd",
-.help =
-
-"This command uses the --audio_file_dir option of para_server to locate\n"
-"your audio files. New files are then added to the mysql database. Use\n"
-"this command if you got new files or if you have moved some files\n"
-"around.\n"
-
-},
-{
-.name = "us",
-.handler = com_us,
-.perms = DB_READ | DB_WRITE,
-.description = "update lastplayed time",
-.synopsis = "us name",
-.help =
-
-"Update lastplayed time without actually playing the thing.\n"
-
-},
-{
-.name = "verb",
-.handler = com_verb,
-.perms = DB_READ | DB_WRITE,
-.description = "send verbatim sql query",
-.synopsis = "verb cmd",
-.help =
-
-"Send cmd to mysql server. For expert/debugging only. Note that cmd\n"
-"usually must be escaped. Use only if you know what you are doing!\n"
-
-},
-{
-.name = "vrfy",
-.handler = com_vrfy,
-.perms = DB_READ,
-.description = "list invalid entries in database",
-.synopsis = "vrfy",
-.help =
-
-"Show what clean would delete. Run 'upd' before this command to make\n"
-"sure your database is up to date.\n"
-
-},
-{
-.name = NULL,
-}
-};
-
-static struct para_macro 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,
-       }
-};
-
-static int real_query(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;
-}
-
-/*
- * 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(char *query)
-{
-       void *result;
-
-       if (real_query(query) < 0)
-               return NULL;
-       result = mysql_store_result(mysql_ptr);
-       if (!result)
-               PARA_ERROR_LOG("%s", "store_result error\n");
-       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(char* old, int size)
-{
-       char *new;
-
-       if (!mysql_ptr || size < 0)
-               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(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
- */
-static int com_na(__unused int fd, int argc, char *argv[])
-{
-       char *q;
-       int ret;
-
-       if (argc < 1)
-               return -E_MYSQL_SYNTAX;
-       q = make_message("alter table data add %s char(1) "
-               "not null default 0", argv[1]);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * delete attribute
- */
-static int com_da(__unused int fd, int argc, char *argv[])
-{
-       char *q;
-       int ret;
-
-       if (argc < 1)
-               return -E_MYSQL_SYNTAX;
-       q = make_message("alter table data drop %s", argv[1]);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/* stradd/pic_add */
-static int com_stradd_picadd(int fd, int argc, char *argv[])
-{
-       char *blob = NULL, *esc_blob = NULL, *q;
-       const char *fmt, *del_fmt;
-       int ret, stradd = strcmp(argv[0], "picadd");
-       size_t size;
-
-       if (argc < 1)
-               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'";
-       }
-       q  = make_message(del_fmt, argv[1]);
-       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;
-       PARA_DEBUG_LOG("length: %i\n", ret);
-       size = ret;
-       if (stradd)
-               blob[size] = '\0';
-       esc_blob = escape_blob(blob, ret);
-       free(blob);
-       if (!esc_blob)
-               return -E_TOOBIG;
-       q = make_message(fmt, argv[1], esc_blob);
-       free(esc_blob);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * print results to fd
- */
-static int print_results(int fd, void *result,
-               unsigned int top, unsigned int left,
-               unsigned int bottom, unsigned int 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
- */
-static int com_verb(int fd, int argc, char *argv[])
-{
-       void *result = NULL;
-       int ret;
-       unsigned int num_rows, num_fields;
-
-       if (argc < 1)
-               return -E_MYSQL_SYNTAX;
-        result = get_result(argv[1]);
-       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, 0, 0, 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, 4); /* skip Lastplayed, Numplayed... */
-       return result;
-}
-
-/*
- * list all attributes
- */
-static int com_laa(int fd, int argc, __unused char *argv[])
-{
-       void *result;
-       int ret;
-
-       if (argc)
-               return -E_MYSQL_SYNTAX;
-       result = get_all_attributes();
-       if (!result)
-               return -E_NOATTS;
-       ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0);
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * history
- */
-static int com_hist(int fd, int argc, char *argv[]) {
-       int ret;
-       void *result = NULL;
-       char *q;
-       unsigned int num_rows;
-
-       q = make_message("select name, to_days(now()) - to_days(lastplayed) from "
-               "data%s%s%s order by lastplayed",
-               (argc < 1)? "" : " where ",
-               (argc < 1)? "" : argv[1], 
-               (argc < 1)? "" : " = '1'");
-        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, 0, 0, num_rows - 1, 1);
-       mysql_free_result(result);
-       return ret;
-}
-
-/*
- * get last num audio files
- */
-static int com_last(int fd, int argc, char *argv[])
-{
-       void *result = NULL;
-       char *q;
-       int num, ret;
-
-       if (argc < 1)
-               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, 0, 0, mysql_num_rows(result) - 1, 0);
-       mysql_free_result(result);
-       return ret;
-}
-
-static int com_mbox(int fd, int argc, char *argv[])
-{
-       void *result;
-       MYSQL_ROW row;
-       int ret;
-       unsigned int num_rows, num_fields;
-       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("%s X-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 >= 1) {
-               char *tmp = make_message("%s where name LIKE '%s'", query,
-                       argv[1]);
-               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, 0, 0, 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, get_a writes a string
- * into atts of the form 'att1="0",att2="1"', which is used in com_cam
- * for contructing a mysql update query.
- * 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, ret;
-       unsigned int num_fields;
-
-       ret = -E_NOATTS;
-       result2 = get_all_attributes();
-       if (!result2)
-               goto out;
-       ret = -E_ESCAPE;
-       if (!(ebn = escaped_basename(name)))
-               goto out;
-       buf = make_message("select * from data where name='%s'", ebn);
-       free(ebn);
-       ret = -E_NORESULT;
-       result = get_result(buf);
-       free(buf);
-       if (!result)
-               goto out;
-       ret = -E_EMPTY_RESULT;
-       num_fields = mysql_num_fields(result);
-       if (num_fields < 5)
-               goto out;
-        mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
-       row = mysql_fetch_row(result);
-       ret = -E_NOROW;
-       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\"");
-        }
-       ret = 1;
-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(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 = para_strdup(streamname);
-       if (!strcmp(tmp, "(none)")) {
-               free(tmp);
-               if (filename) {
-                       char *ret, *ebn = escaped_basename(filename);
-                       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(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(macro_list, arg);
-                       if (deny_opts)
-                               deny_opts = para_strcat(deny_opts, " or ");
-                       deny_opts = para_strcat(deny_opts, tmp2);
-                       free(tmp2);
-                       continue;
-               }
-               if (!strcmp(command, "score:"))
-                       score = s_a_r_list(macro_list, arg);
-       }
-       if (!score) {
-               score = s_a_r_list(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_dbinfo(char *name)
-{
-       char *meta = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL;
-       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);
-       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);
-       if (dir)
-               free(dir);
-       if (meta)
-               free(meta);
-       if (atts)
-               free(atts);
-       if (stream)
-               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;
-}
-
-
-/* print database info */
-static int com_info(int fd, int argc, char *argv[])
-{
-       char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
-       int ret, com_la = strcmp(argv[0], "info");
-
-       if (argc < 1) {
-               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 (com_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:
-       if (meta)
-               free(meta);
-       if (atts)
-               free(atts);
-       if (dir)
-               free(dir);
-       if (name)
-               free(name);
-       return ret;
-}
-static int change_stream(char *stream)
-{
-       char *query;
-       int ret;
-       /* try to insert if it does not exist (compatibility) */
-//     query = make_message("insert into streams (name, def) values "
-//             "('current_stream', '%s')", stream);
-//     real_query(query); /* ignore return value */
-//     free(query);
-       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;
-       long unsigned 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 = atol(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 || !*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(__unused int fd, int argc, char *argv[])
-{
-       int ne = !strcmp(argv[0], "ne");
-       int i, ret;
-       if (argc < 1)
-               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;
-}
-
-/*
- * mv: rename entry
- */
-static int com_mv(__unused int fd, int argc, char *argv[])
-{
-       char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
-       int ret;
-
-       if (argc != 2)
-               return -E_MYSQL_SYNTAX;
-       ebn1 = escaped_basename(argv[1]);
-       ebn2 = escaped_basename(argv[2]);
-       dn = para_dirname(argv[2]);
-       edn = escape_str(dn);
-       free(dn);
-       ret = -E_ESCAPE;
-       if (!ebn1 || !ebn2)
-               goto out;
-       remove_entry(ebn2);
-       q = make_message("update data set name = '%s' where name = '%s'",
-               ebn2, ebn1);
-       ret = real_query(q);
-       free(q);
-       if (ret < 0)
-               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;
-       /* do not touch table dir, return success if argv[2] is no full path */
-       ret = 1;
-       if (!edn || !*edn)
-               goto out;
-       q = make_message("update dir set dir = '%s' where name = '%s'",
-               edn, ebn2);
-//     PARA_DEBUG_LOG("q: %s\n", q);
-       ret = real_query(q);
-       free(q);
-out:
-       if (ebn1)
-               free(ebn1);
-       if (ebn2)
-               free(ebn2);
-       if (edn)
-               free(edn);
-       return ret;
-
-}
-
-/*
- * picass: associate pic to audio file
- * snp: set numplayed
- */
-static int com_set(__unused int fd, int argc, char *argv[])
-{
-       char *q, *ebn;
-       long unsigned id;
-       int i, ret;
-       char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
-
-       if (argc < 2)
-               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;
-}
-
-/*
- * picch: change entry's name in pics table
- */
-static int com_picch(__unused int fd, int argc, char *argv[])
-{
-       int ret;
-       long unsigned id;
-       char *q;
-
-       if (argc != 2)
-               return -E_MYSQL_SYNTAX;
-       id = atol(argv[1]);
-       if (strlen(argv[2]) > MAXLINE)
-               return -E_NAMETOOLONG;
-       q = make_message("update pics set name = '%s' where id = %lu", argv[2], id);
-       ret = real_query(q);
-       free(q);
-       return ret;
-}
-
-/*
- * piclist: print list of pics in db
- */
-static int com_piclist(__unused int fd, int argc, __unused char *argv[])
-{
-       void *result = NULL;
-       MYSQL_ROW row;
-       unsigned long *length;
-       int ret;
-
-       if (argc)
-               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
- */
-static int com_picdel(int fd, int argc, char *argv[])
-{
-       char *q;
-       long unsigned id;
-       my_ulonglong aff;
-       int i, ret;
-
-       if (argc < 1)
-               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
- */
-static int com_pic(int fd, int argc, char *argv[])
-{
-       void *result = NULL;
-       MYSQL_ROW row;
-       unsigned long *length, id;
-       int ret;
-       char *q, *name = NULL;
-
-       if (argc < 1) {
-               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 */
-static int com_strdel(__unused int fd, int argc, char *argv[])
-{
-       char *tmp;
-       int ret = -1;
-
-       if (argc < 1)
-               return -E_MYSQL_SYNTAX;
-       tmp = make_message("delete from streams where name='%s'", argv[1]);
-       ret = real_query(tmp);
-       free(tmp);
-       if (ret < 0)
-               return ret;
-       tmp = get_current_stream();
-       ret = 1;
-       if (strcmp(tmp, "(none)") && !strcmp(tmp, argv[1]))
-               ret = change_stream("(none)");
-       return ret;
-}
-
-/*
- * ls
- */
-static int com_ls(int fd, int argc, char *argv[])
-{
-       char *q;
-       void *result;
-       int ret;
-       unsigned int num_rows;
-
-       if (argc > 0)
-               q = make_message("select name from data where name LIKE '%s'",
-                       argv[1]);
-       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, 0, 0, num_rows - 1, 0);
-       mysql_free_result(result);
-       return ret;
-}
-/*
- * summary
- */
-static int com_summary(__unused int fd, int argc, __unused char *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)
-               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(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:
-       if (ebn)
-               free(ebn);
-       return ret;
-}
-/* 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->dbinfo, info, MMD_INFO_SIZE - 1);
-       mmd->dbinfo[MMD_INFO_SIZE - 1] = '\0';
-}
-
-static void update_audio_file_server_handler(char *name)
-{
-       char *info;
-       info = get_dbinfo(name);
-       update_mmd(info);
-       free(info);
-       update_audio_file(name);
-}
-
-static int com_us(__unused int fd, int argc, char *argv[])
-{
-       if (argc != 1)
-               return -E_MYSQL_SYNTAX;
-       return update_audio_file(argv[1]);
-}
-
-static void refresh_mmd_dbinfo(void)
-{
-       char *name = get_current_audio_file();
-       char *info;
-
-       if (!name)
-               return;
-       info = get_dbinfo(name);
-       free(name);
-       mmd_lock();
-       update_mmd(info);
-       mmd_unlock();
-       free(info);
-}
-
-/* select previous/next stream */
-static int com_ps(__unused int fd, int argc, char *argv[])
-{
-       char *query, *stream = get_current_stream();
-       void *result = get_result("select name from streams");
-       MYSQL_ROW row;
-       int match = -1, ret, i;
-       unsigned int num_rows;
-
-       if (argc)
-               return -E_MYSQL_SYNTAX;
-       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)) {
-                       match = i;
-                       break;
-               }
-       }
-       ret = -E_NO_STREAM;
-       if (match < 0)
-               goto out;
-       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;
-                       i = i < 0? i + num_rows : i;
-               } 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_mmd_dbinfo();
-out:
-       free(stream);
-       if (result)
-               mysql_free_result(result);
-       return ret;
-}
-
-/* streams */
-static int com_streams(int fd, int argc, __unused char *argv[])
-{
-       unsigned int num_rows;
-       int i, ret = -E_NORESULT;
-       void *result;
-       MYSQL_ROW row;
-
-       if (argc && strcmp(argv[1], "current_stream"))
-               return -E_MYSQL_SYNTAX;
-       if (argc) {
-               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 */
-static int com_strq(int fd, int argc, char *argv[])
-{
-       MYSQL_ROW row;
-       char *query, *name;
-       void *result;
-       int ret;
-
-       if (argc < 1) {
-               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(int fd, int argc, char *argv[])
-{
-       int ret, stream_change;
-       char *query;
-       char *old_stream = get_current_stream();
-       int csp = !strcmp(argv[0], "csp");
-
-       if (!argc) {
-               ret = -E_MYSQL_SYNTAX;
-               if (csp)
-                       goto out;
-               ret = send_va_buffer(fd, "%s\n", old_stream);
-               goto out;
-       }
-       ret = -E_GET_QUERY;
-       query = get_query(argv[1], NULL, 0); /* test if stream is valid */
-       if (!query)
-               goto out;
-       free(query);
-       /* stream is ok */
-       stream_change = strcmp(argv[1], old_stream);
-       if (stream_change) {
-               ret = change_stream(argv[1]);
-               if (ret < 0)
-                       goto out;
-               refresh_mmd_dbinfo();
-       }
-       if (csp) {
-               mmd_lock();
-               mmd->new_afs_status_flags |= AFS_PLAYING;
-               if (stream_change)
-                       mmd->new_afs_status_flags |= AFS_NEXT;
-               mmd_unlock();
-       }
-       ret = 1;
-out:
-       free(old_stream);
-       return ret;
-}
-
-/*
- * sl/skip
- */
-static int com_sl(int fd, int argc, char *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 < 1)
-               return -E_MYSQL_SYNTAX;
-       num = atoi(argv[1]);
-       if (!num)
-               return -E_MYSQL_SYNTAX;
-       stream = (argc == 1)?  get_current_stream() : para_strdup(argv[2]);
-       tmp = get_query(stream, NULL, 0);
-       query = make_message("%s limit %d", tmp, num);
-       free(tmp);
-       ret = -E_GET_QUERY;
-       free(stream);
-       if (!query)
-               goto out;
-       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;
-}
-
-/*
- * update attributes of name
- */
-static int update_atts(int fd, 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
- */
-static int com_sa(int fd, int argc, char *argv[])
-{
-       int i, ret;
-       char *atts = NULL, *name;
-
-       if (argc < 1)
-               return -E_MYSQL_SYNTAX;
-       for (i = 1; i <= argc; i++) {
-               int unset = 0;
-               char *tmp, *p =argv[i];
-               int len = strlen(p);
-
-               if (!len)
-                       continue;
-               switch (p[len - 1]) {
-                       case '+':
-                               unset = 0;
-                               break;
-                       case '-':
-                               unset = 1;
-                               break;
-                       default:
-                               goto no_more_atts;
-               }
-               p[len - 1] = '\0';
-               tmp = make_message("%s%s='%s'", atts? "," : "", p,
-                       unset? "0" : "1");
-               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_mmd_dbinfo();
-out:
-       return ret;
-}
-
-/*
- * copy attributes
- */
-static int com_cam(int fd, int argc, char *argv[])
-{
-       char *name = NULL, *meta = NULL, *atts = NULL;
-       int i, ret;
-
-       if (argc < 2)
-               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(int fd, int argc, __unused char *argv[])
-{
-       char *query;
-       int ret, vrfy_mode = strcmp(argv[0], "clean");
-       void *result = NULL;
-       unsigned int num_rows;
-       MYSQL_ROW row;
-       char *escaped_name;
-
-       if (argc)
-               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 %i invalid entr%s\n", num_rows,
-                       num_rows == 1? "y" : "ies");
-               ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
-               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;
-}
-
-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
- */
-static int com_upd(int fd, int argc, __unused char *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)
-               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' 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))) {
-               ret = -E_NOROW;
-               if (!row[0])
-                       goto out;
-               send_va_buffer(fd, "new entry: %s\n", row[0]);
-               query = make_message("insert into data (name, pic_id) values "
-                       "('%s','%s')", row[0], "1");
-               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);
-       query = make_message("%s limit %d", tmp, num);
-       free(tmp);
-       if (!query)
-               goto err_out;
-       result = get_result(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 (query)
-               free(query);
-       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();
-
-       mysql_ptr = mysql_init(NULL);
-       if (!mysql_ptr) {
-               PARA_CRIT_LOG("%s", "mysql init error\n");
-               return -E_NOTCONN;
-       }
-       PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg,
-               conf.mysql_port_arg);
-       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,
-                       conf.mysql_port_arg, 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->dbinfo, "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 */
-static int com_cdb(int fd, int argc, char *argv[])
-{
-       char *query, *name;
-       int ret;
-
-       if (argc < 1)
-               name = "paraslash";
-       else {
-               ret = -E_NAMETOOLONG;
-               name = argv[1];
-               if (strlen(name) > MAXLINE)
-                       goto out;
-       }
-       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;
-       query = make_message("create database %s", name);
-       ret = real_query(query);
-       free(query);
-       if (ret < 0)
-               goto out;
-       /* reconnect with database just created */
-       mysql_close(mysql_ptr);
-       conf.mysql_database_arg = para_strdup(name);
-       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", name);
-out:
-       return ret;
-}
-
-static void shutdown_connection(void)
-{
-       if (mysql_ptr) {
-               PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
-               mysql_close(mysql_ptr);
-               mysql_ptr = NULL;
-       }
-}
-
-/**
- * the init function of the mysql-based database tool
- *
- * Check the command line options and initialize all function pointers of \a db.
- * Connect to the mysql server and initialize the dbinfo string.
- *
- * \sa struct dbtool, misc_meta_data::dbinfo, random_dbtool.c
- */
-int mysql_dbtool_init(struct dbtool *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 = 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 = 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.c b/mysql_selector.c
new file mode 100644 (file)
index 0000000..b51f4f0
--- /dev/null
@@ -0,0 +1,2540 @@
+/*
+ * Copyright (C) 1999-2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \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 "server.cmdline.h"
+#include "server.h"
+#include "afs.h"
+#include "db.h"
+#include <mysql/mysql.h>
+#include <mysql/mysql_version.h>
+#include "error.h"
+#include "net.h"
+#include "string.h"
+
+extern struct gengetopt_args_info conf;
+/** pointer to the shared memory area */
+extern struct misc_meta_data *mmd;
+
+static void *mysql_ptr = NULL;
+
+
+static int com_cam(int, int, char **);
+static int com_cdb(int, int, char **);
+static int com_cs(int, int, char **);
+static int com_da(int, int, char **);
+static int com_hist(int, int, char **);
+static int com_info(int, int, char **);
+static int com_laa(int, int, char **);
+static int com_last(int, int, char **);
+static int com_ls(int, int, char **);
+static int com_mbox(int, int, char **);
+static int com_mv(int, int, char **);
+static int com_na(int, int, char **);
+static int com_pic(int, int, char **);
+static int com_picch(int, int, char **);
+static int com_picdel(int, int, char **);
+static int com_piclist(int, int, char **);
+static int com_ps(int, int, char **);
+static int com_rm_ne(int, int, char **);
+static int com_sa(int, int, char **);
+static int com_set(int, int, char **);
+static int com_sl(int, int, char **);
+static int com_stradd_picadd(int, int, char **);
+static int com_streams(int, int, char **);
+static int com_strdel(int, int, char **);
+static int com_strq(int, int, char **);
+static int com_summary(int, int, char **);
+static int com_upd(int, int, char **);
+static int com_us(int, int, char **);
+static int com_verb(int, int, char **);
+static int com_vrfy(int, int, char **);
+
+static struct server_command cmds[] = {
+{
+.name = "cam",
+.handler = com_cam,
+.perms = DB_READ|DB_WRITE,
+.description = "copy all metadata",
+.synopsis = "cam source dest1 [dest2 ...]",
+.help =
+
+"Copy attributes and other meta data from source file to destination\n"
+"file(s). Useful for files that have been renamed.\n"
+
+},
+{
+.name = "cdb",
+.handler = com_cdb,
+.perms = DB_READ|DB_WRITE,
+.description = "create database",
+.synopsis = "cdb [name]",
+.help =
+
+"\tCreate database name containing the initial columns for basic\n"
+"\tinteroperation with server. This command has to be used only once\n"
+"\twhen you use the mysql audio file selector for the very first time.\n"
+"\n"
+"\tThe optional name defaults to 'paraslash' if not given.\n"
+
+},
+{
+.name = "clean",
+.handler = com_vrfy,
+.perms = DB_READ | DB_WRITE,
+.description = "nuke invalid entries in database",
+.synopsis = "clean",
+.help =
+
+"If the vrfy command shows you any invalid entries in your database,\n"
+"you can get rid of them with clean. Always run 'upd' and 'vrfy'\n"
+"before running this command. Use with caution!\n"
+
+},
+{
+.name = "cs",
+.handler = com_cs,
+.perms = AFS_WRITE | DB_READ | DB_WRITE,
+.description = "change stream",
+.synopsis = "cs [s]",
+.help =
+
+"Selects stream s or prints current stream when s was not given.\n"
+
+},
+{
+.name = "csp",
+.handler = com_cs,
+.perms = AFS_WRITE | DB_READ,
+.description = "change stream and play",
+.synopsis = "csp s",
+.help =
+
+"Select stream s and start playing. If this results in a stream-change,\n"
+"skip rest of current audio file.\n"
+
+},
+{
+.name = "da",
+.handler = com_da,
+.perms = DB_READ | DB_WRITE,
+.description  = "drop attribute from database",
+.synopsis = "da att",
+.help =
+
+"Use with caution. All info on attribute att will be lost.\n"
+
+},
+{
+.name = "hist",
+.handler = com_hist,
+.perms = DB_READ,
+.description = "print history",
+.synopsis = "hist",
+.help =
+
+"Print list of all audio files together with number of days since each\n"
+"file was last played.\n"
+
+},
+{
+.name = "info",
+.handler = com_info,
+.perms = DB_READ,
+.description = "print database info",
+.synopsis = "info [af]",
+.help =
+
+"print database informations for audio file af. Current audio file is\n"
+"used if af is not given.\n"
+
+},
+{
+.name = "la",
+.handler = com_info,
+.perms = DB_READ,
+.description = "list attributes",
+.synopsis = "la [af]",
+.help =
+
+"List attributes of audio file af or of current audio file when invoked\n"
+"without arguments.\n"
+
+},
+{
+.name = "laa",
+.handler = com_laa,
+.perms = DB_READ,
+.description = "list available attributes",
+.synopsis = "laa",
+.help = 
+
+"What should I say more?\n"
+
+},
+{
+.name = "last",
+.handler = com_last,
+.perms = DB_READ,
+.description = "print list of audio files, ordered by lastplayed time",
+.synopsis = "last [n]",
+.help =
+
+"The optional number n defaults to 10 if not specified.\n"
+
+},
+{
+.name = "ls",
+.handler = com_ls,
+.perms = DB_READ,
+.description = "list all audio files that match a LIKE pattern",
+.synopsis = "ls [pattern]",
+.help =
+
+"\tIf pattern was not given, print list of all audio files known\n"
+"\tto the mysql selector. See the documentation of mysql\n"
+"\tfor the definition of LIKE patterns.\n"
+
+},
+{
+.name = "mbox",
+.handler = com_mbox,
+.perms = DB_READ,
+.description = "dump audio file list in mbox format",
+.synopsis = "mbox [p]",
+.help =
+
+"\tDump list of audio files in mbox format (email) to stdout. If\n"
+"\tthe optional pattern p is given, only those audio files,\n"
+"\twhose basename match p are going to be included. Otherwise,\n"
+"\tall files are selected.\n"
+"\n"
+"EXAMPLE\n"
+"\tThe mbox command can be used together with your favorite\n"
+"\tmailer (this example uses mutt) for browsing the audio file\n"
+"\tcollection:\n"
+"\n"
+"\t\tpara_client mbox > ~/para_mbox\n"
+"\n"
+"\t\tmutt -F ~/.muttrc.para -f ~/para_mbox\n"
+"\n"
+"\tFor playlists, you can use mutt's powerful pattern matching\n"
+"\tlanguage to select files. If you like to tag all files\n"
+"\tcontaining the pattern 'foo', type 'T', then '~s foo'.\n"
+"\n"
+"\tWhen ready with the list, type ';|' (i.e., hit the semicolon\n"
+"\tkey to apply the next mutt command to all tagged messages,\n"
+"\tthen the pipe key) to pipe the selected \"mails\" to a\n"
+"\tsuitable script which adds a paraslash stream where exactly\n"
+"\tthese files are admissable or does whatever thou wilt.\n"
+
+},
+{
+.name = "mv",
+.handler = com_mv,
+.perms = DB_READ | DB_WRITE,
+.description = "rename entry in database",
+.synopsis = "mv oldname newname",
+.help =
+
+"Rename oldname to newname. This updates the data table to reflect the\n"
+"new name. All internal data (numplayed, lastplayed, picid,..) is kept.\n"
+"If newname is a full path, the dir table is updated as well.\n"
+
+},
+{
+.name = "na",
+.handler = com_na,
+.perms = DB_READ | DB_WRITE,
+.description = "add new attribute to database",
+.synopsis = "na att",
+.help =
+
+"This adds a column named att to your mysql database. att should only\n"
+"contain letters and numbers, in paricular, '+' and '-' are not allowed.\n"
+
+},
+{
+.name = "ne",
+.handler = com_rm_ne,
+.perms = DB_READ | DB_WRITE,
+.description = "add new database entries",
+.synopsis = "ne file1 [file2 [...]]",
+.help =
+
+"Add the given filename(s) to the database, where file1,... must\n"
+"be full path names. This command might be much faster than 'upd'\n"
+"if the number of given files is small.\n"
+
+},
+{
+.name = "ns",
+.handler = com_ps,
+.perms = AFS_WRITE | DB_READ | DB_WRITE,
+.description = "change to next stream",
+.synopsis = "ns",
+.help =
+
+"Cycle forwards through stream list.\n"
+
+},
+{
+.name = "pic",
+.handler = com_pic,
+.perms = DB_READ,
+.description = "get picture by name or by identifier",
+.synopsis = "pic [name]",
+.help =
+
+"\tDump jpg image that is associated to given audio file (current\n"
+"\taudio file if not specified) to stdout. If name starts with\n"
+"\t'#' it is interpreted as an identifier instead and the picture\n"
+"\thaving that identifier is dumped to stdout.\n"
+"\n"
+"EXAMPLE\n"
+"\n"
+"\tpara_client pic '#123' > pic123.jpg\n"
+
+},
+{
+.name = "picadd",
+.handler = com_stradd_picadd,
+.perms = DB_READ | DB_WRITE,
+.description = "add picture to database",
+.synopsis = "picadd [picname]",
+.help =
+
+"\tRead jpeg file from stdin and store it as picname in database.\n"
+"\n"
+"EXAMPLE\n"
+"\n"
+"\tpara_client picadd foo.jpg < foo.jpg\n"
+
+},
+{
+.name = "picass",
+.handler = com_set,
+.perms = DB_READ | DB_WRITE,
+.description = "associate a picture to file(s)",
+.synopsis = "picass pic_id file1 [file2...]",
+.help =
+
+"Associate the picture given by pic_id to all given files.\n"
+
+},
+{
+.name = "picch",
+.handler = com_picch,
+.perms = DB_READ | DB_WRITE,
+.description = "change name of picture",
+.synopsis = "picch id new_name",
+.help =
+
+"Asign new_name to picture with identifier id.\n"
+
+},
+{
+.name = "picdel",
+.handler = com_picdel,
+.perms = DB_READ | DB_WRITE,
+.description = "delete picture from database",
+.synopsis = "picdel id1 [id2...]",
+.help =
+
+"Delete each given picture from database.\n"
+
+},
+{
+.name = "piclist",
+.handler = com_piclist,
+.perms = DB_READ,
+.description = "print list of pictures",
+.synopsis = "piclist",
+.help =
+
+"Print id, name and length of each picture contained in the database.\n"
+
+},
+{
+.name = "ps",
+.handler = com_ps,
+.perms = AFS_WRITE | DB_READ | DB_WRITE,
+.description = "change to previous stream",
+.synopsis = "ps",
+.help =
+
+"Cycle backwards through stream list.\n"
+
+},
+{
+.name = "rm",
+.handler = com_rm_ne,
+.perms = DB_READ | DB_WRITE,
+.description = "remove entries from database",
+.synopsis = "rm name1 [name2 [...]]",
+.help =
+
+"Remove name1, name2, ... from the data table. Use with caution\n"
+
+},
+{
+.name = "sa",
+.handler = com_sa,
+.perms = DB_READ | DB_WRITE,
+.description = "set/unset attributes",
+.synopsis = "sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]",
+//.synopsis = "foo",
+.help =
+
+"Set ('+') or unset ('-') attribute at1, at2 etc. for given list of\n"
+"audio files. If no audio files were given the current audio file is\n"
+"used. Example:\n"
+"\n"
+"sa rock+ punk+ classic- LZ__Waldsterben.mp3\n"
+"\n"
+"sets the 'rock' and the 'punk' attribute but unsets the 'classic'\n"
+"attribute.\n"
+
+},
+{
+.name = "skip",
+.handler = com_sl,
+.perms = DB_READ | DB_WRITE,
+.description = "skip subsequent audio files(s)",
+.synopsis = "skip n [s]",
+.help =
+
+"Skip the next n audio files of stream s. This is equivalent to the\n"
+"command 'sl n s', followed by 'us name' for each name the output of sl.\n"
+
+},
+{
+.name = "sl",
+.handler = com_sl,
+.perms = DB_READ,
+.description = "print score list",
+.synopsis = "sl n [s]",
+.help =
+
+"Print sorted list of maximal n lines. Each line is an admissible entry\n"
+"with respect to stream s. The list is sorted by score-value which is\n"
+"given by the definition of s. If s is not given, the current stream\n"
+"is used. Example:\n"
+"\n"
+"      sl 1\n"
+"\n"
+"shows you the audio file the server would select right now.\n"
+
+},
+{
+.name = "snp",
+.handler = com_set,
+.perms = DB_READ | DB_WRITE,
+.description = "set numplayed",
+.synopsis = "snp number af1 [af2 ...]",
+.help =
+
+"Update the numplayed field in the data table for all given audio files.\n"
+
+},
+{
+.name = "stradd",
+.handler = com_stradd_picadd,
+.perms = DB_READ | DB_WRITE,
+.description = "add stream",
+.synopsis = "stradd s",
+.help =
+
+"Add stream s to the list of available streams. The stream definition\n"
+"for s is read from stdin and is then sent to para_server. Example:\n"
+"\n"
+"      echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna\n"
+"\n"
+"adds the new stream 'no_madonna' to the list of available streams. A given\n"
+"audio file is admissible for this stream iff its basename does not contain the\n"
+"string 'Madonna'.\n"
+
+
+},
+{
+.name = "strdel",
+.handler = com_strdel,
+.perms = DB_READ | DB_WRITE,
+.description = "delete stream",
+.synopsis = "strdel s",
+.help =
+
+"Remove stream s from database.\n"
+
+},
+{
+.name = "streams",
+.handler = com_streams,
+.perms = DB_READ,
+.description = "list streams",
+.synopsis = "streams",
+.help =
+
+"Print list of available streams. Use 'cs' to switch to any of these.\n"
+
+},
+{
+.name = "strq",
+.handler = com_strq,
+.perms = DB_READ,
+.description = "query stream definition",
+.synopsis = "strq [s]",
+.help =
+
+"Print definition of stream s to stdout. Use current stream if s was\n"
+"not given.\n"
+
+},
+{
+.name = "summary",
+.handler = com_summary,
+.perms = DB_READ,
+.description = "list attributes",
+.synopsis = "summary",
+.help =
+
+"\tPrint a list of attributes together with number of audio\n"
+"\tfiles having that attribute set.\n"
+
+},
+{
+.name = "upd",
+.handler = com_upd,
+.perms = DB_READ | DB_WRITE,
+.description = "update database",
+.synopsis = "upd",
+.help =
+
+"This command uses the --audio_file_dir option of para_server to locate\n"
+"your audio files. New files are then added to the mysql database. Use\n"
+"this command if you got new files or if you have moved some files\n"
+"around.\n"
+
+},
+{
+.name = "us",
+.handler = com_us,
+.perms = DB_READ | DB_WRITE,
+.description = "update lastplayed time",
+.synopsis = "us name",
+.help =
+
+"Update lastplayed time without actually playing the thing.\n"
+
+},
+{
+.name = "verb",
+.handler = com_verb,
+.perms = DB_READ | DB_WRITE,
+.description = "send verbatim sql query",
+.synopsis = "verb cmd",
+.help =
+
+"Send cmd to mysql server. For expert/debugging only. Note that cmd\n"
+"usually must be escaped. Use only if you know what you are doing!\n"
+
+},
+{
+.name = "vrfy",
+.handler = com_vrfy,
+.perms = DB_READ,
+.description = "list invalid entries in database",
+.synopsis = "vrfy",
+.help =
+
+"Show what clean would delete. Run 'upd' before this command to make\n"
+"sure your database is up to date.\n"
+
+},
+{
+.name = NULL,
+}
+};
+
+static struct para_macro 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,
+       }
+};
+
+static int real_query(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;
+}
+
+/*
+ * 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(char *query)
+{
+       void *result;
+
+       if (real_query(query) < 0)
+               return NULL;
+       result = mysql_store_result(mysql_ptr);
+       if (!result)
+               PARA_ERROR_LOG("%s", "store_result error\n");
+       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(char* old, int size)
+{
+       char *new;
+
+       if (!mysql_ptr || size < 0)
+               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(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
+ */
+static int com_na(__unused int fd, int argc, char *argv[])
+{
+       char *q;
+       int ret;
+
+       if (argc < 1)
+               return -E_MYSQL_SYNTAX;
+       q = make_message("alter table data add %s char(1) "
+               "not null default 0", argv[1]);
+       ret = real_query(q);
+       free(q);
+       return ret;
+}
+
+/*
+ * delete attribute
+ */
+static int com_da(__unused int fd, int argc, char *argv[])
+{
+       char *q;
+       int ret;
+
+       if (argc < 1)
+               return -E_MYSQL_SYNTAX;
+       q = make_message("alter table data drop %s", argv[1]);
+       ret = real_query(q);
+       free(q);
+       return ret;
+}
+
+/* stradd/pic_add */
+static int com_stradd_picadd(int fd, int argc, char *argv[])
+{
+       char *blob = NULL, *esc_blob = NULL, *q;
+       const char *fmt, *del_fmt;
+       int ret, stradd = strcmp(argv[0], "picadd");
+       size_t size;
+
+       if (argc < 1)
+               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'";
+       }
+       q  = make_message(del_fmt, argv[1]);
+       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;
+       PARA_DEBUG_LOG("length: %i\n", ret);
+       size = ret;
+       if (stradd)
+               blob[size] = '\0';
+       esc_blob = escape_blob(blob, ret);
+       free(blob);
+       if (!esc_blob)
+               return -E_TOOBIG;
+       q = make_message(fmt, argv[1], esc_blob);
+       free(esc_blob);
+       ret = real_query(q);
+       free(q);
+       return ret;
+}
+
+/*
+ * print results to fd
+ */
+static int print_results(int fd, void *result,
+               unsigned int top, unsigned int left,
+               unsigned int bottom, unsigned int 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
+ */
+static int com_verb(int fd, int argc, char *argv[])
+{
+       void *result = NULL;
+       int ret;
+       unsigned int num_rows, num_fields;
+
+       if (argc < 1)
+               return -E_MYSQL_SYNTAX;
+        result = get_result(argv[1]);
+       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, 0, 0, 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, 4); /* skip Lastplayed, Numplayed... */
+       return result;
+}
+
+/*
+ * list all attributes
+ */
+static int com_laa(int fd, int argc, __unused char *argv[])
+{
+       void *result;
+       int ret;
+
+       if (argc)
+               return -E_MYSQL_SYNTAX;
+       result = get_all_attributes();
+       if (!result)
+               return -E_NOATTS;
+       ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0);
+       mysql_free_result(result);
+       return ret;
+}
+
+/*
+ * history
+ */
+static int com_hist(int fd, int argc, char *argv[]) {
+       int ret;
+       void *result = NULL;
+       char *q;
+       unsigned int num_rows;
+
+       q = make_message("select name, to_days(now()) - to_days(lastplayed) from "
+               "data%s%s%s order by lastplayed",
+               (argc < 1)? "" : " where ",
+               (argc < 1)? "" : argv[1], 
+               (argc < 1)? "" : " = '1'");
+        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, 0, 0, num_rows - 1, 1);
+       mysql_free_result(result);
+       return ret;
+}
+
+/*
+ * get last num audio files
+ */
+static int com_last(int fd, int argc, char *argv[])
+{
+       void *result = NULL;
+       char *q;
+       int num, ret;
+
+       if (argc < 1)
+               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, 0, 0, mysql_num_rows(result) - 1, 0);
+       mysql_free_result(result);
+       return ret;
+}
+
+static int com_mbox(int fd, int argc, char *argv[])
+{
+       void *result;
+       MYSQL_ROW row;
+       int ret;
+       unsigned int num_rows, num_fields;
+       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("%s X-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 >= 1) {
+               char *tmp = make_message("%s where name LIKE '%s'", query,
+                       argv[1]);
+               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, 0, 0, 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, get_a writes a string
+ * into atts of the form 'att1="0",att2="1"', which is used in com_cam
+ * for contructing a mysql update query.
+ * 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, ret;
+       unsigned int num_fields;
+
+       ret = -E_NOATTS;
+       result2 = get_all_attributes();
+       if (!result2)
+               goto out;
+       ret = -E_ESCAPE;
+       if (!(ebn = escaped_basename(name)))
+               goto out;
+       buf = make_message("select * from data where name='%s'", ebn);
+       free(ebn);
+       ret = -E_NORESULT;
+       result = get_result(buf);
+       free(buf);
+       if (!result)
+               goto out;
+       ret = -E_EMPTY_RESULT;
+       num_fields = mysql_num_fields(result);
+       if (num_fields < 5)
+               goto out;
+        mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
+       row = mysql_fetch_row(result);
+       ret = -E_NOROW;
+       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\"");
+        }
+       ret = 1;
+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(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 = para_strdup(streamname);
+       if (!strcmp(tmp, "(none)")) {
+               free(tmp);
+               if (filename) {
+                       char *ret, *ebn = escaped_basename(filename);
+                       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(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(macro_list, arg);
+                       if (deny_opts)
+                               deny_opts = para_strcat(deny_opts, " or ");
+                       deny_opts = para_strcat(deny_opts, tmp2);
+                       free(tmp2);
+                       continue;
+               }
+               if (!strcmp(command, "score:"))
+                       score = s_a_r_list(macro_list, arg);
+       }
+       if (!score) {
+               score = s_a_r_list(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 = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL;
+       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);
+       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);
+       if (dir)
+               free(dir);
+       if (meta)
+               free(meta);
+       if (atts)
+               free(atts);
+       if (stream)
+               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;
+}
+
+
+/* print database info */
+static int com_info(int fd, int argc, char *argv[])
+{
+       char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
+       int ret, com_la = strcmp(argv[0], "info");
+
+       if (argc < 1) {
+               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 (com_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:
+       if (meta)
+               free(meta);
+       if (atts)
+               free(atts);
+       if (dir)
+               free(dir);
+       if (name)
+               free(name);
+       return ret;
+}
+static int change_stream(char *stream)
+{
+       char *query;
+       int ret;
+       /* try to insert if it does not exist (compatibility) */
+//     query = make_message("insert into streams (name, def) values "
+//             "('current_stream', '%s')", stream);
+//     real_query(query); /* ignore return value */
+//     free(query);
+       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;
+       long unsigned 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 = atol(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 || !*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(__unused int fd, int argc, char *argv[])
+{
+       int ne = !strcmp(argv[0], "ne");
+       int i, ret;
+       if (argc < 1)
+               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;
+}
+
+/*
+ * mv: rename entry
+ */
+static int com_mv(__unused int fd, int argc, char *argv[])
+{
+       char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
+       int ret;
+
+       if (argc != 2)
+               return -E_MYSQL_SYNTAX;
+       ebn1 = escaped_basename(argv[1]);
+       ebn2 = escaped_basename(argv[2]);
+       dn = para_dirname(argv[2]);
+       edn = escape_str(dn);
+       free(dn);
+       ret = -E_ESCAPE;
+       if (!ebn1 || !ebn2)
+               goto out;
+       remove_entry(ebn2);
+       q = make_message("update data set name = '%s' where name = '%s'",
+               ebn2, ebn1);
+       ret = real_query(q);
+       free(q);
+       if (ret < 0)
+               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;
+       /* do not touch table dir, return success if argv[2] is no full path */
+       ret = 1;
+       if (!edn || !*edn)
+               goto out;
+       q = make_message("update dir set dir = '%s' where name = '%s'",
+               edn, ebn2);
+//     PARA_DEBUG_LOG("q: %s\n", q);
+       ret = real_query(q);
+       free(q);
+out:
+       if (ebn1)
+               free(ebn1);
+       if (ebn2)
+               free(ebn2);
+       if (edn)
+               free(edn);
+       return ret;
+
+}
+
+/*
+ * picass: associate pic to audio file
+ * snp: set numplayed
+ */
+static int com_set(__unused int fd, int argc, char *argv[])
+{
+       char *q, *ebn;
+       long unsigned id;
+       int i, ret;
+       char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
+
+       if (argc < 2)
+               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;
+}
+
+/*
+ * picch: change entry's name in pics table
+ */
+static int com_picch(__unused int fd, int argc, char *argv[])
+{
+       int ret;
+       long unsigned id;
+       char *q;
+
+       if (argc != 2)
+               return -E_MYSQL_SYNTAX;
+       id = atol(argv[1]);
+       if (strlen(argv[2]) > MAXLINE)
+               return -E_NAMETOOLONG;
+       q = make_message("update pics set name = '%s' where id = %lu", argv[2], id);
+       ret = real_query(q);
+       free(q);
+       return ret;
+}
+
+/*
+ * piclist: print list of pics in db
+ */
+static int com_piclist(__unused int fd, int argc, __unused char *argv[])
+{
+       void *result = NULL;
+       MYSQL_ROW row;
+       unsigned long *length;
+       int ret;
+
+       if (argc)
+               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
+ */
+static int com_picdel(int fd, int argc, char *argv[])
+{
+       char *q;
+       long unsigned id;
+       my_ulonglong aff;
+       int i, ret;
+
+       if (argc < 1)
+               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
+ */
+static int com_pic(int fd, int argc, char *argv[])
+{
+       void *result = NULL;
+       MYSQL_ROW row;
+       unsigned long *length, id;
+       int ret;
+       char *q, *name = NULL;
+
+       if (argc < 1) {
+               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 */
+static int com_strdel(__unused int fd, int argc, char *argv[])
+{
+       char *tmp;
+       int ret = -1;
+
+       if (argc < 1)
+               return -E_MYSQL_SYNTAX;
+       tmp = make_message("delete from streams where name='%s'", argv[1]);
+       ret = real_query(tmp);
+       free(tmp);
+       if (ret < 0)
+               return ret;
+       tmp = get_current_stream();
+       ret = 1;
+       if (strcmp(tmp, "(none)") && !strcmp(tmp, argv[1]))
+               ret = change_stream("(none)");
+       return ret;
+}
+
+/*
+ * ls
+ */
+static int com_ls(int fd, int argc, char *argv[])
+{
+       char *q;
+       void *result;
+       int ret;
+       unsigned int num_rows;
+
+       if (argc > 0)
+               q = make_message("select name from data where name LIKE '%s'",
+                       argv[1]);
+       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, 0, 0, num_rows - 1, 0);
+       mysql_free_result(result);
+       return ret;
+}
+/*
+ * summary
+ */
+static int com_summary(__unused int fd, int argc, __unused char *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)
+               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(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:
+       if (ebn)
+               free(ebn);
+       return ret;
+}
+/* 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 update_audio_file_server_handler(char *name)
+{
+       char *info;
+       info = get_selector_info(name);
+       update_mmd(info);
+       free(info);
+       update_audio_file(name);
+}
+
+static int com_us(__unused int fd, int argc, char *argv[])
+{
+       if (argc != 1)
+               return -E_MYSQL_SYNTAX;
+       return update_audio_file(argv[1]);
+}
+
+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);
+}
+
+/* select previous/next stream */
+static int com_ps(__unused int fd, int argc, char *argv[])
+{
+       char *query, *stream = get_current_stream();
+       void *result = get_result("select name from streams");
+       MYSQL_ROW row;
+       int match = -1, ret, i;
+       unsigned int num_rows;
+
+       if (argc)
+               return -E_MYSQL_SYNTAX;
+       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)) {
+                       match = i;
+                       break;
+               }
+       }
+       ret = -E_NO_STREAM;
+       if (match < 0)
+               goto out;
+       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;
+                       i = i < 0? i + num_rows : i;
+               } 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;
+}
+
+/* streams */
+static int com_streams(int fd, int argc, __unused char *argv[])
+{
+       unsigned int num_rows;
+       int i, ret = -E_NORESULT;
+       void *result;
+       MYSQL_ROW row;
+
+       if (argc && strcmp(argv[1], "current_stream"))
+               return -E_MYSQL_SYNTAX;
+       if (argc) {
+               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 */
+static int com_strq(int fd, int argc, char *argv[])
+{
+       MYSQL_ROW row;
+       char *query, *name;
+       void *result;
+       int ret;
+
+       if (argc < 1) {
+               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(int fd, int argc, char *argv[])
+{
+       int ret, stream_change;
+       char *query;
+       char *old_stream = get_current_stream();
+       int csp = !strcmp(argv[0], "csp");
+
+       if (!argc) {
+               ret = -E_MYSQL_SYNTAX;
+               if (csp)
+                       goto out;
+               ret = send_va_buffer(fd, "%s\n", old_stream);
+               goto out;
+       }
+       ret = -E_GET_QUERY;
+       query = get_query(argv[1], NULL, 0); /* test if stream is valid */
+       if (!query)
+               goto out;
+       free(query);
+       /* stream is ok */
+       stream_change = strcmp(argv[1], old_stream);
+       if (stream_change) {
+               ret = change_stream(argv[1]);
+               if (ret < 0)
+                       goto out;
+               refresh_selector_info();
+       }
+       if (csp) {
+               mmd_lock();
+               mmd->new_afs_status_flags |= AFS_PLAYING;
+               if (stream_change)
+                       mmd->new_afs_status_flags |= AFS_NEXT;
+               mmd_unlock();
+       }
+       ret = 1;
+out:
+       free(old_stream);
+       return ret;
+}
+
+/*
+ * sl/skip
+ */
+static int com_sl(int fd, int argc, char *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 < 1)
+               return -E_MYSQL_SYNTAX;
+       num = atoi(argv[1]);
+       if (!num)
+               return -E_MYSQL_SYNTAX;
+       stream = (argc == 1)?  get_current_stream() : para_strdup(argv[2]);
+       tmp = get_query(stream, NULL, 0);
+       query = make_message("%s limit %d", tmp, num);
+       free(tmp);
+       ret = -E_GET_QUERY;
+       free(stream);
+       if (!query)
+               goto out;
+       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;
+}
+
+/*
+ * update attributes of name
+ */
+static int update_atts(int fd, 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
+ */
+static int com_sa(int fd, int argc, char *argv[])
+{
+       int i, ret;
+       char *atts = NULL, *name;
+
+       if (argc < 1)
+               return -E_MYSQL_SYNTAX;
+       for (i = 1; i <= argc; i++) {
+               int unset = 0;
+               char *tmp, *p =argv[i];
+               int len = strlen(p);
+
+               if (!len)
+                       continue;
+               switch (p[len - 1]) {
+                       case '+':
+                               unset = 0;
+                               break;
+                       case '-':
+                               unset = 1;
+                               break;
+                       default:
+                               goto no_more_atts;
+               }
+               p[len - 1] = '\0';
+               tmp = make_message("%s%s='%s'", atts? "," : "", p,
+                       unset? "0" : "1");
+               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:
+       return ret;
+}
+
+/*
+ * copy attributes
+ */
+static int com_cam(int fd, int argc, char *argv[])
+{
+       char *name = NULL, *meta = NULL, *atts = NULL;
+       int i, ret;
+
+       if (argc < 2)
+               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(int fd, int argc, __unused char *argv[])
+{
+       char *query;
+       int ret, vrfy_mode = strcmp(argv[0], "clean");
+       void *result = NULL;
+       unsigned int num_rows;
+       MYSQL_ROW row;
+       char *escaped_name;
+
+       if (argc)
+               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 %i invalid entr%s\n", num_rows,
+                       num_rows == 1? "y" : "ies");
+               ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
+               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;
+}
+
+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
+ */
+static int com_upd(int fd, int argc, __unused char *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)
+               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' 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))) {
+               ret = -E_NOROW;
+               if (!row[0])
+                       goto out;
+               send_va_buffer(fd, "new entry: %s\n", row[0]);
+               query = make_message("insert into data (name, pic_id) values "
+                       "('%s','%s')", row[0], "1");
+               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);
+       query = make_message("%s limit %d", tmp, num);
+       free(tmp);
+       if (!query)
+               goto err_out;
+       result = get_result(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 (query)
+               free(query);
+       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();
+
+       mysql_ptr = mysql_init(NULL);
+       if (!mysql_ptr) {
+               PARA_CRIT_LOG("%s", "mysql init error\n");
+               return -E_NOTCONN;
+       }
+       PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg,
+               conf.mysql_port_arg);
+       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,
+                       conf.mysql_port_arg, 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 */
+static int com_cdb(int fd, int argc, char *argv[])
+{
+       char *query, *name;
+       int ret;
+
+       if (argc < 1)
+               name = "paraslash";
+       else {
+               ret = -E_NAMETOOLONG;
+               name = argv[1];
+               if (strlen(name) > MAXLINE)
+                       goto out;
+       }
+       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;
+       query = make_message("create database %s", name);
+       ret = real_query(query);
+       free(query);
+       if (ret < 0)
+               goto out;
+       /* reconnect with database just created */
+       mysql_close(mysql_ptr);
+       conf.mysql_database_arg = para_strdup(name);
+       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", name);
+out:
+       return ret;
+}
+
+static void shutdown_connection(void)
+{
+       if (mysql_ptr) {
+               PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
+               mysql_close(mysql_ptr);
+               mysql_ptr = NULL;
+       }
+}
+
+/**
+ * the init function of the mysql-based audio file selector
+ *
+ * Check the command line options and initialize all function pointers of \a db.
+ * Connect to the mysql server and initialize the info string.
+ *
+ * \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 = 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 = 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/ogg.c b/ogg.c
index 0c7b20ea47a8350860c19fe2de7c3eba8b4d0e9f..b38a8f4979a6dfccf4578f314b4914536546b05a 100644 (file)
--- a/ogg.c
+++ b/ogg.c
@@ -35,7 +35,7 @@ static OggVorbis_File *oggvorbis_file;
 static FILE *infile;
 static int header_len, oggbuf_len, vi_channels;
 static char *header, *oggbuf;
-static ogg_int64_t *chunk_table, max_chunk_len;
+static ssize_t *chunk_table, max_chunk_len;
 struct audio_format *af;
 static long vi_sampling_rate, vi_bitrate, vi_bitrate_nominal,
        num_chunks;
@@ -152,7 +152,7 @@ static void tunetable(void)
 static void ogg_compute_chunk_table(double time_total)
 {
        int i, ret, num;
-       ogg_int64_t pos = 0, min = 0, old_pos;
+       ssize_t pos = 0, min = 0, old_pos;
 
        old_pos = 0;
        ret = 0;
@@ -174,15 +174,15 @@ static void ogg_compute_chunk_table(double time_total)
                min = (i == 1)? diff : MIN(min, diff);
                chunk_table[i] = pos;
                if (i < 11 || !((i - 1) % 1000)|| i > num - 11)
-                       PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %lli, "
-                               "size: %lli\n", i - 1,
+                       PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %zd, "
+                               "size: %zd\n", i - 1,
                                i * chunk_time, pos, pos - old_pos);
                old_pos = pos;
        }
        num_chunks = i - 1;
        chunk_table[i] = pos;
        tunetable();
-       PARA_INFO_LOG("%li chunks (%fs), max chunk: %lli, min chunk: %lli\n",
+       PARA_INFO_LOG("%li chunks (%fs), max chunk: %zd, min chunk: %zd\n",
                num_chunks, chunk_time, max_chunk_len, min);
        rewind(infile);
 }
diff --git a/para.h b/para.h
index f3e67ed9c1213b13c3a486767564799421e089e4..d495139ea095ebdeea9abac7a773c053adc22f2e 100644 (file)
--- a/para.h
+++ b/para.h
@@ -156,9 +156,9 @@ enum {
        SI_DBINFO3,             SI_DECODER_FLAGS,       SI_AUDIOD_STATUS,
        SI_PLAY_TIME,           SI_UPTIME,              SI_OFFSET,
        SI_LENGTH,              SI_STREAM_START,        SI_CURRENT_TIME,
-       SI_AUDIOD_UPTIME,       SI_DBTOOL,
+       SI_AUDIOD_UPTIME,       SI_SELECTOR,            NUM_STAT_ITEMS
 };
-#define NUM_STAT_ITEMS (SI_DBTOOL + 1)
+
 int stat_line_valid(const char *);
 void stat_client_write(char *msg);
 int stat_client_add(int);
diff --git a/playlist_selector.c b/playlist_selector.c
new file mode 100644 (file)
index 0000000..346f1ce
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file playlist_selector.c The playlist audio file selector of paraslash  */
+
+#include "server.h"
+#include "db.h"
+#include "error.h"
+#include "net.h"
+#include "string.h"
+#include "ipc.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;
+
+static int com_ppl(int, int, char **);
+static int com_lpl(int, int, char **);
+extern struct misc_meta_data *mmd;
+
+/* array of supported commands */
+static struct server_command cmds[] = {
+{
+.name = "ppl",
+.handler = com_ppl,
+.perms = DB_READ,
+.description = "print playlist",
+.synopsis = "ppl",
+.help =
+"Print out the current playlist"
+}, {
+.name = "lpl",
+.handler = com_lpl,
+.perms = DB_WRITE,
+.description = "load playlist",
+.synopsis = "lpl",
+.help =
+"Read a new playlist from stdin. Example:\n"
+"\tfind /audio -name '*.mp3' | para_client lpl"
+}, {
+.name = NULL,
+}
+};
+
+static void playlist_add(char *path)
+{
+       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);
+}
+
+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 (%d 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;
+}
+
+static int com_lpl(int fd, __unused int argc, __unused char *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;
+}
+
+static int com_ppl(int fd, __unused int argc, __unused char *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 = 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) % 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(__unused fd_set *rfds, __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 (%d bytes)\n", pcd->size);
+       ret = for_each_line((char *)shm, pcd->size, &playlist_add);
+       shm_detach(shm);
+       PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
+       pcd->retval = 1;
+       pcd->size = 0;
+       mutex_unlock(pcd->mutex);
+out:
+       mutex_unlock(ppd->server_mutex);
+}
+
+void pls_update_audio_file(char *audio_file)
+{
+       unsigned i;
+
+       for (i = 0; i < playlist_len; i++) {
+               unsigned j = (current_playlist_entry + i) % playlist_len;
+               if (strcmp(playlist[j], audio_file))
+                       continue;
+               current_playlist_entry = (j + 1) % playlist_len;
+       }
+}
+
+/**
+ * the init function for the playlist selector
+ *
+ * Init all function pointers of \a db
+ *
+ * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
+ * random_selector.c.
+ */
+int playlist_selector_init(struct audio_file_selector *db)
+{
+       int ret;
+       struct private_pls_data *ppd = NULL;
+       void *shm = NULL;
+
+       self = db;
+       db->cmd_list = cmds;
+       db->get_audio_file_list = pls_get_audio_file_list;
+       db->shutdown = pls_shutdown;
+       db->post_select = pls_post_select;
+       db->update_audio_file = pls_update_audio_file;
+       ppd = para_calloc(sizeof(struct private_pls_data));
+       db->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, "playlist selector initialized");
+       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/plm_dbtool.c b/plm_dbtool.c
deleted file mode 100644 (file)
index ad6a95b..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, write to the Free Software
- *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-/** \file plm_dbtool.c Playlist manager for paraslash  */
-
-#include "server.h"
-#include "db.h"
-#include "error.h"
-#include "net.h"
-#include "string.h"
-#include "ipc.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 plm_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 plm database tool */
-struct private_plm_data {
-/** guards against concurrent client access */
-       int client_mutex;
-/** guards against concurrent parent-child access */
-       int server_mutex;
-/** pointer to the client data */
-       struct plm_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 dbtool *self;
-
-static int com_ppl(int, int, char **);
-static int com_lpl(int, int, char **);
-extern struct misc_meta_data *mmd;
-
-/* array of commands that are supported by this database tool */
-static struct server_command cmds[] = {
-{
-.name = "ppl",
-.handler = com_ppl,
-.perms = DB_READ,
-.description = "print playlist",
-.synopsis = "ppl",
-.help =
-"Print out the current playlist"
-}, {
-.name = "lpl",
-.handler = com_lpl,
-.perms = DB_WRITE,
-.description = "load playlist",
-.synopsis = "lpl",
-.help =
-"Read a new playlist from stdin. Example:\n"
-"\tfind /audio -name '*.mp3' | para_client lpl"
-}, {
-.name = NULL,
-}
-};
-
-static void playlist_add(char *path)
-{
-       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);
-}
-
-static int send_playlist_to_server(const char *buf, size_t size)
-{
-       struct private_plm_data *ppd = self->private_data;
-       int ret, shm_mutex = -1, shm_id = -1;
-       void *shm = NULL;
-
-       PARA_DEBUG_LOG("new playlist (%d 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;
-}
-
-static int com_lpl(int fd, __unused int argc, __unused char *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;
-}
-
-static int com_ppl(int fd, __unused int argc, __unused char *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 **plm_get_audio_file_list(unsigned int num)
-{
-       char **file_list;
-       unsigned i;
-
-       num = 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) % 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 plm_shutdown(void)
-{
-       struct private_plm_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 plm_post_select(__unused fd_set *rfds, __unused fd_set *wfds)
-{
-       struct private_plm_data *ppd = self->private_data;
-       struct plm_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 (%d bytes)\n", pcd->size);
-       ret = for_each_line((char *)shm, pcd->size, &playlist_add);
-       shm_detach(shm);
-       PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
-       pcd->retval = 1;
-       pcd->size = 0;
-       mutex_unlock(pcd->mutex);
-out:
-       mutex_unlock(ppd->server_mutex);
-}
-
-void plm_update_audio_file(char *audio_file)
-{
-       unsigned i;
-
-       for (i = 0; i < playlist_len; i++) {
-               unsigned j = (current_playlist_entry + i) % playlist_len;
-               if (strcmp(playlist[j], audio_file))
-                       continue;
-               current_playlist_entry = (j + 1) % playlist_len;
-       }
-}
-
-/**
- * the init function for the plm database tool
- *
- * Init all function pointers of \a db
- *
- * \sa struct dbtool, misc_meta_data::dbinfo, mysql.c random_dbtool.c
- */
-int plm_dbtool_init(struct dbtool *db)
-{
-       int ret;
-       struct private_plm_data *ppd = NULL;
-       void *shm = NULL;
-
-       self = db;
-       db->cmd_list = cmds;
-       db->get_audio_file_list = plm_get_audio_file_list;
-       db->shutdown = plm_shutdown;
-       db->post_select = plm_post_select;
-       db->update_audio_file = plm_update_audio_file;
-       ppd = para_calloc(sizeof(struct private_plm_data));
-       db->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 plm_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->dbinfo, "plm initialized");
-       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/random_dbtool.c b/random_dbtool.c
deleted file mode 100644 (file)
index 26a6b10..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2004-2006 Andre Noll <maan@systemlinux.org>
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, write to the Free Software
- *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-/** \file random_dbtool.c Simple database tool implementation. Feel free to modify.  */
-
-#include <sys/time.h> /* gettimeofday */
-#include "server.cmdline.h"
-#include "server.h"
-#include "db.h"
-#include "error.h"
-#include "net.h"
-#include "string.h"
-
-static int com_random_info(int, int, char **);
-extern struct gengetopt_args_info conf;
-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(__unused const char *dir, __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)
-               return -E_FILE_COUNT;
-       audio_file_list[audio_file_count] = make_message("%s/%s", dir, name);
-       audio_file_count++;
-       return 1;
-}
-
-/* array of commands that are supported by this database tool */
-static struct server_command cmds[] = {
-{
-.name = "random_info",
-.handler = com_random_info,
-.perms = 0,
-.description = "about the random database tool",
-.synopsis = "random_info",
-.help =
-
-"Select a random file under the given directory"
-}, {
-.name = NULL,
-}
-};
-
-static int com_random_info(int fd, __unused int argc, __unused char *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_dbtool_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_dbtool_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 = 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]);
-       }
-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)
-//             PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
-       return ret_list;
-}
-
-static void random_shutdown(void)
-{
-       PARA_DEBUG_LOG("%s", "thanks for using another dbtool.\n");
-}
-
-/** random's (constant) database info text */
-#define DBINFO "dbinfo1:database info? You're kidding. I'm still dopey!\ndbinfo2:\ndbinfo3:\n"
-
-/**
- *  the init function for the random database tool
- *
- * Init all function pointers of \a db, init the dbinfo text and seed the
- * PRNG.
- *
- * \sa struct dbtool, misc_meta_data::dbinfo, mysql.c
- */
-int random_dbtool_init(struct dbtool *db)
-{
-       struct timeval now;
-
-       PARA_INFO_LOG("%s", "registering random handlers ;)\n");
-       sprintf(mmd->dbinfo, DBINFO);
-       gettimeofday(&now, NULL);
-       srand(now.tv_usec);
-       db->cmd_list = cmds;
-       db->get_audio_file_list = random_get_audio_file_list;
-       db->shutdown = random_shutdown;
-       return 1;
-}
diff --git a/random_selector.c b/random_selector.c
new file mode 100644 (file)
index 0000000..bdb8268
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2004-2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file random_selector.c An audio file selector which chooses files by random */
+
+#include <sys/time.h> /* gettimeofday */
+#include "server.cmdline.h"
+#include "server.h"
+#include "db.h"
+#include "error.h"
+#include "net.h"
+#include "string.h"
+
+static int com_random_info(int, int, char **);
+extern struct gengetopt_args_info conf;
+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(__unused const char *dir, __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)
+               return -E_FILE_COUNT;
+       audio_file_list[audio_file_count] = make_message("%s/%s", dir, name);
+       audio_file_count++;
+       return 1;
+}
+
+/* array of commands that are supported by this selector */
+static struct server_command cmds[] = {
+{
+.name = "random_info",
+.handler = com_random_info,
+.perms = 0,
+.description = "about the random audio file selector",
+.synopsis = "random_info",
+.help =
+
+"Select a random file under the given directory"
+}, {
+.name = NULL,
+}
+};
+
+static int com_random_info(int fd, __unused int argc, __unused char *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 = 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]);
+       }
+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)
+//             PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
+       return ret_list;
+}
+
+static void random_shutdown(void)
+{
+}
+
+/**
+ *  the init function for the random audio file selector
+ *
+ * Init all function pointers of \a db, 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 *db)
+{
+       struct timeval now;
+
+       PARA_INFO_LOG("%s", "registering random handlers ;)\n");
+       sprintf(mmd->selector_info, "dbinfo1:database info? You're kidding. "
+               "I'm still dopey!\ndbinfo2:\ndbinfo3:\n");
+       gettimeofday(&now, NULL);
+       srand(now.tv_usec);
+       db->cmd_list = cmds;
+       db->get_audio_file_list = random_get_audio_file_list;
+       db->shutdown = random_shutdown;
+       return 1;
+}
index 003aa7b0304aae4d07f0ed4ed834fe469f7af0c2..6a51ec1651c3cbc4aeaeb6bc688898682f32d12e 100755 (executable)
@@ -6,15 +6,15 @@ dir="$HOME/.paraslash"
 client_conf="$dir/client.conf"
 audioc_conf="$dir/audioc.conf"
 server=www.paraslash.org
-proj=paraslash-0.2.10
+proj=paraslash-0.2.11
 df=$proj.tar.bz2 # download file
 url=http://$server/versions/$df
 kf="$dir/key.anonymous" # key file
 key_url=http://$server/key.anonymous
 socket="$dir/socket"
-receiver_opts="mp3:http:-i:$server:-p:8009"
+receiver_opts="mp3:http -i $server -p 8009"
 audiod_log="$dir/audiod.log"
-audiod_opts="-FDdr $receiver_opts -L $audiod_log -s $socket"
+audiod_opts="-FDd -L $audiod_log -s $socket"
 msg()
 {
        echo "`date`: $1"
@@ -46,8 +46,8 @@ EOF
        msg "writing $audioc_conf"
        echo "socket \"$socket\"" > "$audioc_conf"
        (para_audioc term; killall para_audiod para_client) >> "$log" 2>&1
-       msg "para_audiod $audiod_opts"
-       para_audiod $audiod_opts -w "mp3:mpg123 -"
+       msg "para_audiod $audiod_opts -r '$receiver_opts'"
+       para_audiod $audiod_opts -r "$receiver_opts" -w "mp3:mpg123 -"
        echo "hit return to start para_gui, hit ctrl+c to abort"
        read
        para_gui
index bdc809b59d58e0b33797eabc11fc9439b99636af..81acd545ff4a5be97a5b0d9afba1e96816d86f5b 100644 (file)
--- a/sdl_gui.c
+++ b/sdl_gui.c
@@ -211,18 +211,18 @@ static void init_stat_items(void)
        s[SI_UPTIME].font = N_YELLOW;
        s[SI_UPTIME].align = RIGHT;
 
-       s[SI_DBTOOL].prefix = "dbtool: ";
-       s[SI_DBTOOL].postfix = "";
-       s[SI_DBTOOL].content = "no content yet";
-       s[SI_DBTOOL].x = 35;
-       s[SI_DBTOOL].y = 48;
-       s[SI_DBTOOL].w = 35;
-       s[SI_DBTOOL].h = FONT_HEIGHT;
-       s[SI_DBTOOL].r = 0;
-       s[SI_DBTOOL].g = 0;
-       s[SI_DBTOOL].b = 0;
-       s[SI_DBTOOL].font = N_YELLOW;
-       s[SI_DBTOOL].align = LEFT;
+       s[SI_SELECTOR].prefix = "selector: ";
+       s[SI_SELECTOR].postfix = "";
+       s[SI_SELECTOR].content = "no content yet";
+       s[SI_SELECTOR].x = 35;
+       s[SI_SELECTOR].y = 48;
+       s[SI_SELECTOR].w = 35;
+       s[SI_SELECTOR].h = FONT_HEIGHT;
+       s[SI_SELECTOR].r = 0;
+       s[SI_SELECTOR].g = 0;
+       s[SI_SELECTOR].b = 0;
+       s[SI_SELECTOR].font = N_YELLOW;
+       s[SI_SELECTOR].align = LEFT;
 
        s[SI_FORMAT].prefix = "Format: ";
        s[SI_FORMAT].postfix = "";
index 67d9bce4e5aea8822f5c4462af37fa2fb36c6ec1..c2ede7aa92566337c8d5644d1913da5b247ff512 100644 (file)
--- a/server.c
+++ b/server.c
@@ -21,8 +21,9 @@
 
 /** \mainpage Paraslash API Reference
  *
- *  Good starting points for reading are probably \ref dbtool, \ref sender,
- *  \ref receiver, \ref receiver_node, \ref filter, \ref filter_node.
+ *  Good starting points for reading are probably \ref audio_file_selector,
+ *  \ref sender, \ref receiver, \ref receiver_node, \ref filter, \ref
+ *  filter_node.
  *
  */
 
@@ -47,11 +48,11 @@ INIT_SERVER_ERRLISTS;
 /** shut down non-authorized connections after that many seconds */
 #define ALARM_TIMEOUT 10
 
-/* these are exported to afs/command/dbtool */
+/* these are exported to afs.c. command.c and to all selectors */
 struct misc_meta_data *mmd;
 /** the configuration of para_server
  *
- * It also contains the options for all database tools and all supported
+ * It also contains the options for all audio file selectors and all supported
  * senders.
 */
 struct gengetopt_args_info conf;
@@ -61,16 +62,16 @@ extern void http_send_init(struct sender *);
 extern void ortp_send_init(struct sender *);
 extern struct audio_format afl[];
 
-/** the list of supported database tools */
-struct dbtool dblist[] = {
+/** the list of supported audio file selectors */
+struct audio_file_selector dblist[] = {
        {
                .name = "random",
-               .init = random_dbtool_init,
+               .init = random_selector_init,
                .update_audio_file = NULL,
        },
        {
-               .name = "plm",
-               .init = plm_dbtool_init,
+               .name = "playlist",
+               .init = playlist_selector_init,
                .update_audio_file = NULL,
                .pre_select = NULL,
                .post_select = NULL,
@@ -78,7 +79,7 @@ struct dbtool dblist[] = {
 #ifdef HAVE_MYSQL
        {
                .name = "mysql",
-               .init = mysql_dbtool_init,
+               .init = mysql_selector_init,
                .update_audio_file = NULL,
                .pre_select = NULL,
                .post_select = NULL,
@@ -178,7 +179,7 @@ static void shm_init(void)
                goto err_out;
        mmd_mutex = ret;
 
-       mmd->dbt_num = 0;
+       mmd->selector_num = 0;
        mmd->num_played = 0;
        mmd->num_commands = 0;
        mmd->events = 0;
@@ -281,29 +282,29 @@ static void setup_signal_handling(void)
        }
 }
 
-static void init_dbtool(void)
+static void init_selector(void)
 {
        int i, ret;
 
-       mmd->dbt_change = -1; /* no change nec., set to new dbt num by com_cdt */
-       if (!conf.dbtool_given)
+       mmd->selector_change = -1; /* no change nec., set to new num by com_chs */
+       if (!conf.selector_given)
                goto random;
        for (i = 0; dblist[i].name; i++) {
-               if (strcmp(dblist[i].name, conf.dbtool_arg))
+               if (strcmp(dblist[i].name, conf.selector_arg))
                        continue;
-               PARA_NOTICE_LOG("initializing %s database tool\n",
+               PARA_NOTICE_LOG("initializing %s audio file selector\n",
                        dblist[i].name);
                ret = dblist[i].init(&dblist[i]);
                if (ret < 0) {
                        PARA_WARNING_LOG("%s", PARA_STRERROR(-ret));
                        break;
                }
-               mmd->dbt_num = i;
+               mmd->selector_num = i;
                return;
        }
-       PARA_WARNING_LOG("%s", "falling back to the random dbtool\n");
+       PARA_WARNING_LOG("%s", "falling back to the random selector\n");
 random:
-       mmd->dbt_num = 0;
+       mmd->selector_num = 0;
        dblist[0].init(&dblist[0]); /* always successful */
 }
 
@@ -356,7 +357,7 @@ static unsigned do_inits(int argc, char **argv)
        /* become daemon */
        if (conf.daemon_given)
                daemon_init();
-       init_dbtool();
+       init_selector();
        PARA_NOTICE_LOG("%s", "initializing audio file sender\n");
        /* audio file sender */
        afs_init();
@@ -374,21 +375,21 @@ static unsigned do_inits(int argc, char **argv)
        return sockfd;
 }
 
-static void handle_dbt_change(void)
+static void change_selector(void)
 {
-       int ret, old = mmd->dbt_num, new = mmd->dbt_change;
+       int ret, old = mmd->selector_num, new = mmd->selector_change;
 
        dblist[old].shutdown();
        ret = dblist[new].init(&dblist[new]);
-       mmd->dbt_change = -1; /* reset */
+       mmd->selector_change = -1; /* reset */
        if (ret >= 0) {
-               mmd->dbt_num = new;
+               mmd->selector_num = new;
                return;
        }
        /* init failed */
-       PARA_ERROR_LOG("%s -- switching to the random dbtool\n", PARA_STRERROR(-ret));
+       PARA_ERROR_LOG("%s -- switching to the random selector\n", PARA_STRERROR(-ret));
        dblist[0].init(&dblist[0]);
-       mmd->dbt_num = 0;
+       mmd->selector_num = 0;
 }
 
 /*
@@ -400,8 +401,8 @@ static void handle_sighup(void)
        close_log(logfile); /* gets reopened if necessary by parse_config */
        logfile = NULL;
        parse_config(1); /* reopens log */
-       mmd->dbt_change = mmd->dbt_num; /* do not change dbtool */
-       handle_dbt_change(); /* force reloading dbtool */
+       mmd->selector_change = mmd->selector_num; /* do not change selector.. */
+       change_selector(); /* .. just reload */
 }
 
 static void status_refresh(void)
@@ -463,8 +464,8 @@ repeat:
                        &max_fileno,
                        &rfds, &wfds);
        }
-       if (dblist[mmd->dbt_num].pre_select) {
-               ret = dblist[mmd->dbt_num].pre_select(&rfds, &wfds);
+       if (dblist[mmd->selector_num].pre_select) {
+               ret = dblist[mmd->selector_num].pre_select(&rfds, &wfds);
                max_fileno = MAX(max_fileno, ret);
        }
        mmd_unlock();
@@ -473,10 +474,10 @@ repeat:
        err = errno;
        //PARA_DEBUG_LOG("%s: select returned  %i\n", __func__, ret);
        mmd_lock();
-       if (mmd->dbt_change >= 0)
-               handle_dbt_change();
-       if (dblist[mmd->dbt_num].post_select)
-               dblist[mmd->dbt_num].post_select(&rfds, &wfds);
+       if (mmd->selector_change >= 0)
+               change_selector();
+       if (dblist[mmd->selector_num].post_select)
+               dblist[mmd->selector_num].post_select(&rfds, &wfds);
        if (ret < 0 && err == EINTR)
                goto repeat;
        if (ret < 0) {
@@ -511,7 +512,7 @@ repeat:
                case SIGTERM:
                        PARA_EMERG_LOG("terminating on signal %d\n", sig);
                        kill(0, SIGTERM);
-                       dblist[mmd->dbt_num].shutdown();
+                       dblist[mmd->selector_num].shutdown();
                        mutex_destroy(mmd_mutex);
                        shm_detach(mmd);
                        shm_destroy(mmd_shm_id);
index 418b2b4aaaa7bbe2cb38d2bc378e3921c2a2f441..0cf4bb625bcf56b4bf53c19558044c4137252055 100644 (file)
@@ -9,12 +9,12 @@ option "logfile" L "(default=stdout/stderr)" string typestr="filename" no
 option "config_file" c "(default='~/.paraslash/server.conf'" string typestr="filename" no
 option "user_list" - "(default='~/.paraslash/server.users')" string typestr="filename" no
 
-section "Options concerning the audio file sender"
+section "audio file sender"
 option "autoplay" a "start playing on startup" flag off
 option "announce_time" A "Delay betweeen announcing the stream and sending data" int typestr="milliseconds" default="300" no
-option "dbtool" D "(default=first available that works)" string typestr="name_of_dbtool" no
+option "selector" S "(default=random)" string typestr="name" no
 
-section "Mysql database tool options"
+section "mysql selector:"
 option "mysql_host" - "mysql server" string default="localhost" no
 option "mysql_port" - "where mysql is listening" int default="3306" no
 option "mysql_user" - "default value: username from /etc/passwd" string no
@@ -23,22 +23,20 @@ option "mysql_database" - "name of mysql database" string default="paraslash" no
 option "mysql_audio_file_dir"  - "dir to search for audio files (required)" string no
 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()))" no
 
+section "random selector"
+option "random_dir" - "dir to search for audio files" string default="/home/music" no
 
-
-section "Random database tool options"
-option "random_dbtool_dir" - "dir to search for files to be streamed" string default="/home/music" no
-
-section "Http sender options"
+section "http sender"
 option "http_port" - "tcp port for http streaming" int typestr="portnumber" default="8000" no
 option "http_default_deny" - "deny connections from hosts which are not explicitly allowed" flag off
 option "http_access" - "Add given host/network to access control list (whitelist if http_default_deny was given, blacklist otherwise) before opening the tcp port. This option can be given multiple times. Example: '192.168.0.0/24' whitelists/blacklists the 256 hosts 192.168.0.x" string typestr="a.b.c.d/n" no multiple
 option "http_no_autostart" - "do not open tcp port on server startup" flag off
 option "http_max_clients" - "maximal simultaneous connections, non-positive value means unlimited" int typestr="number" default="-1" no
 
-section "Dccp sender options"
+section "dccp sender"
 option "dccp_port" - "port for http streaming" int typestr="portnumber" default="5001" no
 
-section "Ortp sender options"
+section "ortp sender"
 option "ortp_target" - "Add given host/port to the list of targets. This option can be given multiple times. Example: '224.0.1.38:1500' instructs the ortp sender to send to udp port 1500 on host 224.0.1.38 (unassigned ip in the Local Network Control Block 224.0.0/24). This is useful for LAN-streaming." string typestr="a.b.c.d:p" no multiple
 option "ortp_no_autostart" - "do not start to send automatically" flag off
 option "ortp_default_port" - "default udp port if not specified" int typestr="portnumber" default="1500" no
index fe6d858199c97a5a27ed586b76a6c8078840ad63..fcda7a3ace835d65c0e960640291396e62f628ea 100644 (file)
--- a/server.h
+++ b/server.h
@@ -21,7 +21,7 @@
 #include "para.h"
 
 
-/** size of the dbinfo and audio_file info strings of struct misc_meta_data */
+/** size of the selector_info and audio_file info strings of struct misc_meta_data */
 #define MMD_INFO_SIZE 16384
 
 /**
@@ -139,12 +139,12 @@ struct misc_meta_data{
        unsigned int active_connections;
 /** the process id of para_server */
        pid_t server_pid;
-/** a string that gets filled in by the current database tool */
-       char dbinfo[MMD_INFO_SIZE];
-/** the number if the current database tool */
-       int dbt_num;
-/** commands set this to non-zero to request a database tool change */
-       int dbt_change;
+/** a string that gets filled in by the current audio file selector */
+       char selector_info[MMD_INFO_SIZE];
+/** the number if the current audio file selector */
+       int selector_num;
+/** commands set this to non-zero to change the current selector */
+       int selector_change;
 /** used by the sender command */
        struct sender_command_data sender_cmd_data;
 };
index 558f55bde07e4f5740fa128e32838ef73c42e2d8..d0bb6d8df8922d6a458d56f80e6b54775ded9bab 100644 (file)
--- a/slider.c
+++ b/slider.c
@@ -28,7 +28,7 @@
 #define VAL_2_SL_VAL(v) (v) * (SLIDER_RATIO - 1.0) / SLIDER_RATIO
 #define SL_VAL_2_VAL(v) (v) * SLIDER_RATIO / (SLIDER_RATIO - 1.0)
 #define VAL_2_SCORE(v) (v) > 0.5? 1 / (1.03 - (v)) / (1.03 - (v)) :\
-        - 1 / ((v) + 0.03) / ((v) + 0.03)
+       - 1 / ((v) + 0.03) / ((v) + 0.03)
 
 #define RGB(R,G,B) (((R)<<12) + ((G)<<6) + (B))
 
 #define NUMPLAYED_FORMULA(v)\
        10 * (v) + (v) / (1 - (v) * (1 - EPSILON))
 
-
 static int argc;
 static char **argv;
 char *streamname = NULL;
 static Zmw_Float_0_1 *slider_vals, lastplayed_val, numplayed_val;
 
-#if 0
-static void boxed_text(const char *text, int i)
-{
-  ZMW(zmw_decorator(Zmw_Decorator_Border_Embossed))
-    {
-      if (i == 2)
-        {
-          zmw_horizontal_expand(Zmw_False) ;
-          zmw_vertical_expand(Zmw_False) ;
-        }
-      zmw_label(text) ;
-    }
-}
-#endif
-
 void para_log(int ll, char* fmt,...) /* no logging */
 {
 }
diff --git a/stat.c b/stat.c
index 4ad1b5a88d957f411c5ccad6bb00f871e440f842..4259956f6ed5724d5afac014c170a1426b2e56db 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -91,7 +91,7 @@ const char *status_item_list[NUM_STAT_ITEMS] = {
        [SI_CURRENT_TIME] = "current_time",
 
        [SI_AUDIOD_UPTIME] = "audiod_uptime",
-       [SI_DBTOOL] = "dbtool"
+       [SI_SELECTOR] = "dbtool"
 };
 #define FOR_EACH_STAT_ITEM(i) for (i = 0; i < NUM_STAT_ITEMS; i++)
 
diff --git a/versions/paraslash-0.2.11.tar.bz2 b/versions/paraslash-0.2.11.tar.bz2
new file mode 100644 (file)
index 0000000..bf2c023
Binary files /dev/null and b/versions/paraslash-0.2.11.tar.bz2 differ
diff --git a/versions/paraslash-0.2.11.tar.bz2.asc b/versions/paraslash-0.2.11.tar.bz2.asc
new file mode 100644 (file)
index 0000000..fc03b9b
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.2.2 (GNU/Linux)
+
+iD8DBQBEEuu2Wto1QDEAkw8RAoZ9AJ9WqLLqMOmyF7fi417dzI50uzFPKACfTrtx
+kka6qNGfM2jUOJhaMa7ELQ4=
+=uvOA
+-----END PGP SIGNATURE-----
index deaa45f633009f7eb3ffe3358952da34783d6d01..273f2fe444b3f6e233a45f0c56fc62057e73213f 100644 (file)
@@ -9,7 +9,7 @@ for general information (including a list of required software),
        <a href="INSTALL.html">INSTALL</a>
 for installation notes, and
        <a href="README.mysql.html">README.mysql</a>
-for instructions on how to use the mysql database tool
+for instructions on how to use the mysql audio file selector
 shipped with paraslash.</p>
 <p>
 The various commands of para_server and para_audiod are explained in
diff --git a/web/header2.html b/web/header2.html
new file mode 100644 (file)
index 0000000..56703c7
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+       <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+       <title>Paraslash</title>
+       <LINK href="../../para.css" REL="stylesheet" TYPE="text/css">
+       <link rel="shortcut icon" href="../../paraslash.ico">
+</head>
+<body>
+       <basefont face="lucida, helvetica, arial" size="3">
+       <table border="0" cellpadding="10" cellspacing="0">
+       <tr>
+               <td>
+                       <a href="../..//"><IMG SRC="../../paraslash.png" alt="paraslash" border="0"></a><BR>
+               </td>
+               <td>
+                       <h3>Paraslash: Play, archive, rate and stream
+                       large audio sets happily</h3>
+
+                       A set of tools for doing just what its name
+                       suggests.
+               </td>
+       </tr>
+       <tr>
+               <td valign="TOP">
+                       <br><a href="../../index.html">Home</a>
+                       <br><a href="../../NEWS.html">News</a>
+                       <br><a href="../../FEATURES.html">Features</a>
+                       <br><a href="../../download.html">Download</a>
+                       <br><a href="../../screenshots.html">Screenshots</a>
+                       <br><a href="../../demo.html">Live Demo</a>
+                       <br><a href="/cgi-bin/gitweb.cgi?p=.git;a=shortlog">Changes</a>
+                       <br><a href="../../documentation.html">Documentation</a>
+                       <br><a href="../../license.html">License</a>
+                       <br><a href="../../contact.html">Contact</a>
+                       <br><a href="../../CREDITS.html">Credits</a>
+               </td>
+               <td Valign="TOP">
+       <hr>
index b20c9a0cec8797589f3a0ebd3e716a9d3dd505a6..211562f0be88b5bca9dc52ffed8405edf77a21e0 100644 (file)
@@ -1,5 +1,9 @@
 <h3>Events</h3>
 <ul>
+       <li>2006-03-11: <a href="versions/paraslash-0.2.11.tar.bz2">paraslash-0.2.11</a>
+               <a href="versions/paraslash-0.2.11.tar.bz2.asc">(sig)</a>
+               atomic duality"
+       </li>
        <li>2006-02-22: <a href="/cgi-bin/gitweb.cgi?p=.git;a=shortlog">browsable changelog</a> (gitweb)</li>
        <li>2006-02-17: <a href="versions/paraslash-0.2.10.tar.bz2">paraslash-0.2.10</a>
        <a href="versions/paraslash-0.2.10.tar.bz2.asc">(sig)</a> "cyclic attractor"</li>