]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Fri, 1 Apr 2016 23:33:47 +0000 (01:33 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 1 Apr 2016 23:33:47 +0000 (01:33 +0200)
A couple of overflow bugs and a aslignment issue, all detected by ubsan. Plus
two unrelated old bugs.

* maint:
  client: Fix lsatt completer.
  playlist: Do not update score if no playlist is open.
  Avoid member access within misaligned address for ancillary data buffer.
  mood.c: Avoid overflow in update_quadratic_deviation().
  mood.c: Avoid integer underflow.
  mood.c: Avoid integer overflow.

234 files changed:
.changelog_before_cvs [deleted file]
.changelog_cvs [deleted file]
.gitignore
Doxyfile
FEATURES [deleted file]
GIT-VERSION-GEN
INSTALL
Makefile.in
Makefile.real [new file with mode: 0644]
NEWS
README
aac.h
aac_afh.c
aac_common.c
aacdec_filter.c
acl.c
acl.h
afh.c
afh.h
afh_common.c
afh_recv.c
afs.c
afs.cmd
afs.h
aft.c
alsa_mix.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.cmd
audiod.h
audiod_command.c
autogen.sh
bash_completion
bitstream.c
bitstream.h
blob.c
buffer_tree.c
buffer_tree.h
check_wav.c
check_wav.h
chunk_queue.c
chunk_queue.h
client.c
client.h
client_common.c
close_on_fork.c
command.c
command.h
command_util.bash [new file with mode: 0755]
command_util.sh [deleted file]
compress_filter.c
configure.ac
convert_0.3-0.4.sh [deleted file]
crypt.c
crypt.h
crypt_backend.h
crypt_common.c
daemon.c
daemon.h
dccp_recv.c
dccp_send.c
depend.sh [deleted file]
error.h
error2.c [new file with mode: 0644]
error2.pl [deleted file]
exec.c
fade.c
fd.c
fd.h
fecdec_filter.c
file_write.c
filter.c
filter.h
filter_common.c
flac_afh.c
flacdec_filter.c
gcrypt.c
ggo.c
ggo.h
grab_client.c
grab_client.h
gui.c
gui.h
gui_theme.c
http_recv.c
http_send.c
imdct.c
install-sh [deleted file]
interactive.c
interactive.h
ipc.c
list.h
m4/gengetopt/afh.m4
m4/gengetopt/afh_recv.m4
m4/gengetopt/alsa_write.m4
m4/gengetopt/amp_filter.m4
m4/gengetopt/ao_write.m4
m4/gengetopt/audioc.m4
m4/gengetopt/audiod.m4
m4/gengetopt/client.m4
m4/gengetopt/color.m4
m4/gengetopt/compress_filter.m4
m4/gengetopt/config_file.m4
m4/gengetopt/daemon.m4
m4/gengetopt/dccp_recv.m4
m4/gengetopt/fade.m4
m4/gengetopt/file_write.m4
m4/gengetopt/filter.m4
m4/gengetopt/gui.m4
m4/gengetopt/history_file.m4
m4/gengetopt/http_recv.m4
m4/gengetopt/logfile.m4
m4/gengetopt/makefile
m4/gengetopt/mp3dec_filter.m4
m4/gengetopt/oss_write.m4
m4/gengetopt/osx_write.m4
m4/gengetopt/play.m4
m4/gengetopt/prebuffer_filter.m4
m4/gengetopt/priority.m4 [new file with mode: 0644]
m4/gengetopt/recv.m4
m4/gengetopt/resample_filter.m4
m4/gengetopt/sample_format.m4
m4/gengetopt/sample_rate.m4
m4/gengetopt/server.m4
m4/gengetopt/sync_filter.m4 [new file with mode: 0644]
m4/gengetopt/udp_recv.m4
m4/gengetopt/write.m4
man_util.bash [new file with mode: 0755]
mix.h
mm.c
mm.h
mood.c
mood.h
mp3_afh.c
mp3dec_filter.c
net.c
net.h
ogg_afh.c
ogg_afh_common.c
ogg_afh_common.h
oggdec_filter.c
opus_afh.c
opus_common.c
opus_common.h
opusdec_filter.c
oss_mix.c
oss_write.c
osx_write.c
para.h
play.c
playlist.c
portable_io.h
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
ringbuffer.c
ringbuffer.h
sched.c
sched.h
score.c
send.h
send_common.c
server.c
server.cmd
server.h
sideband.c
sideband.h
signal.c
signal.h
skencil/overview.sk [deleted file]
spx.h
spx_afh.c
spx_common.c
spxdec_filter.c
stat.c
stdin.c
stdin.h
stdout.c
stdout.h
string.c
string.h
sync_filter.c [new file with mode: 0644]
t/makefile.test
t/t0004-server.sh
t/t0005-man.sh [new file with mode: 0755]
t/test-lib.sh
time.c
udp_recv.c
udp_send.c
user_list.c
user_list.h
version.c
version.h
vss.c
vss.h
wav_filter.c
web/.htaccess [new file with mode: 0644]
web/about.in.html [new file with mode: 0644]
web/contact.in.html [deleted file]
web/dia/overview.dia [new file with mode: 0644]
web/documentation.in.html
web/download.in.html
web/gitweb.css [deleted file]
web/gitweb_footer.html [deleted file]
web/gitweb_header.html.in [deleted file]
web/header.html
web/header2.html
web/images/git-logo.png [deleted file]
web/images/paraslash.ico
web/images/paraslash.png
web/images/signature.png [new file with mode: 0644]
web/images/tar-icon.png [new file with mode: 0644]
web/index.in.html [deleted file]
web/license.in.html [deleted file]
web/manual.m4
web/para.css
web/screenshots.in.html [deleted file]
web/screenshots/audiod.log [deleted file]
web/screenshots/gui.png
web/screenshots/server.log [deleted file]
wma.h
wma_afh.c
wma_common.c
wmadec_filter.c
write.c
write.h
write_common.c
write_common.h

diff --git a/.changelog_before_cvs b/.changelog_before_cvs
deleted file mode 100644 (file)
index 7f95255..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-This is the old Changelog, before the icc project switched to cvs.
-
-dbtool (sa): fix SEGFAULT when some attributes are NULL
-icc_dbtool.template: add help command
-Makefile: Check for mp3info
-dbtool.c: Handle files with Â«'» properly
-
-****** Version 91 (Sat Nov 30 13:52:37 MET 2002) *******
-Makefile: include icc_dbtool.conf.sample
-icebear: Remove unneeded signal handlers
-icc_dbtool.c Add documentation to upd command
-Documentation update
-dbtool.c: Add config file functionality
-icc_dbtool.c: Add upd command
-command.c: Bug: cs prints current stream in err msg
-zombies, zombies
-many fixes all over the place
-dbtool.c: new command: vrfy (verify entries in database)
-dbtool.c: implement clean command
-icc_dbtool: implement skip
-dont use pointers to malloced mem in shared area (causes SEGFAULT)
-icebear: SEGFAULT when cs was called after server had got HUP. Nasty bug
-icc_bash_completion: adapt to new syntax
-command.c: nuke drop_ss and new_ss
-dbtool.c: Add da (drop attribut) command (replaces old drop_ss)
-dbtool.c: Add na (new attribut) command (replaces old new_ss)
-Makefile: Changelog contains entries not yet done
-
-****** Version 90 (Wed Nov 13 02:05:34 MET 2002) *******
-icebear: kill unneeded signal handlers
-icebear: call info only if it is a valid command
-server: code cleanup
-command.c uptime: print #connections and #commands
-server: count #connections and #commands
-server/icebear/command: count/display number of songs already played
-icc.h: cleanup
-command.c: nuke ms command
-dbtool.c: add summary command
-dbtool.c: new command: sa, replaces old ss (set subset) command
-dbtool.c: new command: streams
-server: renew command list on SIGHUP
-icebear: call us only if it is a valid command
-command.c: sort command list alphabetically
-server: Nicify logging
-command.c: New command: perms
-command.c: Rewrite command handlers to use linked list
-command.c: introdule linked list of commands
-
-****** Version 89 (Sat Nov  9 17:27:32 MET 2002) *******
-command.c: Major cleanup
-
-****** Version 88 (Sat Nov  9 01:34:14 MET 2002) *******
-icebear: New command: cs
-init_icebear put default_stream to freshly allocated shared mem
-server: pass default_stream to init_icebear
-server: new configfile option: default_stream
-command.c help: call icc_dbtool help
-
-****** Version 87 (Fri Nov  8 23:03:03 MET 2002) *******
-Makefile: new target icc_dbtool
-icc_dbtool: Switch to mysql C API
-
-****** Version 86 (Thu Oct 17 22:38:37 MEST 2002) *******
-New file: README.dbtool
-client/server: use OPENSSL define of config.h instead of hard coded path
-client/server: include config.h
-README: add icecast to requirements
-
-****** Version 85 (Thu Oct 10 22:56:29 MEST 2002) *******
-Makefile: Don't include tdldb in tgz
-Makefile: New target: distclean (clean no longer removes Changelog, TODO and version.h)
-dbtool ms: print each dot only once (reduces size of ps from 171kb to 22kb)
-
-****** Version 84 (Fri Oct  4 15:01:54 MEST 2002) *******
-icc_server.conf.sample/icecast.conf.sample: Use same passwd
-Makefile: include icecast.conf.sample in tarball
-Makefile: mkdir ~/public_html
-
-****** Version 83 (Wed Sep 25 01:59:06 MEST 2002) *******
-Makefile tgz: php crap got messed up
-
-****** Version 82 (Wed Sep 25 01:56:55 MEST 2002) *******
-server: Reread configuration on SIGHUP
-Makefile: New Changelog format
-Makefile tgz: create archive with leading directory icc-$version
-
-************* Version 81 *************
-server update_str: fix bug: uptime string messed up
-README: add description of icc_bash_completion
-INSTALL: Update, extentions and nicifying
-icc_bash_completion: adapt to new layout of Â«icc help«
-
-************* Version 80 *************
-server: print uptime in welcome message
-server: new functions: uptime,uptime_str
-command.c: new commands: uptime,version
-icebear: exec_cmd: test if stopped for ff/jmp/next. Fixes Segfault when eg Â«next» was called with no file opened yet
-
-************* Version 79 *************
-server: implement remaining part of configuration part
-server: implement basic configuration file functionality
-
-************* Version 78 *************
-Makefile: install icc_client.conf.sample and also include it in tgz
-new file: icc_client.conf.sample
-client: actually print version and info if loglevel is at least info
-client: new function: print_version
-client: read_config: change verbose setting to loglevel
-client: new option -l (loglevel) obsoletes -v (verbose)
-client: Fix Â«if(verbose==TRUE)» madness, use loglevels instead
-client: write log function similar to server's log function
-README: Update icc_client description: Delete sentence on telnet
-
-************* Version 77 *************
-client: take localhost as default host, not p133
-client: new option -c (specify config file)
-client: implement reading of config file
-
-************* Version 76 *************
-server sigchld_handler: Add exit status also as numerical value
-command.c: Add description for drop_ss, fut, us
-
-************* Version 75 *************
-icebear: fix segfault on nomore (double close mp3_stream)
-icebear: nm->nomore
-command.c: add nomore command (got forgotten)
-client: catch SIGCHLD
-client: introduce internal variable debug to toggle debug before command line argumants are parsed (for verbose switch)
-client: Nicify verbose output
-client: Fix command line parsing
-
-************* Version 74 *************
-Makefile tgz: use prefix icc-0.0.
-Makefile tgz: bug: forget to read VERSION
-Makefile: Add .tdldb to sources
-
-************* Version 73 *************
-client: new option -V (version)
-server: new option -V (version)
-Makefile: new target: tgz (implies version)
-Makefile: fix version numbering
-Makefile: include INSTALL in sources
-Makefile: Use -p Option for tar
-
-************* Version 72 *************
-icc_server: print info on who we are serving to argv[0]
-icebear: print status to argv[0]
-command.c: Add missing descriptions to all internal commands
-dbtool.template: Use find instead of locate
-server: send proper error message if client sent unkown command
-
-************* Version 71 *************
-client: Added help option -h
-command.c help: Nicify output. Include needed permissions.
-command.c: Add description for stat, sb and term
-Makefile: Add target dbtool_install
-Makefile install: don't install dbtool
-server/client: print version number
-icc.h include version.h
-Makefile: added target version.h
-added INSTALL
-README: Remove Warning message concerning lack of authentification
-Makefile: implement PREFIX
-icebear: typo in jmp command
-icc_dbtool.template us: Don't print warning
-Makefile install: Install also icc_dbtool.template
-
-************* Version 70 *************
-
-************* Version 69 *************
-icc_dbtool.template: Change fut so that it does not need bogosort anymore
-
-************* Version 68 *************
-Makefile: include icc_dbtool.template in tgz package
-server: new command line options: P, i, ip
-server: kill global variable conn
-command.c parse_cmd: Fix SEGV when in interactive mode and no option given (reported by Christof Müller)
-Makefile: all: dont make TODO and Changelog
-icc.client: remove passwd
-icc.h remove maan
-
-************* Version 67 *************
-icebear: delete option for id3 comment in mp3info call
-icebear: Fix bug: pollret returns strange events
-icebear: fix division by 0 bug when length of file is zero seconds
-
-************* Version 66 *************
-
-************* Version 65 *************
-command.c: perror -> log
-command.c: new fuction: check_permissions
-command.c: insert dummy at the beginning of command list to let start command numbers by 1 instead of 0
-
-************* Version 64 *************
-server: add string strerror(errno) to log file entries
-command.c: make parse_command only return command number and call handle_cmd from main
-server: print log msg _before_ exit(1) if send fails
-server: exit(1) if send fails
-
-************* Version 63 *************
-command.c: code cleanup
-icc_server: implement option -c (specify config file)
-client: fix SEGFAULT when command line ends with invalid option
-client: add option -k (secret key file)
-
-************* Version 62 *************
-implement permission check for all commands
-
-************* Version 61 *************
-implement basic configuration file functionality
-icc_server/client: implement rsa authentification via openssl
-
-************* Version 60 *************
-command.c: add help text for jmp and ff commands
-command.c: help: print command handler and needed privileges
-icebear: minor code cleanups
-icebear: Nicify log output
-icebear: split poll_cmd_listener to allow blocking reads => no more sleep(1)
-
-************* Version 59 *************
-icebear: Code documentation
-icebear: move shout init/shoutdown to exec_cmd. This might fix pause command.
-
-************* Version 58 *************
-icebear: increase number of allowed invalid files before giving up to 100
-dbtool clean: Handle files with Â«'» properly
-
-************* Version 57 *************
-dbtool: add summary command
-icc_process_form.php: change strcmp(a,b)=0 to not strcmp(a,b)
-
-************* Version 56 *************
-
-************* Version 55 *************
-kill id3 code
-dbtool: change info and fut to avoid Lost connection to MySQL server during query
-icebear: It dies if there no valid songs. Stop and lurk for play instead
-icebear: code cleanup
-
-************* Version 54 *************
-icebear/icc.h: use long unsigned int to avoid overflow in status bar
-
-************* Version 53 *************
-server: BUG in help: dont check args if args=NULL
-command.c help: implement Â»help command«
-README: add LICENSE
-README: include description to php scripts
-server: kill guics command
-server: kill log command
-dbtool: kill pw
-dbtool: add local for all local vars
-
-************* Version 52 *************
-command.c, icc.h: cleanup declaration of command struct
-dbtool us: add option to specify update time
-add bash completion for icc_client
-
-************* Version 51 *************
-kill spl command
-dbtool: add ls command
-icc_server: add ls command
-icc_server: kill cd command
-
-************* Version 50 *************
-php/icc_form, php/icc_process_form: fix several bugs
-
-************* Version 49 *************
-add background gradient image
-
-************* Version 48 *************
-write mp3 search form icc_form.php
-icebear: log seconds with 2 digits
-icc_gui: Add sleep(2) before each reconnect
-php: delete unneeded pics
-
-************* Version 47 *************
-icc_server: add time and date to log
-
-************* Version 46 *************
-php/icc_dbgui: fix off by one bug
-dbtool: clean reports files as deleted which are still there
-remove wtc.jpg
-
-*** Version 45 ***
-improve php scripts
-
-*** Version 44 ***
-add php scripts for apache/mysql
-
-*** Version 43 ***
-icc.h changed #define ERR to #define ERROR
-
-*** Version 42 ***
-gui: make it survive window resize
-
-*** Version 41 ***
-Add short description of all icc tools to README
-
-*** Version 40 ***
-dbtool fut: replace use of ancient anplay variable by aprropriate one
-dbtool ss: make it work again when args _are_ given
-server: Add -p <port> option
-client: catch SIGINT
-
-*** Version 39 ***
-dbtool: ps does not work if no arg is given
-dbtool: ss does not work if no arg is given
-server: log GPL banner (loglevel INFO)
-client: Add GPL banner in welcome message
-dbtool last: print full filenames
-dbtool info: print name if no argument was given
-
-*** Version 38 ***
-icebear: make jmp,ff,next,pause,play,stop,nm actually respond via new pipe
-create new pipe icebear->server for responses to icebear commands
-
-*** Version 37 ***
-dbtool cs: print info if no argument is given
-
-*** Version 36 ***
-icebear: kill getenv code
-icebear: rewrite startup code to prevent icebear from playing on startup
-
-*** Version 35 ***
-icebear: nicify log messages
-icebear: make pause, play, stop work in all possible cases
-
-*** Version 34 ***
-Makefile version: include COPYRIGHT, GPL and README files in tarball
-Add COPYRIGHT and GPL files
-*.c *.h dbtool: Add GPL headers
-inplement option -L logfile
-client: Fix bug: commands with options don't work
-
-*** Version 33 ***
-Makefile: only put completed todos into Changelog
-server: implement Daemon mode
-icc_client: Dont send empty lines
-
-*** Version 32 ***
-command: Fix nasty bug which caused sending 2 nullbytes instead of only one
-dbtool info: print info on current song if no argument was given
-
-*** Version 31 ***
-dbtool: Another silly bug in info which prevented dir to be printed
-client: Fix silly bug caused it to send wrong number of bytes
-icc_server: implemented term command
-Makefile: strip away useless tdl info
-icc_dbtool: Changed Warning message for commands not yet implemented
-icc_server: Added help command
-icc_client: add readline support
-icc_dbtool fut: Fix bug when there is exactly one new song
-db_tool: implement skip
-db_tool: implement ss
-db_tool: implement clean
-db_tool: implement upd
-dbtool: implemented pw
-move icc ms to dbtool
-icc_shell: ms
-icc_server/dbtool: new command hist
-dbtool: implement last
-created icc_shell
-make icebear use icc_dbtool instead of mp3
-write db_tool basics (fut, ass, ps, info, us)
-Makefile improvements
-icc_client: Add verbose flag -v
-Adjust loglevels and make logging nicer
-icc_client: properly handle command line options
-icc_server: Dont start playing on startup
-take output of info command as misc info
-icc_client: treat nr_options correctly
-log messages cleanup
-log proper exit status (WIFSIGNALED...)
-icebear: stop actually restarts icebear
-icc_client: interactive mode
-icc_server: on errors send messages clients
-
-
diff --git a/.changelog_cvs b/.changelog_cvs
deleted file mode 100644 (file)
index 889f23c..0000000
+++ /dev/null
@@ -1,12680 +0,0 @@
-2006-02-20 07:52  maan
-
-       * filter.c: cosmetics
-
-2006-02-20 07:51  maan
-
-       * crypt.h: add doxygen file comment
-
-2006-02-20 07:50  maan
-
-       * Doxyfile: exclude some more files
-
-2006-02-20 07:16  maan
-
-       * NEWS: [no log message]
-
-2006-02-20 07:16  maan
-
-       * CREDITS: add Lorenzo Bettini
-
-2006-02-20 05:24  maan
-
-       * Makefile.in: with gengetopt-2.16 we no longer need the conversion
-         optind=1 -> optind=0. Get rid of it.
-
-2006-02-19 07:46  maan
-
-       * crypt.c: add documentation
-
-2006-02-19 05:55  maan
-
-       * crypt.c: para_decrypt_challenge() leaks 128 bytes if
-         para_decrypt_buffer() fails. Fix it
-
-2006-02-19 05:47  maan
-
-       * gcc-compat.h: trivial: fix comment
-
-2006-02-19 05:46  maan
-
-       * crypt.c: whitespace cleanup
-
-2006-02-19 05:46  maan
-
-       * Doxyfile: activate the source browser
-
-2006-02-19 05:34  maan
-
-       * fade.c: make fade() static
-
-2006-02-19 04:15  maan
-
-       * Doxyfile: deactivate latex and man output, activate search engine
-
-2006-02-19 03:35  maan
-
-       * sdl_gui.c: add missing file name in comment
-
-2006-02-18 01:52  maan
-
-       * scripts/demo-script: download paraslash-0.2.10
-
-2006-02-18 01:21  maan
-
-       * web/index.in.html: typo
-
-2006-02-18 00:51  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2006-02-18 00:49  maan
-
-       * versions/: paraslash-0.2.10.tar.bz2,
-         paraslash-0.2.10.tar.bz2.asc: paraslash-0.2.10
-
-2006-02-18 00:48  maan
-
-       * Makefile.in, NEWS, configure.ac, web/index.in.html: paraslash
-         0.2.10
-
-2006-02-18 00:12  maan
-
-       * Doxyfile: config file for doxygen
-
-2006-02-17 23:23  maan
-
-       * ortp_recv.c: increase number of bad chunks before returning an
-         error
-
-2006-02-16 05:28  maan
-
-       * README.mysql: [no log message]
-
-2006-02-16 04:48  maan
-
-       * filter.ggo: update help text for --list_filters
-
-2006-02-16 02:37  maan
-
-       * web/: documentation.in.html, index.in.html: annonce the new API
-         Reference
-
-2006-02-16 02:36  maan
-
-       * Makefile.in: new targets: doxygen, web/header2.html. Target www
-         implies doxygen
-
-2006-02-16 02:32  maan
-
-       * server.c: rename 'Reference Manual' to 'API Reference'
-
-2006-02-16 02:31  maan
-
-       * web/header.html: use the new stylesheet
-
-2006-02-16 02:30  maan
-
-       * web/para.css: the new stylesheet for the paraslash web pages
-
-2006-02-16 00:41  maan
-
-       * db.h, dopey.c, filter.h, mysql.c, recv.h, server.c: some small
-         documentation cleanups and additions
-
-2006-02-15 22:56  maan
-
-       * http_send.c, ortp_send.c, send.h, server.c: struct sender: rename
-         set_fds/handle_fds to pre_select/post_select and add
-         documentation
-
-2006-02-15 22:55  maan
-
-       * http_recv.c: kill superfluous parenthesis
-
-2006-02-15 22:14  maan
-
-       * oggdec.c: rename private_ogg_data to private_oggdec_data
-
-2006-02-15 22:13  maan
-
-       * http_recv.c: add remaining documentation
-
-2006-02-15 22:12  maan
-
-       * afs.h, filter.h, mp3.c, ortp.h, recv.h, ringbuffer.h, signal.c,
-         string.c, string.h: add missing doxygen file comments
-
-2006-02-15 05:49  maan
-
-       * http.h: and doxygen file comments
-
-2006-02-15 05:49  maan
-
-       * net.h: add GPL header and doxygen file comments
-
-2006-02-15 05:04  maan
-
-       * exec.c: add documentation of all functions
-
-2006-02-15 04:19  maan
-
-       * recv_common.c: gcc-compat.h gets included by para.h
-
-2006-02-15 03:51  maan
-
-       * mysql.c, string.c, string.h: rename regex_list to para_macro and
-         add documentation
-
-2006-02-15 03:11  maan
-
-       * Makefile.in, afs.c, audioc.c, audiod.c, client.c,
-         close_on_fork.c, command.c, compress.c, crypt.c, daemon.c, db.c,
-         dbadm.c, dopey.c, exec.c, fade.c, filter.c, filter_chain.c,
-         grab_client.c, gui.c, http_recv.c, http_send.c, mp3dec.c,
-         mysql.c, net.c, ogg.c, oggdec.c, ortp_recv.c, ortp_send.c,
-         para.h, recv_common.c, ringbuffer.c, sdl_gui.c, server.c,
-         slider.c, stat.c, string.c, string.h, wav.c: move the function
-         prototypes of string.c from para.h to its own header file
-         string.h
-
-2006-02-15 02:20  maan
-
-       * ortp_send.c: add documentation of struct ortp_target
-
-2006-02-15 02:00  maan
-
-       * error.h: some new error messages for the mp3 audio format handler
-
-2006-02-15 01:59  maan
-
-       * mp3.c: use the error subsystem everywhere
-
-2006-02-15 01:27  maan
-
-       * mp3.c: whitespace cleanup, make mp3_read_chunk() static
-
-2006-02-15 01:21  maan
-
-       * mp3.c: typdefs are evil
-
-2006-02-15 00:35  maan
-
-       * mp3dec.c: another quite serious issue: do not try to convert if
-         the output buffer is almost full. Otherwise we risk that the next
-         frame does not fit into the ouput buffer and we only can error
-         out in this situation. This patch avoids the problem.
-
-2006-02-15 00:31  maan
-
-       * filter.c: fix serious bug: -1 is no valid error number for
-         filter.c. The proper error number is returned from filter_io()
-         anyway, so use that one.
-
-2006-02-14 23:24  maan
-
-       * server.c: doxification
-
-2006-02-14 23:24  maan
-
-       * http_send.c: typo
-
-2006-02-14 22:57  maan
-
-       * http_send.c, ortp_send.c, server.c: add documentation of all
-         structures used by http_send, rename para_http_init to
-         http_send_init, likewise for ortp
-
-2006-02-14 22:09  maan
-
-       * command.c, server.h: add documentation of struct misc_meta_data.
-         Kill the unused send field of struct_sender_command_data and add
-         documentation for that struct as well.
-
-2006-02-14 22:08  maan
-
-       * server.c: add documentation of mmd_lock() and mmd_unlock()
-
-2006-02-14 21:25  maan
-
-       * README: add gcc to the list of required software
-
-2006-02-14 21:07  maan
-
-       * Makefile.in: make gcc-2.95.4 happy
-
-2006-02-14 21:02  maan
-
-       * Makefile.in, configure.ac: let gcc produce the dependencies for
-         make rather than doing it by hand
-
-2006-02-14 20:17  maan
-
-       * para.h: kill another unused macro
-
-2006-02-14 19:46  maan
-
-       * para.h: kill two unused macros, change year in copyright to 2006
-
-2006-02-14 19:41  maan
-
-       * server.h: add more documentation on AFS_READ and friends
-
-2006-02-14 19:40  maan
-
-       * daemon.c, daemon.h, para.h: move enum uptime from para.h to
-         daemon.h. Include daemon.h from daemon.c
-
-2006-02-14 19:26  maan
-
-       * para.h: use the __printf_x_y macros
-
-2006-02-14 19:20  maan
-
-       * para.h, server.h: move permission flags from para.h to server.h,
-         turn them into an enum and doxify
-
-2006-02-14 19:14  maan
-
-       * command.c, dopey.c, mysql.c, server.h: rename usage to synopsis
-         in struct server_command
-
-2006-02-14 19:09  maan
-
-       * server.h: add documentation of struct server_command and
-         struct_user
-
-2006-02-14 19:04  maan
-
-       * Makefile.in, list.h, ortp_recv.c, server.h, web/download.in.html:
-         oops, the last commit was an accident, back to previous versions
-
-2006-02-14 18:59  maan
-
-       * Makefile.in, list.h, ortp_recv.c, server.h, web/download.in.html:
-         add documentation o
-
-2006-02-14 03:36  maan
-
-       * server.c: log error message if database init failed
-
-2006-02-14 03:34  maan
-
-       * error.h, mysql.c: add two more error messages
-
-2006-02-14 02:56  maan
-
-       * command.c, db.h, dopey.c, mysql.c, server.h: add some
-         documentation, rename struct command to struct server_command
-
-2006-02-14 02:16  maan
-
-       * grab_client.h: cosmetics
-
-2006-02-14 02:07  maan
-
-       * mysql.c: make a function static, add documentation
-
-2006-02-14 02:05  maan
-
-       * filter_chain.c: fix typo, add file comment
-
-2006-02-14 02:04  maan
-
-       * audiod.c: add documentation of struct slot_info
-
-2006-02-14 01:25  maan
-
-       * stat.c: cosmetics
-
-2006-02-13 22:29  maan
-
-       * audiod.c: rename struct stream_io to struct slot_info
-
-2006-02-13 20:59  maan
-
-       * audiod.c: struct audio_format_info: rename wcmd to write_command
-         and add documentation
-
-2006-02-13 20:39  maan
-
-       * audiod.c: add documentation of struct audiod_command
-
-2006-02-13 20:32  maan
-
-       * audiod.c: rename struct command to audiod_command
-
-2006-02-13 02:41  maan
-
-       * time.c: add documentation
-
-2006-02-13 01:49  maan
-
-       * audiod.c: kill unused filter_cmds field in struct stream_io, add
-         some documentation, minor cleanups
-
-2006-02-12 03:55  maan
-
-       * recv.c: make parse_config() static
-
-2006-02-12 03:44  maan
-
-       * recv.c: only print an error message if the return value is
-         negative
-
-2006-02-12 03:42  maan
-
-       * close_on_fork.c, close_on_fork.h: add documentation for all
-         public symbols
-
-2006-02-12 03:12  maan
-
-       * audiod.c: make two functions static. Add documentation of
-         para_audiod's different modes and of num_filters()
-
-2006-02-11 22:04  maan
-
-       * list.h: kill two unused macros
-
-2006-02-11 21:23  maan
-
-       * grab_client.c: finish documentation of all public functions
-
-2006-02-11 20:42  maan
-
-       * audiod.c, audiod.h, error.h, grab_client.c: make
-         get_audio_format_num() public and use it from grab_client.c, add
-         documentation of grab_client_new().
-
-2006-02-11 20:28  maan
-
-       * grab_client.h: add documentiation of struct grab_client
-
-2006-02-11 19:48  maan
-
-       * audiod.c: new function get_audio_format_num()
-
-2006-02-11 01:47  maan
-
-       * audiod.c, audiod.h, grab_client.c: nuke the 'name' field of
-         struct audio_format
-
-2006-02-11 00:57  maan
-
-       * audiod.h, grab_client.h: move AUDIO_FORMAT_MP3,
-         AUDIO_FORMAT_ARRAY and MAX_STREAM_SLOTS from grab_client.h to
-         audiod.h.
-
-2006-02-11 00:35  maan
-
-       * http_recv.c, ortp_recv.c: fix typo in comment
-
-2006-02-10 04:38  maan
-
-       * Makefile.in: add some missing dependencies
-
-2006-02-10 04:37  maan
-
-       * grab_client.h: oops, argc, argv _are_ used. Add them again.
-
-2006-02-10 03:37  maan
-
-       * grab_client.h: make mode type enum rather than int, nuke two
-         unused members in struct grab_client
-
-2006-02-10 03:32  maan
-
-       * grab_client.c: whitespace cleanup
-
-2006-02-10 02:42  maan
-
-       * oggdec.c: Use the pointer to the filter_node as datasource for
-         the ov_callbacks. This makes the filter chain info available for
-         the read callback via the new fn->fci pointer. Use that pointer
-         instead of keeping a copy of the relevant information in struct
-         private_ogg_data. As this simplifies oggdec.c quite a bit, add
-         documentation of struct private_ogg_data to compensate ;)
-
-2006-02-09 23:53  maan
-
-       * audiod.c, compress.c, daemon.c, filter.c, filter.h,
-         filter_chain.c, mp3dec.c, oggdec.c, ortp_send.c, wav.c: add new
-         pointer 'fci' to struct filter_node. It is initialized by
-         para_audiod/para_filter when the chain is constructed. This
-         allows to strip off the fci argument of the convert functions of
-         struct filter.
-
-2006-02-09 00:57  maan
-
-       * oggdec.c: simplify the read callback
-
-2006-02-09 00:01  maan
-
-       * mp3dec.c: remove unused mad_timer from private_mp3_data and add
-         documentation of said struct
-
-2006-02-08 23:19  maan
-
-       * ortp.h, ortp_recv.c, ortp_send.c: also encode the chunk timestamp
-         in each packet
-
-2006-02-08 20:23  maan
-
-       * http_recv.c: rename private_http_data to private_http_recv_data,
-         just to be consistent
-
-2006-02-08 20:21  maan
-
-       * db.c: add short description for db.c, fix a typo.
-
-2006-02-08 20:14  maan
-
-       * compress.c: add documentation of struct private_compress_data,
-         change clip and pn to unsigned type
-
-2006-02-08 19:46  maan
-
-       * Makefile.in, audiod.c, command.c, daemon.c, daemon.h, para.h,
-         server.c: move function prototypes for daemon.c to its own file
-
-2006-02-08 19:41  maan
-
-       * NEWS: [no log message]
-
-2006-02-08 17:49  maan
-
-       * ortp_send.c: fix a huge memory leak which was introduced
-         yesterday
-
-2006-02-08 04:46  maan
-
-       * audiod.c, command.c, daemon.c, para.h, server.c: add
-         documentation of daemon.c, minor cleanups
-
-2006-02-08 02:37  maan
-
-       * audiod.c, recv.h: nuke chunk_time from struct receiver_node
-
-2006-02-08 02:35  maan
-
-       * string.c: better docu of s_a_r()
-
-2006-02-08 02:05  maan
-
-       * filter_chain.c: filter_io: call the convert function of each
-         filter not just once, but in a loop that exits when nothing was
-         converted. Fixes drop outs with ortp and ogg.
-
-2006-02-08 01:28  maan
-
-       * error.h, ortp.h, ortp_recv.c, ortp_recv.ggo, ortp_send.c: encode
-         the chunk time and a flag which indicates whether headers get
-         streamed at the beginning of each data packet. This allows to
-         nuke the --chunk_time and --header command line options of the
-         ortp receiver.
-
-2006-02-08 01:00  maan
-
-       * wav.c: cosmetics
-
-2006-02-08 00:59  maan
-
-       * recv.c: print error message instead of error number
-
-2006-02-07 00:12  maan
-
-       * mp3dec.c: trvial rename: mp3dec_data -> private_mp3dec_data. Just
-         to keep naming consistent with the other filters/receivers
-
-2006-02-07 00:08  maan
-
-       * ortp_recv.c: finish documentation of struct private_ortp_data
-
-2006-02-06 18:43  maan
-
-       * ortp_recv.c: kill the timeout member of struct private_ortp_data.
-         Start documentation of this struct
-
-2006-02-06 17:48  maan
-
-       * filter.h: move the fancy macros to the end and exclude them from
-         doxification
-
-2006-02-06 17:46  maan
-
-       * afs.h: cosmetics
-
-2006-02-06 17:35  maan
-
-       * filter.h: add another reference
-
-2006-02-06 17:34  maan
-
-       * error.h, oggdec.c, recv.h: add some conditional sections to
-         nicify the doxygen output
-
-2006-02-06 17:22  maan
-
-       * afs.h: kill two unused prototypes
-
-2006-02-06 06:25  maan
-
-       * Makefile.in: add -Wunused-macros to DEBUG_CPPFLAGS
-
-2006-02-06 06:22  maan
-
-       * audiod.c, compress.c, sdl_gui.c: kill some more dead macros
-
-2006-02-06 06:18  maan
-
-       * gui.c: kill two unused macros
-
-2006-02-06 05:16  maan
-
-       * recv.h, recv_common.c: this array must be called 'receivers'
-         anyway as this is the name that gets exported a few lines above.
-         Hence, no need to make it a function-like macro.
-
-2006-02-06 05:09  maan
-
-       * recv.h: add documentation for struct receiver_node
-
-2006-02-06 04:42  maan
-
-       * filter.h: add some introductory text on paraslash filters, use
-         multi-line macros
-
-2006-02-06 04:00  maan
-
-       * http_recv.c, ortp_recv.c, recv.h: nuke unused print_help
-
-2006-02-06 03:55  maan
-
-       * recv.h: finish documentation of struct receiver
-
-2006-02-06 01:53  maan
-
-       * para.h: minor cleanup
-
-2006-02-06 01:52  maan
-
-       * http_recv.c: kill unused HTTP_DISCONNECTED, use named enum and
-         add documentaion of struct private_http_data
-
-2006-02-06 00:46  maan
-
-       * http_recv.c, ortp_recv.c, recv.c, recv.h: all receiver init
-         functions always return success. Let them return void instead.
-         Start doxification of recv.h. http_recv.c/ortp_recv.c: No need to
-         keep a copy of the configuration in the private data of each
-         receiver node. recv.c: Initialize the activated receiver only
-         once.
-
-2006-02-06 00:00  maan
-
-       * recv_common.c: delete two extern function declarations that are
-         already declared in recv.h
-
-2006-02-05 21:42  maan
-
-       * gui.c: cosmetics
-
-2006-02-05 21:38  maan
-
-       * http_send.c: do not use send_bin_buffer() as it retries on short
-         writes which might block. Use write() instead and queue the
-         remaining buffer on short writes.
-
-2006-02-05 21:34  maan
-
-       * audiod.c: add missing call to clear_slot()
-
-2006-02-05 19:41  maan
-
-       * gui.c: redraw bot win on SIGWINCH
-
-2006-02-05 02:09  maan
-
-       * Makefile.in: add ringbuffer.h to headers
-
-2006-02-05 00:52  maan
-
-       * gui.c: fix a off-by-one scrolling bug
-
-2006-02-05 00:11  maan
-
-       * gui.c: Kill the crappy output pager. It is no longer needed as
-         the bottom window is scrollable
-
-2006-02-04 22:33  maan
-
-       * recv.h: kill unused argc, argv from struct receiver_node
-
-2006-02-04 22:24  maan
-
-       * error.h: adjust NUM_SS
-
-2006-02-04 22:21  maan
-
-       * error.h: kill the unused first argument of PARA_ERROR()
-
-2006-02-04 22:09  maan
-
-       * error.h: doxyfication
-
-2006-02-04 20:36  maan
-
-       * audiod.c, filter.c, filter.h, filter_chain.c: make
-         check_filter_arg() return int (the filter number) which is a much
-         saner interface that what we've had before. Now, fn-conf is NULL
-         if no command line parser exists for that filter and
-         success/failure is detected by the return value. Add
-         documentation for filter_chain.c and the remaining part of
-         filter.h
-
-2006-02-04 03:08  maan
-
-       * audiod.c, compress.c, filter.c, filter.h, filter_chain.c,
-         mp3dec.c, oggdec.c, wav.c: init() and open() are always
-         successful, for all filters. So make them return void instead of
-         int. Include the mp3dec filter only if mad headers mad libs both
-         were detected. Make wav_open() static. Start doxyfication of
-         filter.h
-
-2006-02-04 00:01  maan
-
-       * audiod.c: use init_filters() instead of duplicating it
-
-2006-02-03 23:34  maan
-
-       * client.c, http_recv.c, net.c, net.h: rename do_connect() to
-         para_connect()
-
-2006-02-03 23:03  maan
-
-       * string.c: switch to doxygen format, add some references
-
-2006-02-03 22:40  maan
-
-       * Makefile.in, audioc.c, audiod.c, client.c, command.c, dopey.c,
-         http_recv.c, http_send.c, mysql.c, net.c, para.h, server.c,
-         string.c: add dependencies on new net.h, move para_hostname()
-         from net to string which makes afs independent of net as it
-         should be. No need to include gcc-compat.h twice.
-
-2006-02-03 22:37  maan
-
-       * net.h: contains function declarations for all non-static
-         functions in net.c. Yanked out from para.h.
-
-2006-02-03 21:47  maan
-
-       * Makefile.in: whitespace fixes
-
-2006-02-03 21:42  maan
-
-       * net.c: document all remaining non-static functions
-
-2006-02-03 21:41  maan
-
-       * stat.c: add documentation for stat_client_add()
-
-2006-02-03 07:13  maan
-
-       * audiod.c, para.h, stat.c: rename dump_stat_line() to
-         stat_client_write() and add documentation
-
-2006-02-02 18:42  maan
-
-       * afs.c, afs.h, mp3.c, ogg.c: simplify read_chunk()
-
-2006-02-02 16:48  maan
-
-       * afs.c, afs.h, db.h, ringbuffer.c, stat.c, string.c: switch to
-         doxygen comment style
-
-2006-02-02 16:47  maan
-
-       * db.c: whitespace fixes, switch to doxygen comment style
-
-2006-02-02 01:15  maan
-
-       * net.c, signal.c: switch to doxygen comment format
-
-2006-02-01 22:07  maan
-
-       * net.c, para.h: add more documentation, minor cleanups
-
-2006-02-01 22:06  maan
-
-       * ringbuffer.c: cosmetics
-
-2006-02-01 19:40  maan
-
-       * NEWS: [no log message]
-
-2006-02-01 19:40  maan
-
-       * error.h: add ringbuffer stuff
-
-2006-02-01 19:39  maan
-
-       * Makefile.in, configure.ac: gui and audiod need ringbuffer.o
-
-2006-02-01 19:38  maan
-
-       * gui.c: make bottom window scrollable
-
-2006-02-01 19:36  maan
-
-       * audiod.c: use the generic ringbuffer functions from ringbuffer.c
-
-2006-02-01 19:25  maan
-
-       * ringbuffer.c, ringbuffer.h: generic ring buffer routines to be
-         used by audiod and gui
-
-2006-02-01 19:24  maan
-
-       * http_send.c: comment out noisy debug message
-
-2006-01-29 21:52  maan
-
-       * oggdec.c: no source doc for static functions, kill obsolete
-         comment
-
-2006-01-29 21:30  maan
-
-       * mp3.c, play.c: no docbook for static functions
-
-2006-01-29 21:25  maan
-
-       * afs.c, db.h, dopey.c, server.c: make update_audio_file()
-         optional, add documentation of struct dbtool
-
-2006-01-29 14:38  maan
-
-       * pics/paraslash/no_pics.jpg: no need to include this in each
-         tarball
-
-2006-01-27 19:34  maan
-
-       * server.c: use ifdef HAVE_ORTP rather than ifdef HAVE_LIBORTP as
-         the former is only defined if libortp _and_ the ortp headers were
-         found, which is what we want here.
-
-2006-01-27 19:14  maan
-
-       * audiod.c: add missing <cr>
-
-2006-01-27 17:48  maan
-
-       * stat.c: add documentation for stat_line_valid()
-
-2006-01-27 17:35  maan
-
-       * configure.ac: fix ortp detection
-
-2006-01-27 15:28  maan
-
-       * string.c: fix some typos in documentation
-
-2006-01-27 15:17  maan
-
-       * stat.c: add documentation for for_each_line()
-
-2006-01-27 15:01  maan
-
-       * audiod.c, gui.c, gui_common.c, para.h, stat.c: rename
-         check_buf_for_items() to for_each_line()
-
-2006-01-27 13:14  maan
-
-       * NEWS, audiod.c, error.h, gui.c, http_recv.c, para.h, server.c,
-         signal.c: simplify para_signal_init(), add documentation for each
-         non-static function in signal.c
-
-2006-01-27 12:13  maan
-
-       * server.c: use para_reap_children() from signal.c rather than
-         duplicating its code in server.c
-
-2006-01-27 10:14  maan
-
-       * afs.h: add documentation for struct audio format
-
-2006-01-26 20:36  maan
-
-       * afs.c: add documentation for afs_send_chunk()
-
-2006-01-26 19:56  maan
-
-       * afs.c, afs.h, server.c: simplify afs_preselect() and add
-         documentation
-
-2006-01-26 19:29  maan
-
-       * afs.c: afs_preselect(): kill an unused variable and add some
-         documentation
-
-2006-01-26 19:08  maan
-
-       * afs.c, afs.h, server.c: rename afs_mainloop() to afs_preselect()
-
-2006-01-26 19:06  maan
-
-       * command.c: com_nomore(): only set the NO_MORE bit if playing or
-         paused
-
-2006-01-26 19:03  maan
-
-       * afs.c: add documentation of afs_init() and of afs_playing() and
-         friends
-
-2006-01-26 17:22  maan
-
-       * http_recv.c, net.c: use the return value of do_connect(). Move a
-         generic log message from http_recv.c to net.c.
-
-2006-01-26 15:34  maan
-
-       * error.h, net.c: E_HOST_INFO belongs to the net error list rather
-         than the recv error list. That fixes an audiod segfault when
-         given an invalid hostname as the http receiver option (because
-         the recv error list is undefined in audiod). Also, kill pointless
-         log message in get_host_info().
-
-2006-01-25 16:48  maan
-
-       * play.c: cosmetics
-
-2006-01-25 15:32  maan
-
-       * client.c, crypt.c, db.c, dopey.c, ogg.c: replace 2005 by 2006 in
-         GPL headers
-
-2006-01-25 15:23  maan
-
-       * error.h: fix NUM_SS
-
-2006-01-25 15:06  maan
-
-       * NEWS, command.c, db.h, error.h, mysql.c, server.c: convert the
-         remaining part of para_server to the new error subsystem
-
-2006-01-25 12:32  maan
-
-       * dopey.c, error.h: convert dopey.c to the new error subsystem
-
-2006-01-25 11:53  maan
-
-       * error.h, http_send.c, ortp_send.c, send.h: the return value of
-         sender.set_fds and sender.send is never checked. In fact, all
-         these functions always returned 1. Let them return void instead.
-
-2006-01-25 11:32  maan
-
-       * error.h, http_send.c, net.c, para.h: convert the http sender to
-         the new error subsystem
-
-2006-01-25 10:42  maan
-
-       * client.c, command.c, crypt.c, crypt.h: rename
-         para_encrypt/para_decrypt to
-         para_encrypt_challenge/para_decrypt_challenge
-
-2006-01-25 10:35  maan
-
-       * crypt.c, error.h: convert crypt.c to the new error subsystem
-
-2006-01-25 09:48  maan
-
-       * db.c, db.h, dopey.c, error.h, mysql.c: convert db.c to the new
-         error subsystem. Move the common directory changing/restoring
-         code from dopey.c/mysql.c to db.c. Add documentation for
-         find_audio_files().
-
-2006-01-25 08:06  maan
-
-       * mp3.c: kill the 'file' parameter of find_valid_start() as
-         find_valid_start() knows it anyway
-
-2006-01-25 07:57  maan
-
-       * afs.c, afs.h, error.h: convert afs to the new error subsystem. As
-         the return values of afs_send_chunk() and afs_mainloop() are
-         never used, change them to return void
-
-2006-01-24 13:21  maan
-
-       * afs.c, afs.h, server.c: kill unused afs_handle_sighup()
-
-2006-01-24 13:11  maan
-
-       * error.h, mp3.c, ogg.c: convert ogg.c to the new error subsystem
-
-2006-01-24 11:33  maan
-
-       * error.h, mp3.c: error messages for the mp3 format handler
-
-2006-01-24 10:37  maan
-
-       * error.h: first preparations for para_server towards the new error
-         subsystem
-
-2006-01-24 10:36  maan
-
-       * Makefile.in, configure.ac: cleanup of the build system
-
-2006-01-24 07:40  maan
-
-       * afs.c: cosmetics
-
-2006-01-24 05:54  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2006-01-24 05:51  maan
-
-       * versions/: paraslash-0.2.9.tar.bz2, paraslash-0.2.9.tar.bz2.asc:
-         paraslash-0.2.9
-
-2006-01-24 05:50  maan
-
-       * Makefile.in, NEWS, configure.ac, scripts/demo-script,
-         web/index.in.html: paraslash 0.2.9
-
-2006-01-24 05:43  maan
-
-       * web/demo.in.html: kill the instructions for manual installation
-         as they do not work with paraslash-0.2.9 any more.
-
-2006-01-24 05:01  maan
-
-       * audiod.c: fix null pointer dereference in error log message
-         (s->receiver_node->receiver may well be NULL on errors)
-
-2006-01-24 03:54  maan
-
-       * README: [no log message]
-
-2006-01-24 03:16  maan
-
-       * command.c: initialize argv pointer to NULL as it is freed
-         unconditionally at the end of handle_connect()
-
-2006-01-23 14:18  maan
-
-       * FEATURES: [no log message]
-
-2006-01-23 08:52  maan
-
-       * web/license.in.html: add a link to a diff between v2 and v3 of
-         the GPL
-
-2006-01-23 08:08  maan
-
-       * Makefile.in: use para_util to make the changelog. Restrict output
-         to log messages newer than one year to reduce size
-
-2006-01-23 06:47  maan
-
-       * grab_client.c: handle short grab client writes properly according
-         to their grabbing mode: close pedantic grab clients, ignore the
-         short write for sloppy grab clients and rewrite the remaining
-         buffer in case of aggressive grab clients
-
-2006-01-23 06:44  maan
-
-       * audiod.c: make client_write() return an error on short writes,
-         always use the non-blocking write_client() instead of the
-         blocking send_buffer()
-
-2006-01-23 06:27  maan
-
-       * audiod.c: only set the filter chain error if there is a filter
-         chain. Fixes potential NULL pointer dereference.
-
-2006-01-23 06:25  maan
-
-       * grab_client.c: fix a use-after-free bug and remove the grab
-         client fd from the close_on_fork list in gc_close()
-
-2006-01-23 05:01  maan
-
-       * audiod.c, error.h, http_send.c, net.c, para.h, server.c:
-         generalize and simplify do_accept() so that it can also be used
-         by audiod. Rename it to para_accept() and add documentation as we
-         are at it.
-
-2006-01-23 04:29  maan
-
-       * gcc-compat.h, string.c: add documentation for each function in
-         string.c
-
-2006-01-23 01:54  maan
-
-       * Makefile.in: add some missing dependencies
-
-2006-01-23 01:43  maan
-
-       * mp3dec.c, ortp_send.c, wav.c: fix some signed issues noted by
-         gcc-4.0.2
-
-2006-01-23 01:39  maan
-
-       * filter.h: make gcc-4.0.2 happy
-
-2006-01-23 00:49  maan
-
-       * audiod.c, compress.c, filter_chain.c, http_send.c, oggdec.c: kill
-         some unused function parameters or mark them __unused if
-         appropriate
-
-2006-01-23 00:38  maan
-
-       * Makefile.in: add -W to DEBUG_CPPFLAGS instead of -Wunused wich
-         appeared tiwce
-
-2006-01-23 00:35  maan
-
-       * grab_client.c: fix serious typo
-
-2006-01-23 00:13  maan
-
-       * audiod.c: open_filters() exits immediately on errors, no need to
-         provide a return value
-
-2006-01-22 22:59  maan
-
-       * audioc.c, command.c, http_send.c, mp3.c, play.c, recv.c,
-         server.c, signal.c, stat.c, string.c, wav.c: change 2005 to 2006
-         in GPL header
-
-2006-01-22 22:59  maan
-
-       * NEWS: [no log message]
-
-2006-01-22 22:53  maan
-
-       * daemon.c: cosmetics
-
-2006-01-22 22:41  maan
-
-       * FEATURES: typo
-
-2006-01-22 08:13  maan
-
-       * README.mysql: [no log message]
-
-2006-01-22 07:52  maan
-
-       * FEATURES: [no log message]
-
-2006-01-22 07:19  maan
-
-       * skencil/overview.sk: update to forthcoming v0.2.9
-
-2006-01-22 05:22  maan
-
-       * command.c, server.c: shut down non-authorized connections after
-         10 seconds
-
-2006-01-22 04:41  maan
-
-       * audiod.c, filter_chain.c, recv_common.c: comment out some debug
-         messages
-
-2006-01-22 04:41  maan
-
-       * Makefile.in: format the output of cvs2cl to avoid long lines
-
-2006-01-22 04:32  maan
-
-       * Makefile.in: typo
-
-2006-01-22 04:27  maan
-
-       * Makefile.in: adjust filename of startup 'screenshots'
-
-2006-01-22 04:23  maan
-
-       * web/screenshots.in.html: adjust filename of startup 'screenshots'
-
-2006-01-22 04:21  maan
-
-       * pics/screenshots/: para_audiod-startup.txt,
-         para_server-startup.txt: recent versions
-
-2006-01-22 04:19  maan
-
-       * pics/screenshots/: para_audiod-2005-11-10.loglevel1.txt,
-         para_server-2005-10-13.loglevel1.txt: replaced by
-         para_audiod-startup.txt and para_server-startup.txt
-
-2006-01-22 03:55  maan
-
-       * audiod.c, daemon.c, para.h, server.c, string.c: make sure fd 0,
-         1, and 2 are valid. In daemon mode, dup2() these fds to /dev/null
-
-2006-01-22 03:27  maan
-
-       * audiod.c, audiod.ggo: new command line option:
-         --no_default_filters to deactivate the automatic filter
-         activation. Kill ifdefs HAVE_MAD and HAVE_OGGVORBIS as audiod now
-         supports both mp3 and ogg even if the mp3dec/oggdec filter is not
-         compiled in. Use --no_default_filters and e.g 'madplay -' as the
-         stream write command in this case.
-
-2006-01-22 00:37  maan
-
-       * ortp_recv.c: loglevel adjustment
-
-2006-01-22 00:36  maan
-
-       * audiod.c: filter_io() returns a proper error value, so set
-         fci->error accordingly
-
-2006-01-21 23:57  maan
-
-       * http_send.c: replace http_check_perms() by a saner version and
-         rename it to host_in_access_perm_list()
-
-2006-01-21 22:12  maan
-
-       * audiod.c: remove an unused variable
-
-2006-01-21 22:08  maan
-
-       * Makefile.in, NEWS, audiod.c, configure.ac, error.h, exec.c,
-         filter.c, filter_chain.c, grab_client.c, grab_client.h,
-         http_recv.c, mp3.c, mp3dec.c, net.c, oggdec.c, ortp_recv.c,
-         para.h, recv.c, recv.h, recv_common.c, signal.c, stat.c,
-         string.c: the shiny new error subsystem
-
-2006-01-21 22:07  maan
-
-       * web/download.in.html: mention that the nightly snapshot may or
-         may not compile
-
-2006-01-21 22:06  maan
-
-       * wav.c: write the wave header and the first chunk in one run if
-         output buffer can hold both
-
-2006-01-21 22:01  maan
-
-       * compress_filter.ggo: reduce default value of gain_max
-
-2006-01-18 20:39  maan
-
-       * compress.c: fix a nasty casting bug which sometimes caused an
-         output buffer overflow by 2 bytes. Also make sure we always
-         convert an even number of bytes
-
-2006-01-16 00:23  maan
-
-       * audiod.c: do not close filters as long as writer pid > 0
-
-2006-01-16 00:19  maan
-
-       * para.h: add some missing parentheses
-
-2006-01-15 18:07  maan
-
-       * audiod.c: fix nasty thinko: close_decoder_if_idle() might close
-         the receiver node, so s->receiver_node becomes NULL in this case.
-         The current code, however, saves s->receiver node in the local
-         variable 'rn' _before_ it calls close_decoder_if_idle() which
-         means that rn points to memory already freed. Fix is trivial:
-         Just call close_decoder_if_idle() at the beginning of the
-         FOR_EACH_SLOT loop
-
-2006-01-15 16:53  maan
-
-       * ortp_send.c: comment out two noisy debug messages
-
-2006-01-15 07:30  maan
-
-       * configure.ac: set mad_defs to the empty string if mad was not
-         detected
-
-2006-01-15 07:30  maan
-
-       * grab_client.c: cosmetics
-
-2006-01-15 06:56  maan
-
-       * NEWS: [no log message]
-
-2006-01-15 06:33  maan
-
-       * FEATURES, INSTALL, Makefile.in, NEWS, README, audiod.c,
-         audiod.ggo, compress.c, compress_filter.ggo, configure.ac,
-         filter.c, filter.h, filter_chain.c, grab_client.c, grab_client.h,
-         http_recv.c, mp3dec.c, oggdec.c, oggdec_filter.ggo, ortp.h,
-         ortp_recv.c, ortp_recv.ggo, ortp_send.c, para.h, recv.c, recv.h:
-         integrate para_recv and para_filter into para_audiod
-
-2006-01-15 06:27  maan
-
-       * net.c: add GPL header, use sizeof(struct ucred) instead of
-         sizeof(*cred) for the length argument of memcopy
-
-2006-01-15 06:06  maan
-
-       * audioc.c: avoid an extra <cr>
-
-2006-01-15 06:03  maan
-
-       * audiod.h: ATM, only one function prototype that is used by the
-         grabbing routines
-
-2006-01-15 06:01  maan
-
-       * recv_common.c: contains functions that are shared between
-         para_audiod and para_recv
-
-2006-01-15 06:00  maan
-
-       * grab_client.c, grab_client.ggo, grab_client.h: functions and
-         command line options for the grab command of audiod.
-
-2006-01-07 13:32  maan
-
-       * play.c: fix prebuffering
-
-2006-01-06 15:29  maan
-
-       * mp3dec.c: do not feed more than 4k to libmad
-
-2006-01-02 11:37  maan
-
-       * INSTALL, Makefile.in, configure.ac, http_recv.c, http_recv.ggo,
-         oggdec.c, ortp_recv.c, ortp_recv.ggo, recv.c, recv.ggo, recv.h:
-         receivers use their own command line parser
-
-2006-01-02 06:48  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2006-01-02 06:46  maan
-
-       * versions/: paraslash-0.2.8.tar.bz2, paraslash-0.2.8.tar.bz2.asc:
-         paraslash-0.2.8
-
-2006-01-02 06:45  maan
-
-       * Makefile.in, NEWS, configure.ac, scripts/demo-script,
-         web/index.in.html: paraslash 0.2.8
-
-2006-01-02 05:31  maan
-
-       * filter.h: make the FILTER_INIT macro work also in the unsupported
-         case
-
-2006-01-02 05:04  maan
-
-       * compress.ggo, compress_filter.ggo: rename compress.cmdline.h to
-         compress_filter.cmdline.h
-
-2006-01-02 04:50  maan
-
-       * configure.ac: Add oggdec_filter.cmdline.o to $ogg_filter_objs.
-
-2006-01-02 04:50  maan
-
-       * filter.c: Do not print help via the -L option. Use the -f
-         filtername:-h instead.
-
-2006-01-02 04:50  maan
-
-       * Makefile.in: Use the same gengetopt options for all
-         xxx_filter.ggo files.
-
-2006-01-02 04:50  maan
-
-       * compress.ggo: cosmetics.
-
-2006-01-02 04:50  maan
-
-       * filter.h: new macros DECLARE_EXTERN_FILTER_INIT and FILTER_INIT
-         filter.h
-
-2006-01-02 04:50  maan
-
-       * compress.c: replace compress.cmdline.h by
-         compress_filter.cmdline.h, simplify compress_parse_config().
-
-2006-01-02 04:49  maan
-
-       * filter_chain.c: check_filter_arg(): only check filter args if the
-         filter has a config parser.
-
-2006-01-02 04:47  maan
-
-       * oggdec.c, oggdec_filter.ggo: make the ogg output buffer size a
-         config option
-
-2005-12-31 11:53  maan
-
-       * Makefile.in, compress.c, filter.c, filter.ggo, filter.h,
-         filter_chain.c, mp3dec.c, wav.c: make the filters use their own
-         ggo command line parser
-
-2005-12-31 05:31  maan
-
-       * Makefile.in, http_recv.c, ortp_recv.c, recv.c, recv.ggo, recv.h:
-         http_recv and ortp_recv no longer depend on the args_info struct.
-         This should allow that code to be reused from within para_audiod.
-
-2005-12-31 02:25  maan
-
-       * filter.c, server.ggo: cosmetics
-
-2005-12-30 11:52  maan
-
-       * command.c: com_version(): nicify output
-
-2005-12-30 10:26  maan
-
-       * compress.c: cosmetics
-
-2005-12-30 10:19  maan
-
-       * oggdec.c: make some functions static
-
-2005-12-30 07:00  maan
-
-       * filter.ggo: the command line options for para_filter
-
-2005-12-30 06:48  maan
-
-       * INSTALL, Makefile.in, NEWS, README, compress.c, configure.ac,
-         filter.c, filter.h, filter_chain.c, mp3dec.c, oggdec.c, wav.c:
-         the new modular filter design
-
-2005-12-30 04:48  maan
-
-       * ortp_send.c: turn off jitter compensation as it also delays the
-         EOF packet
-
-2005-12-29 03:04  maan
-
-       * play.c: fix nasty bug that caused para_play to stall: We must not
-         read more than chunk_buf bytes.
-
-2005-12-27 20:31  maan
-
-       * web/index.in.html: paraslash-0.2.7
-
-2005-12-27 20:21  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-12-27 20:18  maan
-
-       * versions/: paraslash-0.2.7.tar.bz2, paraslash-0.2.7.tar.bz2.asc:
-         paraslash-0.2.7
-
-2005-12-27 20:17  maan
-
-       * Makefile.in, NEWS, configure.ac, index.html, scripts/demo-script:
-         paraslash 0.2.7
-
-2005-12-27 18:47  maan
-
-       * oggdec.c: use ov_raw_tell() to determine the initial delay in
-         case of holes in data
-
-2005-12-27 18:44  maan
-
-       * ogg.c: increase eof_timeout, reintroduce chunk_table shift
-
-2005-12-27 18:37  maan
-
-       * ortp_send.c: only reset each connection once
-
-2005-12-26 20:10  maan
-
-       * compress.ggo: better default values for gain_smooth and gain_max
-
-2005-12-26 20:03  maan
-
-       * afs.c: introduce a small delay before shutting down the senders.
-         This allows com_stat() to send 'audio_format=-1' in between
-
-2005-12-26 19:49  maan
-
-       * ortp_send.c: use rtp_session_reset() instead of
-         rtp_session_destroy()
-
-2005-12-26 19:25  maan
-
-       * daemon.c: fix minor mem leak
-
-2005-12-24 20:43  maan
-
-       * audiod.c: fix thinko in do_filter_io(): We tested ret==len (short
-         write) _AFTER_ decrementing len which triggred the memmove to
-         shift the buffer even if we had written the whole damn thing. But
-         the real bug is that the memmove was not executed in some rare
-         cases where we did have a short write. Kill that nasty bug with
-         pleasure and simplify the surrounding code a bit while we're at
-         it.
-
-2005-12-24 19:51  maan
-
-       * oggdec.c: distinguish between OV_HOLE and other errors
-
-2005-12-24 19:44  maan
-
-       * ortp_send.c: loglevel adjustment: use NOTICE loglevel for removal
-         of targets
-
-2005-12-23 23:27  maan
-
-       * audiod.c: clear play_time when stopped
-
-2005-12-23 23:26  maan
-
-       * ortp_send.c: turn off libortp's debug logging
-
-2005-12-23 23:23  maan
-
-       * http_recv.c, ortp_recv.c: para_log() is already declared in
-         para.h
-
-2005-12-23 23:00  maan
-
-       * CREDITS: add Jaroslav Kysela
-
-2005-12-23 22:41  maan
-
-       * command.c: com_stat(): only print mtime if we have a valid audio
-         file
-
-2005-12-23 22:40  maan
-
-       * afs.c: new function afs_eof() to be called when the 'next' flag
-         is set. Reset audio file info, dbinfo etc.
-
-2005-12-23 22:36  maan
-
-       * server.c: also refresh status after afs_send() if neccessary
-
-2005-12-23 19:34  maan
-
-       * command.c: get_status(): always use the copy of the mmd struct as
-         we do not hold the mmd_lock
-
-2005-12-23 19:12  maan
-
-       * command.c: com_stat(): kill an unused variable
-
-2005-12-22 23:48  maan
-
-       * http_send.c: kill unused http_send_header(). http_send(): the
-         current_chunk arg _is_ used, so nuke the __unused annotation
-
-2005-12-22 18:37  maan
-
-       * http_send.c: cosmetics
-
-2005-12-22 04:55  maan
-
-       * play.ggo, recv.ggo: typo
-
-2005-12-22 02:19  maan
-
-       * http_send.c: cosmetics
-
-2005-12-22 02:03  maan
-
-       * audiod.c: store last death time instead of last start time for
-         each format and use it together with the new MIN_PENALTY_MS to
-         avoid immediate restart of the (wrong) decoder
-
-2005-12-22 01:59  maan
-
-       * http_send.c: fix send-double-header-sent bug
-
-2005-12-21 22:12  maan
-
-       * server.c: kill an unused variable, add debug message
-
-2005-12-21 22:12  maan
-
-       * audiod.c: The previous change was a brain fart. Revert it.
-
-2005-12-21 22:11  maan
-
-       * audiod.ggo: reduce default stream delay to 200ms
-
-2005-12-21 17:09  maan
-
-       * audiod.c: wait for the audio format to be resent by para_server
-         before restarting the stream reader
-
-2005-12-21 02:28  maan
-
-       * recv.c: cosmetics
-
-2005-12-21 01:43  maan
-
-       * recv.c: add GPL header, whitespace cleanup
-
-2005-12-20 22:48  maan
-
-       * INSTALL, NEWS, README, audiod.c, audiod.ggo, http_recv.c: replace
-         para_http_recv and para_ortp_recv by para_recv
-
-2005-12-20 22:36  maan
-
-       * NEWS: [no log message]
-
-2005-12-20 22:36  maan
-
-       * play.c, string.c: cosmetics
-
-2005-12-20 22:35  maan
-
-       * list.h: reintroduce list_add_tail() as it is needed by
-         http_send.c
-
-2005-12-20 22:34  maan
-
-       * mp3.c: write chunk info as 'num_chunks x chunk_time' as this info
-         is read by audiod, increase eof time
-
-2005-12-20 22:34  maan
-
-       * ogg.c: write chunk info as 'num_chunks x chunk_time' as this info
-         is read by audiod. comment out some rather strange workaround
-         code in tunetable()
-
-2005-12-20 22:31  maan
-
-       * audiod.ggo: specify delay in milliseconds
-
-2005-12-20 22:30  maan
-
-       * oggdec.c: prebuffer data if hole was detected, increase output
-         buffer size
-
-2005-12-20 22:26  maan
-
-       * audiod.c: read the chunk time from server info and push that time
-         to the executed commands via the new CHUNK_TIME() macro. Use
-         milliseconds for the stream delay. Some misc fixes also
-
-2005-12-20 22:19  maan
-
-       * http_recv.c: rewrite to fit into the new receiver struct which is
-         also used by the ortp receiver. This should ease the inclusion
-         into para_audiod
-
-2005-12-20 22:16  maan
-
-       * ortp_recv.c: almost a complete rewrite. The inclusion into
-         para_recv, the update to ortp-0.8.x  and the new ORTP_EMPTY
-         packet type made this neccessary. Should chew less CPU now
-
-2005-12-20 22:12  maan
-
-       * http_send.c: implement a per-client packet queue that holds
-         packets which can not be sent out immediately
-
-2005-12-20 22:10  maan
-
-       * afs.c: always call the senders, even if there is no data to send.
-         This allows the senders to send out buffered data
-
-2005-12-20 22:08  maan
-
-       * recv.ggo: combined ortp_recv.ggo and http_recv.ggo, slightly
-         modified
-
-2005-12-20 22:07  maan
-
-       * http_recv.ggo, ortp_recv.ggo: these went to recv.ggo
-
-2005-12-20 22:05  maan
-
-       * Makefile.in, configure.ac, recv.c, recv.h: this combines
-         para_ortp_recv and para_http_recv into one binary
-
-2005-12-20 22:03  maan
-
-       * ortp.h, ortp_send.c: use the new ORTP_EMPTY packet to indicate
-         that no data packet is going to be sent right now, compute the
-         timestamp of the packet always by multiplying chunk_time and
-         chunks_sent instead of asking the ortp library, turn off
-         scheduling mode
-
-2005-12-17 22:16  maan
-
-       * compress.c: kill some dead code
-
-2005-12-15 15:26  maan
-
-       * ortp_send.c: use rtp_session_time_to_ts() instead of
-         rtp_session_get_current_send_ts() for computing the timestamp
-
-2005-12-15 15:24  maan
-
-       * afs.c: compute offset after reading the first chunk
-
-2005-12-08 16:02  maan
-
-       * web/footer.html: comment out ssi for last modification date.
-
-2005-12-08 12:52  maan
-
-       * afs.c: add some docu
-
-2005-12-08 12:52  maan
-
-       * mysql.c: com_streams(): If called with arg 'current_stream',
-         print the name of the current stream. This info is also availabe
-         by calling cs (change stream) with no args, but cs is restricted
-         to users with DB_WRITE privileges..
-
-2005-12-08 12:48  maan
-
-       * play.c: prebuffer data if start_time is not reached rather than
-         simply sleep until that time. Add some documentation
-
-2005-11-28 23:05  maan
-
-       * ortp_send.c: destroy session also in case EOF notifier could not
-         be sent
-
-2005-11-28 23:04  maan
-
-       * web/demo.in.html: mention -Z option for mpg123
-
-2005-11-23 16:42  maan
-
-       * web/contact.in.html: [no log message]
-
-2005-11-23 16:41  maan
-
-       * server.c: Oops. Fix random number generation.
-
-2005-11-15 14:48  maan
-
-       * sdl_gui.c: fix zombie bug
-
-2005-11-13 19:57  maan
-
-       * Makefile.in: add ChangeLog to phony targets. This way it gets
-         always rebuild.
-
-2005-11-13 19:29  maan
-
-       * Makefile.in: modify rule for PUBLIC_KEY so that it also applies
-         to key.anonymous
-
-2005-11-13 19:16  maan
-
-       * Makefile.in: add anonyous key to web_misc
-
-2005-11-13 19:11  maan
-
-       * key.anonymous: for www.paraslash.org
-
-2005-11-13 18:47  maan
-
-       * Makefile.in: move some stuff around, make web/sync, use sk2ps
-
-2005-11-13 18:17  maan
-
-       * pics/screenshots/: gui-2005-11-12.png,
-         para_audiod-2005-11-10.loglevel1.txt,
-         para_server-2005-10-13.loglevel1.txt: new screenshots
-
-2005-11-13 18:13  maan
-
-       * web/: contact.in.html, demo.in.html, documentation.in.html,
-         download.in.html, footer.html, header.html, index.in.html,
-         license.in.html, screenshots.in.html: new web pages
-
-2005-11-13 18:04  maan
-
-       * Makefile.in: add target www that replaces the old html target
-
-2005-11-13 04:00  maan
-
-       * dopey.c: add some more comments
-
-2005-11-13 03:59  maan
-
-       * CREDITS, FEATURES, INSTALL, NEWS, README, README.mysql: minor
-         corrections and cleanups
-
-2005-11-13 03:58  maan
-
-       * gcc-compat.h: make gcc 3.3.5 happy
-
-2005-11-10 23:28  maan
-
-       * afs.c, audiod.c, command.c, dopey.c, play.c, string.c: add some
-         missing includes
-
-2005-11-10 23:28  maan
-
-       * Makefile.in: tell gcc to bail out on implicit function
-         declarations
-
-2005-11-10 23:27  maan
-
-       * gui.c: para_gui does not kill any decoders. Remove obsolete help
-         text.
-
-2005-11-10 23:25  maan
-
-       * README: [no log message]
-
-2005-11-07 21:36  maan
-
-       * net.c, para.h: remove some incdudes which are not needed and move
-         inclusion ofnetdb.h from para.h to net.c
-
-2005-11-07 21:15  maan
-
-       * dbadm.c: whitespace cleanup
-
-2005-11-07 16:53  maan
-
-       * para.h: kill unused para_open_fifo()
-
-2005-11-07 16:28  maan
-
-       * stat.c: shutdown stat_client connection if corresponding fd is
-         not ready for writing
-
-2005-11-07 13:34  maan
-
-       * gcc-compat.h: remove the __const macro. It is not used by
-         paraslash, but alsa seems to use it for something else so gcc
-         produces the following warning: passing arg 4 of __assert_fail
-         discards qualifiers from pointer target type
-
-2005-11-06 19:08  maan
-
-       * index.html: typo
-
-2005-11-06 18:45  maan
-
-       * Makefile.in: make it work again for gcc-2.95.4
-
-2005-11-06 18:43  maan
-
-       * server.c: change #if HAVE_MYSQL to #ifdef HAVE_MYSQL
-
-2005-11-06 18:42  maan
-
-       * command.c, para.h: malloc.h is only needed from command.c, so
-         move the include from para.h to command.c
-
-2005-11-06 18:41  maan
-
-       * index.html: add icon, nicify layout, use :hover to highlight
-         links
-
-2005-11-06 18:37  maan
-
-       * pics/web/paraslash.ico: icon resource file
-
-2005-11-06 18:35  maan
-
-       * list.h: include stddef.h, kill unused macros
-
-2005-11-06 18:34  maan
-
-       * audiod.c: only list.h needs stddef.h, so include it from there
-
-2005-11-06 18:31  maan
-
-       * INSTALL: [no log message]
-
-2005-11-06 15:41  maan
-
-       * NEWS: [no log message]
-
-2005-11-06 15:41  maan
-
-       * Makefile.in: use common rule to let all object files depend on
-         gcc-compat.h and on para.h, add some more -W flags
-
-2005-11-06 14:53  maan
-
-       * daemon.c: fix warning: getpwnam discards qualifiers from pointer
-         target type
-
-2005-11-06 14:46  maan
-
-       * audiod.c: include gcc-compat.h and mark unused args as such.
-         Also, change third arg of accept() to proper type socklen_t*
-
-2005-11-06 14:42  maan
-
-       * client.c: include gcc-compat.h and mark arg of sigint_handler()
-         as unused
-
-2005-11-06 14:42  maan
-
-       * command.c: include gcc-compat.h and use __unused macros for
-         commands that take no args or do not output anything. Be a little
-         more anal and return -E_SYNTAX for commands that take no args in
-         case args were given
-
-2005-11-06 14:39  maan
-
-       * dbadm.c, audioc.c: include gcc-compat.h and mark args of dummy
-         para_log() as unused
-
-2005-11-06 14:37  maan
-
-       * db.c, dopey.c: kill unused arg dir, include and use gcc-compat.h
-
-2005-11-05 23:31  maan
-
-       * fade.c: include gcc-compat.h and mark loglevel arg of para_log()
-         as unused
-
-2005-11-05 23:30  maan
-
-       * http_send.c: include gcc-compat.h and use __unused macros for
-         unused args of http_send() (those are used by the ogg audio
-         format handler so we cannot simply kill the unused args),
-         http_handle_fds() no longer takes pointer to max_fileno. Mark
-         unused args of com_on() and com_off()
-
-2005-11-05 23:26  maan
-
-       * oggdec.c: use int main(void) as all args are unused
-
-2005-11-05 23:25  maan
-
-       * list.h: kill empty prefetch()
-
-2005-11-05 23:24  maan
-
-       * mysql.c: include gcc-compat.h and use __unused macros for
-         commands that take no args. Be a little more anal and return
-         -E_SYNTAX for commands that take no args in case args were given
-
-2005-11-05 23:22  maan
-
-       * net.c: include gcc-compat.h and use __printf and __must_check
-         macros
-
-2005-11-05 23:20  maan
-
-       * mp3dec.c: fix signedness bug, use int main(void) as all args are
-         unused
-
-2005-11-05 23:15  maan
-
-       * ortp_recv.c: include gcc-compat.h and use __printf macro
-
-2005-11-05 23:14  maan
-
-       * para.h: kill struct gui_command which was moved to gui.c, include
-         gcc-compat.h and use __must_check and __malloc macros
-
-2005-11-05 23:12  maan
-
-       * sdl_gui.c: use C99 array initializers, include gcc-compat.h and
-         mark loglevel arg of para_log() as unused
-
-2005-11-05 23:11  maan
-
-       * send.h: handle_fds() does no longer take a pointer to max_fileno
-
-2005-11-05 23:11  maan
-
-       * server.c: use macros from gcc-compat.h, move para_log(), no need
-         to init fd in init_random_seed(), handle_fds() does no longer
-         take a pointer to max_fileno
-
-2005-11-05 22:59  maan
-
-       * string.c: add __malloc and __must_check
-
-2005-11-05 22:57  maan
-
-       * gui.c: commands do not need to know the key which was pressed to
-         invoke them, just kill the arg. Move struct gui_command from
-         para.h to gui.c, use new __printf macro
-
-2005-11-05 17:22  maan
-
-       * gcc-compat.h: add __printf
-
-2005-11-05 17:19  maan
-
-       * afs.c, afs.h, mp3.c, ogg.c: no need to pass FILE pointer to
-         close_audio_file() as the audio format handlers know it anyway.
-         Kill unused function parameter raw_total in
-         ogg_compute_chunk_table()
-
-2005-11-05 17:12  maan
-
-       * index.html: new design with menu on the left
-
-2005-11-05 16:14  maan
-
-       * FEATURES: [no log message]
-
-2005-11-04 18:05  maan
-
-       * index.html: html links to directories should have a trailing
-         slash
-
-2005-11-01 13:57  maan
-
-       * http_send.c: fix dangerous typo in case of invalid get request
-
-2005-10-31 22:57  maan
-
-       * stat.c: use ISO C99 array initializers
-
-2005-10-31 22:56  maan
-
-       * Makefile.in: fix dependencies on close_on_fork.h, add
-         dependencies on new gcc-compat.h
-
-2005-10-31 22:53  maan
-
-       * audioc.c: add GPL header
-
-2005-10-31 22:05  maan
-
-       * client.c, command.c, crypt.c, net.c: fix some gcc-4 signedness
-         warnings regarding char vs unsigned char
-
-2005-10-31 22:05  maan
-
-       * play.c: fix gcc-4 signedness warning
-
-2005-10-31 22:02  maan
-
-       * audiod.c: use __noreturn gcc extenstion where appropriate which
-         allows to kill some bogus 'return 42' statements. Rename .buflen
-         of struct stream_io to .loaded (as in struct filter)
-
-2005-10-31 21:59  maan
-
-       * gcc-compat.h: beautified GCC extensions that define away for
-         non-gcc compilers and gcc 2. Kudos to Rob Love who mentioned that
-         at kernelplanet
-
-2005-10-29 22:19  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs, turn on debugging
-
-2005-10-29 22:16  maan
-
-       * versions/: paraslash-0.2.6.tar.bz2, paraslash-0.2.6.tar.bz2.asc:
-         paraslash-0.2.6
-
-2005-10-29 22:14  maan
-
-       * NEWS, configure.ac, index.html, scripts/demo-script: paraslash
-         0.2.6
-
-2005-10-29 21:18  maan
-
-       * Makefile.in: turn off debugging (-g)
-
-2005-10-29 18:32  maan
-
-       * command.c: cosmetics
-
-2005-10-29 18:32  maan
-
-       * compress.ggo: increase default values of gain_max and gain_smooth
-
-2005-10-28 21:46  maan
-
-       * command.c: cosmetics
-
-2005-10-28 21:38  maan
-
-       * command.c: mention the afs status bits in the help text of the
-         relevant commands
-
-2005-10-28 20:07  maan
-
-       * mysql.c: add some examples
-
-2005-10-28 18:12  maan
-
-       * mysql.c: typo
-
-2005-10-28 18:04  maan
-
-       * command.c: use 'audio file' instead of 'song'
-
-2005-10-28 17:56  maan
-
-       * afs.c, db.h, dopey.c, mysql.c: use 'audio file' instead of 'song'
-         also in struct dbtool
-
-2005-10-28 17:50  maan
-
-       * mysql.c: use 'audio file' instead of 'song', extend docu of ls
-
-2005-10-28 17:35  maan
-
-       * mysql.c: improve mbox docu
-
-2005-10-28 12:42  maan
-
-       * INSTALL, NEWS: [no log message]
-
-2005-10-27 20:48  maan
-
-       * crypt.h, crypt.c: fix debian compiler warning: passing arg 2 of
-         RSA_public_encrypt discards qualifiers from pointer target type
-
-2005-10-27 20:36  maan
-
-       * README: replace dead xiph link
-
-2005-10-27 20:25  maan
-
-       * INSTALL, README: [no log message]
-
-2005-10-27 19:55  maan
-
-       * index.html: mention new manual pages
-
-2005-10-27 19:32  maan
-
-       * mysql.c: typo
-
-2005-10-27 19:28  maan
-
-       * command.c: reformat help text of com_help
-
-2005-10-27 19:04  maan
-
-       * Makefile.in, index.html: include man pages that replace
-         manual.txt
-
-2005-10-27 18:29  maan
-
-       * 1.0, NEWS, command.c, mysql.c: further documentation cleanup
-
-2005-10-27 00:50  maan
-
-       * exec.c: kill unused para_open_fifo()
-
-2005-10-27 00:23  maan
-
-       * audiod.c, command.c, dopey.c, mysql.c: reformat, beautify and
-         improve documentation
-
-2005-10-26 01:37  maan
-
-       * net.c: whitespace cleanup
-
-2005-10-25 04:02  maan
-
-       * command.c: fix status update on db_write commands
-
-2005-10-24 01:26  maan
-
-       * client.c: kill debug message
-
-2005-10-23 21:16  maan
-
-       * audiod.c: handle short writes for grab clients properly according
-         to grab mode, logging improvements
-
-2005-10-23 20:50  maan
-
-       * audioc.c: use recv_bin_buffer() instead of recv_buffer(). Fixes
-         grab command
-
-2005-10-23 17:04  maan
-
-       * audiod.c, compress.c, dopey.c, para.h, string.c: introduce
-         para_calloc() and use it where aprropriate
-
-2005-10-23 16:57  maan
-
-       * NEWS: typo
-
-2005-10-23 16:37  maan
-
-       * client.c: kill unused function get_exit_status(), make two other
-         functions static
-
-2005-10-23 05:13  maan
-
-       * NEWS: [no log message]
-
-2005-10-23 04:56  maan
-
-       * stat.c: add GPL header
-
-2005-10-23 04:55  maan
-
-       * command.c, server.c: init random seed in parent, rather that in
-         each client. As reads of /dev/random may block, clients were
-         blocked under load if many connections arrived at the same time.
-
-2005-10-23 04:41  maan
-
-       * Makefile.in: add two missing header files to the tarball
-
-2005-10-23 04:07  maan
-
-       * audiod.c, para.h, stat.c: use macros from list.h for stat_client
-         list rather than homebrewed list logic, some smallish cleanups
-         also.
-
-2005-10-23 02:03  maan
-
-       * Makefile.in, audiod.c, stat.c: add stream readers/writers/filters
-         as well as grab/stat clients to the list of fds to be closed in
-         para_exec()
-
-2005-10-22 23:36  maan
-
-       * Makefile.in, audiod.c, exec.c: para_exec(): close fds on
-         close_on_fork after forking. Mark audiod's local socket fd for
-         close after fork
-
-2005-10-22 23:09  maan
-
-       * Makefile.in, http_send.c, server.c: use new close_on_fork list to
-         mark fds for closing in the child after fork
-
-2005-10-22 23:07  maan
-
-       * close_on_fork.c, close_on_fork.h: implement list of fds that
-         should be closed in the child after fork together with simple
-         functions to add/delete fds from that list as well a a function
-         that closes all fds on the list
-
-2005-10-22 20:18  maan
-
-       * gui.c: speed up display of command output by refreshing the
-         screen only after it is completely filled or there is no more
-         output available
-
-2005-10-22 19:58  maan
-
-       * gui.c, net.c, para.h, string.c: new macro: PARA_VSPRINTF, a
-         user-friendly and secure version of vsprintf(). Replace all
-         occurences of vsprintf by PARA_VSPRINTF, thus fixing some
-         potential buffer overflows in gui.c. Moreover, the new macro
-         allows to get rid of some duplicate code in make_message() and
-         send_va_buffer()
-
-2005-10-22 19:57  maan
-
-       * fade.c: simplify para_log()
-
-2005-10-22 19:48  maan
-
-       * NEWS: [no log message]
-
-2005-10-22 18:36  maan
-
-       * audiod.c: nuke another unused variable
-
-2005-10-22 18:33  maan
-
-       * audiod.c: nuke unused variable rb_bytes
-
-2005-10-22 17:28  maan
-
-       * client.c, command.c, para.h: major para_client cleanup: remove
-         sigchild-handling, change some old crufty code to use
-         make_message() and para_strcat(), improve SIGINT handling,
-         #define CHALLENGE_RESPONSE_MSG in para.h and use it from server
-         and client rather than harcoding the message text.
-
-2005-10-22 15:55  maan
-
-       * client.c: fix interactive mode (only the first command worked)
-
-2005-10-22 15:48  maan
-
-       * 1.0, Makefile.in, NEWS, audioc.c, audiod.c, client.c, client.ggo,
-         command.c, crypt.c, crypt.h, http_recv.c, net.c, para.h, rc4.h:
-         implement crypted connections by using openssl's rc4 stream
-         cipher algorithm
-
-2005-10-20 15:26  maan
-
-       * command.c: kill outdated comment
-
-2005-10-19 15:22  maan
-
-       * net.c: remove unused pass_fd()
-
-2005-10-19 15:18  maan
-
-       * net.c, para.h: make do_bind() static
-
-2005-10-19 15:17  maan
-
-       * net.c, para.h: make setserversockopts() static
-
-2005-10-19 11:52  maan
-
-       * server.h: whitespace cleanup
-
-2005-10-19 11:51  maan
-
-       * Makefile.in, NEWS, afs.c, command.c, db.c, db.h, dopey.c,
-         http_send.c, mp3.c, mysql.c, ogg.c, ortp_send.c, server.c,
-         server.h: major header cleanup
-
-2005-10-19 11:50  maan
-
-       * afs.h: contains afs related stuff, pulled out from server.h
-
-2005-10-17 09:45  maan
-
-       * SFont.c: cosmetics
-
-2005-10-17 07:54  maan
-
-       * Makefile.in, NEWS, README.mysql, client.c, dopey.c, mysql.c,
-         server.ggo, server.h: implement, use and document an internal
-         find command and get rid of exec.o in para_server
-
-2005-10-17 07:53  maan
-
-       * db.c, db.h: functions common to all database tools. ATM, only the
-         new internal find command
-
-2005-10-17 02:27  maan
-
-       * SFont.c: remove superflous include
-
-2005-10-16 22:15  maan
-
-       * net.c: cosmetics
-
-2005-10-16 22:13  maan
-
-       * INSTALL: [no log message]
-
-2005-10-14 10:10  maan
-
-       * krell.c: replace old email address
-
-2005-10-14 10:10  maan
-
-       * http_send.c: loglevel adjustment
-
-2005-10-14 05:23  maan
-
-       * README.mysql: whitespace cleanup
-
-2005-10-14 01:15  maan
-
-       * http_send.c, ortp_send.c, send.h, server.c: as senders can be
-         controlled at runtime, there is no need to panic on
-         sender-related syntax errors in command line args or  in the
-         config file. Just ignore invalid options and make corresponding
-         functions return void.
-
-2005-10-14 00:54  maan
-
-       * afs.c, mp3.c, ogg.c, server.c, server.h: afs_init is always
-         successful. Change it to return void.
-
-2005-10-14 00:45  maan
-
-       * afs.c, command.c, server.h: the set of supported audio formats is
-         known at compile time. So make it a preproccessor macro and
-         remove the function that computed this constant value. Also
-         introduce an array-version of all supported audio formats.
-
-2005-10-14 00:07  maan
-
-       * index.html: typo
-
-2005-10-13 23:00  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-10-13 22:56  maan
-
-       * versions/: paraslash-0.2.5.tar.bz2, paraslash-0.2.5.tar.bz2.asc:
-         paraslash-0.2.5
-
-2005-10-13 22:55  maan
-
-       * NEWS, configure.ac, index.html, scripts/demo-script: paraslash
-         0.2.5
-
-2005-10-13 22:34  maan
-
-       * mysql.c: make debian's gcc-2.95.4 happy
-
-2005-10-13 20:56  maan
-
-       * server.c: another small memory leak
-
-2005-10-13 20:56  maan
-
-       * ogg.c: fix a memory leak
-
-2005-10-13 10:37  maan
-
-       * audioc.c, para.h, string.c: move concat_args() from string.c to
-         audioc.c. As it is only used there, make it static
-
-2005-10-13 10:33  maan
-
-       * daemon.c, string.c: move uptime_str() from string.c to daemon.c.
-         It is only used by para_server and by para_audiod
-
-2005-10-13 10:14  maan
-
-       * http_send.c: cosmetics
-
-2005-10-13 10:05  maan
-
-       * http_recv.c: decrease buffer size: 8k should be enough.
-
-2005-10-13 09:45  maan
-
-       * http_recv.c: add GPL header
-
-2005-10-13 09:00  maan
-
-       * exec.c, para.h, string.c: move split_args() from exec.c to
-         string.c. Each caller of split_args() links string.o anyway.
-         Moreover, para_server needs it, and the plan is to make
-         para_server fully single-threaded..
-
-2005-10-13 08:50  maan
-
-       * string.c: no need to include server.h. para.h is enough
-
-2005-10-13 07:35  maan
-
-       * command.c, http_send.c, ortp_send.c, send.h: add sender-specific
-         help commands
-
-2005-10-13 07:28  maan
-
-       * NEWS: [no log message]
-
-2005-10-13 05:29  maan
-
-       * ogg.c: free buffers on song exit, take packet-end time into
-         aacount when computing the chunk table (should fix dropouts at
-         the beginnin)
-
-2005-10-13 03:21  maan
-
-       * audiod.c: fix segfault in get_empty_slot() for invalid
-         stream_read commands
-
-2005-10-13 00:19  maan
-
-       * audiod.c: simplify check_sigchld(), fix potential segfault on
-         server shutdown
-
-2005-10-12 22:02  maan
-
-       * skencil/overview.sk: nuke para_send, change version to 0.2.5
-
-2005-10-11 21:15  maan
-
-       * oggdec.c: kill bogus usleep, increase bufsize to 64k, handle
-         short writes
-
-2005-10-11 21:03  maan
-
-       * ortp_send.c: check return values from rtp_session_send_with_ts()
-         and shut down target on errors
-
-2005-10-11 04:52  maan
-
-       * gui.c: fix memory leak in client_cmd_cmdline()
-
-2005-10-11 03:27  maan
-
-       * ortp_send.c: fix null pointer bug in com_si()
-
-2005-10-11 03:26  maan
-
-       * audiod.c: kill reader, writer and filters on stream timeout, fix
-         close_unused_stream_fds() in case stream writer dies first
-
-2005-10-10 07:30  maan
-
-       * ortp_send.c: cosmetics
-
-2005-10-10 06:08  maan
-
-       * mysql.c: fix a memory leak in get_query()
-
-2005-10-10 06:04  maan
-
-       * http.h, http_recv.c, http_send.c, net.c, para.h: send/recv proper
-         get request, not just a minimal one, so para_http_recv works also
-         with other http streamers like poc-http
-
-2005-10-10 06:00  maan
-
-       * Makefile.in: add missing dependencies of http_recv, add new
-         header files for the tarball, use -O0 optimization -- it produces
-         smaller binaries (on my current system) and valgrind likes it too
-
-2005-10-10 05:58  maan
-
-       * INSTALL: [no log message]
-
-2005-10-10 03:23  maan
-
-       * audiod.c: fix memory leak in handle_connect()
-
-2005-10-10 03:20  maan
-
-       * ortp_send.c: whitespace cleanup
-
-2005-10-10 03:18  maan
-
-       * ortp_recv.c: include ortp_recv.cmdline.h instead of former
-         recv.cmdline.h
-
-2005-10-10 03:17  maan
-
-       * server.c: fix memory leak in parse_config(), replace sprintf() by
-         make_message()
-
-2005-10-10 03:15  maan
-
-       * exec.c: fix memory leak in para_exec_cmdline_pid()
-
-2005-10-10 03:14  maan
-
-       * command.c: fix memory leak in com_si()
-
-2005-10-10 03:13  maan
-
-       * NEWS: [no log message]
-
-2005-10-07 10:15  maan
-
-       * Makefile.in: kill para_send-related targets
-
-2005-10-07 01:41  maan
-
-       * send.c, send.ggo: no more need for the stand-alone sender
-
-2005-10-07 01:35  maan
-
-       * audiod.c, audiod.ggo: make para_http_recv the default receiver
-
-2005-10-07 01:33  maan
-
-       * Makefile.in, configure.ac: change para_recv to para_ortp_recv
-
-2005-10-07 01:20  maan
-
-       * ortp_recv.c, ortp_recv.ggo: former recv.c and recv.ggo
-
-2005-10-07 01:20  maan
-
-       * recv.c, recv.ggo: renamed to ortp_recv.c and ortp_recv.ggo
-
-2005-10-07 01:16  maan
-
-       * ortp_send.c, server.ggo: new option: ortp_header_interval
-
-2005-10-07 01:15  maan
-
-       * afs.c: remove bogus check, only call the send functions if buffer
-         is non-empty
-
-2005-10-07 01:14  maan
-
-       * INSTALL, NEWS: [no log message]
-
-2005-10-04 01:31  maan
-
-       * afs.c: kill unused eof_time
-
-2005-10-04 00:10  maan
-
-       * afs.c: remove outdated comment
-
-2005-10-04 00:10  maan
-
-       * http_send.c, mp3.c, ogg.c, server.h: remove send_header function
-         pointer from struct audio format
-
-2005-10-03 21:59  maan
-
-       * afs.c: kill send_function(). It's kinda pointless.
-
-2005-10-03 21:28  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: remove fifo. fifoname and sw_pid
-         from struct audio format
-
-2005-10-03 21:19  maan
-
-       * Makefile.in: afs depends on send.h
-
-2005-10-03 21:19  maan
-
-       * server.ggo: remove option stream_write_cmd
-
-2005-10-03 21:18  maan
-
-       * afs.c: kill some dead code: af->sw_pid is always -1
-
-2005-10-03 20:48  maan
-
-       * afs.c, server.h: kill open_cmd in struct audio info, kill
-         setup_stream_command()
-
-2005-10-03 20:47  maan
-
-       * command.c: increase event counter on pause
-
-2005-10-03 19:04  maan
-
-       * afs.c, command.c, server.h: kill the AFS_STREAM_WRITER flag
-
-2005-09-30 10:12  maan
-
-       * index.html: cosmetics
-
-2005-09-30 05:23  maan
-
-       * afs.c: fix paused mode: only set eof_barrier once
-
-2005-09-30 05:07  maan
-
-       * afs.c: fix status flags on eof when AFS_NOMORE is set
-
-2005-09-30 04:21  maan
-
-       * afs.c: sw_restart_barrier is no longer needed
-
-2005-09-30 04:18  maan
-
-       * afs.c: kill unused SW_RESTART_TIME
-
-2005-09-30 04:17  maan
-
-       * afs.c: kill unneeded sw_restart_tv
-
-2005-09-30 04:13  maan
-
-       * afs.c, server.c: there are no more children to handle for afs
-
-2005-09-30 03:58  maan
-
-       * server.c: only call afs_send_chunk() if select timeout expired
-
-2005-09-30 03:48  maan
-
-       * afs.c: kill afs_open_fifo(). It has no more callers
-
-2005-09-30 03:32  maan
-
-       * mp3.c, ogg.c, server.h: kill the mainloops themselves
-
-2005-09-30 03:16  maan
-
-       * afs.c: kill the call to the mainloop of audio format handlers as
-         it is no longer needed
-
-2005-09-30 02:45  maan
-
-       * afs.c: reset mmd->chunks_sent on sender shutdown rather than on
-         stream writer death
-
-2005-09-30 02:21  maan
-
-       * afs.c: compute data send barrier on repos and after sucessfully
-         getting the next audio file rather than after stream writer exec
-
-2005-09-30 01:09  maan
-
-       * afs.c, server.c: afs_compute_timeout: return -1 rather than 1
-         when chunk is overdue, otherwise select blocks on bof. server.c:
-         No more need to check the stream writer fd. It is no longer used.
-
-2005-09-29 23:58  maan
-
-       * afs.c: introduce extra check in afs_send_chunk() that tests
-         whether chunk is really due. This was previously broken: Whenever
-         select returned we sent a chunk which might happen too early. Not
-         serious though as the time got corrected automatically during the
-         next call to afs_mainloop()
-
-2005-09-29 23:41  maan
-
-       * afs.c: do not reset audio_format in afs_send_chunk() if eof was
-         encountered as this causes the audio file not to be closed in the
-         following call to afs_mainloop(). Also, invalidate af pointer
-         after closing the audio file
-
-2005-09-29 23:17  maan
-
-       * mp3.c: kill two unused functions
-
-2005-09-29 23:13  maan
-
-       * afs.c, mp3.c, ogg.c: call the new close_audio_file function from
-         afs.c rather than closing the file in the mainloop of the audio
-         format handler
-
-2005-09-29 23:02  maan
-
-       * mp3.c, ogg.c, server.h: new function pointer in struct audio
-         format: close_audio_file
-
-2005-09-29 22:48  maan
-
-       * afs.c, mp3.c, ogg.c: compute mmd->offset in afs.c rather than in
-         the audio format handlers
-
-2005-09-29 12:01  maan
-
-       * FEATURES, README: update docu wrt internal senders
-
-2005-09-29 09:56  maan
-
-       * afs.c: simplify AFS_NOMORE handling
-
-2005-09-29 09:15  maan
-
-       * afs.c, ogg.c: kill three unused variables
-
-2005-09-29 09:13  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: nuke function parameter eof_time
-         which is no longer used
-
-2005-09-29 09:06  maan
-
-       * afs.c: compute eof_barrier from new eof field in struct audio
-         format. Also, do not base computation on stream writer death time
-         but on sender shutdown time
-
-2005-09-29 08:39  maan
-
-       * mp3.c, ogg.c, server.h: new element in struct audio format:
-         eof_tv
-
-2005-09-29 08:22  maan
-
-       * afs.c, mp3.c, ogg.c: move repositioning handling from audio file
-         handlers to afs.c
-
-2005-09-29 07:59  maan
-
-       * mp3.c, ogg.c, server.h: new function pointer in struct audio
-         format: reposition_stream
-
-2005-09-29 07:50  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: remove calculation of next chunk
-         time from format handlers
-
-2005-09-29 07:44  maan
-
-       * afs.c: kill unused variable
-
-2005-09-29 07:42  maan
-
-       * afs.c: do calculation of next chunk time in afs.c rather than in
-         the audio format handlers
-
-2005-09-29 06:30  maan
-
-       * ogg.c: oops, we are not using milliseocnds..
-
-2005-09-29 03:10  maan
-
-       * mp3.c, ogg.c, server.h: new element in struct audio format:
-         chunk_tv that is filled in by the audio format handler
-
-2005-09-29 03:09  maan
-
-       * Makefile.in: add more missing dependencies
-
-2005-09-29 01:29  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: kill send_chunk
-
-2005-09-29 01:24  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: new function pointer in struct
-         audio format: read_chunk which is going to replace send_chunk as
-         the sending functions are better invoked from afs.c
-
-2005-09-29 01:14  maan
-
-       * string.c: typos
-
-2005-09-28 23:48  maan
-
-       * afs.c, http_send.c, mp3.c, ogg.c, send.h, server.c, server.ggo,
-         server.h: include support for the new ortp sender
-
-2005-09-28 23:48  maan
-
-       * net.c: log short writes
-
-2005-09-28 23:45  maan
-
-       * command.c: off by one bug
-
-2005-09-28 23:44  maan
-
-       * recv.c: use enums from new ortp.h
-
-2005-09-28 23:43  maan
-
-       * recv.ggo: change default host from XINGTV.MCAST.NET to the
-         officially unasaigned ip 224.0.1.38 which happens to resolve to
-         DANTZ.MCAST.NET. Change default port to 1500
-
-2005-09-28 23:39  maan
-
-       * Makefile.in, configure.ac: build the ortp sender if libortp was
-         found, add some missing dependencies
-
-2005-09-28 23:37  maan
-
-       * ortp.h, ortp_send.c: the new internal ortp sender
-
-2005-09-28 07:09  maan
-
-       * http_send.c, mp3.c, ogg.c, server.h: new function pointer in
-         struct audio_format: get_header_info. Use it in http_send.c
-         instead of the obsolete send_header function pointer
-
-2005-09-28 06:32  maan
-
-       * ogg.c: save header of starup rather than seeking the file
-
-2005-09-28 01:04  maan
-
-       * afs.c, http_send.c, server.c: more cosmetics
-
-2005-09-28 00:37  maan
-
-       * audiod.c, command.c, http_send.c, mp3.c: cosmetics
-
-2005-09-27 23:57  maan
-
-       * http_send.c: whitespace cleanup
-
-2005-09-27 23:57  maan
-
-       * command.c: comment out debug message
-
-2005-09-27 23:49  maan
-
-       * send.h, server.h: new struct sender_command_data for server
-         callbacks
-
-2005-09-27 23:48  maan
-
-       * server.c: implement callbacks for sender commands, only call send
-         functions if status is on
-
-2005-09-27 23:46  maan
-
-       * ogg.c: comment out noisy debug message
-
-2005-09-27 23:44  maan
-
-       * http_send.c: use new http sender options, implement sender
-         commands allow, deny, on, off, info
-
-2005-09-27 23:41  maan
-
-       * server.ggo: add some http sender options
-
-2005-09-27 23:40  maan
-
-       * command.c: implement new sender command
-
-2005-09-27 23:39  maan
-
-       * Makefile.in: add some more dependencies on server.cmdline.h
-
-2005-09-27 04:32  maan
-
-       * server.h: prototypes for new functions for testing
-         afs_status_flags. No need to pass status flags to the mainloop
-         and send_chunk functions
-
-2005-09-27 04:30  maan
-
-       * server.c: support for internal senders. Use init_tcp_socket from
-         net.c
-
-2005-09-27 04:27  maan
-
-       * para.h: add the two new function prototypes from net.c
-
-2005-09-27 04:26  maan
-
-       * oggdec.c: delay if hole in data was found
-
-2005-09-27 04:25  maan
-
-       * ogg.c: use new functions for testing afs_status_flags, support
-         for internal senders. Nuke initial_chunk logic.
-
-2005-09-27 04:22  maan
-
-       * net.c: pull init_tcp_socket() from server.c. It is also used by
-         http_send.c. New function rec_pattern to be used by both servers
-         and clients.
-
-2005-09-27 04:18  maan
-
-       * mp3.c: use new functions for testing afs_status_flags, support
-         for internal senders
-
-2005-09-27 04:17  maan
-
-       * afs.c: new functions for testing the afs_status_flags, support
-         for internal senders
-
-2005-09-27 04:16  maan
-
-       * Makefile.in: build http_recv, add http_send.o to server objects,
-         add some more header dependencies that were lost
-
-2005-09-27 04:12  maan
-
-       * http.h, http_recv.c, http_recv.ggo, http_send.c, send.h: internal
-         sender and external receiver
-
-2005-09-25 20:53  maan
-
-       * index.html: kernziel is dead
-
-2005-09-23 21:42  maan
-
-       * server.h: remove unused liked_cmd_list
-
-2005-09-22 00:40  maan
-
-       * mysql.c: fix empty stream definitions, ignore errors on remove
-
-2005-09-22 00:22  maan
-
-       * para.h: replace __func__ by __FUNCTION__ as this seems to be
-         supported by a wider class of complilers
-
-2005-09-21 20:07  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-09-21 20:05  maan
-
-       * versions/: paraslash-0.2.4.tar.bz2, paraslash-0.2.4.tar.bz2.asc:
-         paraslash-0.2.4
-
-2005-09-21 20:04  maan
-
-       * NEWS, configure.ac, index.html, scripts/demo-script: paraslash
-         0.2.4
-
-2005-09-21 06:36  maan
-
-       * command.c: cosmetics
-
-2005-09-20 00:32  maan
-
-       * audiod.c: cosmetics
-
-2005-09-20 00:23  maan
-
-       * audiod.c: typo, make max_deviation a constant, no need to check
-         for items if itemnum < 0
-
-2005-09-19 22:12  maan
-
-       * audiod.c: simplify check_stat_line()
-
-2005-09-19 22:11  maan
-
-       * Makefile.in: audiod no longer needs gui_common.o
-
-2005-09-19 22:10  maan
-
-       * gui_common.c, para.h, stat.c: move some functions from
-         gui_common.c to stat.c since they are also needed by audiod.
-
-2005-09-19 20:35  maan
-
-       * gui_common.c: kill unused variable
-
-2005-09-19 20:24  maan
-
-       * command.c, server.c, server.h: logging improvements
-
-2005-09-19 05:17  maan
-
-       * mp3.c: loglevel adjustment
-
-2005-09-19 05:00  maan
-
-       * daemon.c: simplify log_welcome()
-
-2005-09-19 05:00  maan
-
-       * gui_common.c: fix check_buf_for_items()
-
-2005-09-19 04:58  maan
-
-       * server.c: fix logging for higher loglevels
-
-2005-09-18 19:08  maan
-
-       * ogg.c: increase eof time
-
-2005-09-18 00:54  maan
-
-       * gui.c: rename struct args_info to conf
-
-2005-09-18 00:38  maan
-
-       * server.c: simplify parse_config()
-
-2005-09-18 00:32  maan
-
-       * NEWS, afs.c, server.ggo: make announce_time a run-time option
-
-2005-09-18 00:28  maan
-
-       * Makefile.in: add some missing dependencies
-
-2005-09-12 16:48  maan
-
-       * NEWS, README: [no log message]
-
-2005-09-12 15:41  maan
-
-       * command.c: com_si(): report total size of memory allocated with
-         sbrk by malloc, in kbytes.
-
-2005-09-12 15:36  maan
-
-       * NEWS: [no log message]
-
-2005-09-12 15:36  maan
-
-       * configure.ac: check for presence and usability of some more
-         headers
-
-2005-09-12 15:35  maan
-
-       * crypt.c: fix potential memory leak
-
-2005-09-11 22:16  maan
-
-       * para.h: small cleanup and more comments
-
-2005-09-11 22:15  maan
-
-       * server.c: remove old crap from para_log(), typo, do not write to
-         console in daemon mode, close fd 0,1,2 in daemon mode
-
-2005-09-09 06:44  maan
-
-       * oggdec.c: make write_header() static. Cosmetics
-
-2005-09-09 05:28  maan
-
-       * audiod.c: replace global var by local ones
-
-2005-09-09 05:12  maan
-
-       * audiod.c: some more trivial cleanups
-
-2005-09-09 04:56  maan
-
-       * audiod.c: reorder global vars, defines and the like. Trivial.
-
-2005-09-09 04:46  maan
-
-       * audiod.c: remove an unused variable, man cmds[] static
-
-2005-09-09 04:43  maan
-
-       * audiod.c: make functions static where possible
-
-2005-09-09 04:39  maan
-
-       * audiod.c: remove pointless NULL-check in ring_buffer_free()
-
-2005-09-09 04:34  maan
-
-       * audiod.c: fix nasty mem leak. We must not reset s->format too
-         early as it is needed in the filter-free loop
-
-2005-09-08 23:48  maan
-
-       * NEWS: [no log message]
-
-2005-09-08 23:47  maan
-
-       * sdl_gui.c: better layout
-
-2005-09-08 16:15  maan
-
-       * configure.ac: new configure option: --enable-ssldir=path, fix
-         typo
-
-2005-09-06 01:17  maan
-
-       * play.c: use stringification macro to print err msg
-
-2005-09-06 00:55  maan
-
-       * Makefile.in: audiod.o depends on list.h. Add list.h to the
-         tarball
-
-2005-09-06 00:51  maan
-
-       * list.h: Needed by audiod. Copied from the Linux kernel source
-         tree.
-
-2005-09-06 00:50  maan
-
-       * NEWS: [no log message]
-
-2005-09-06 00:49  maan
-
-       * mp3dec.c: comment out debug message
-
-2005-09-06 00:37  maan
-
-       * audiod.c: new command: grab. Close fd 0,1,2 in deamon mode
-
-2005-09-06 00:32  maan
-
-       * audioc.ggo: new option: --bufsize
-
-2005-09-06 00:32  maan
-
-       * audioc.c: new option: --bufsize. Use select() rather than plain
-         read/write to avoid blocking writes writes when there is space
-         left in the buffer
-
-2005-09-06 00:29  maan
-
-       * client.c: only check the first data buffer for the
-         AWAITING_DATA_MSG. Always use full buffersize for recv
-
-2005-09-06 00:27  maan
-
-       * net.c: no need to waste one byte
-
-2005-09-04 11:05  maan
-
-       * client.ggo: add typestr to options
-
-2005-09-04 11:05  maan
-
-       * Makefile.in: use --unamed-opts only if neccessary
-
-2005-09-02 14:43  maan
-
-       * audiod.c: do_filter_io(): only add return value of read() to
-         loaded bytes if it is positive
-
-2005-09-02 14:41  maan
-
-       * index.html: typo
-
-2005-09-01 20:14  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-09-01 20:12  maan
-
-       * versions/: paraslash-0.2.3.tar.bz2, paraslash-0.2.3.tar.bz2.asc:
-         paraslash-0.2.3
-
-2005-09-01 20:11  maan
-
-       * NEWS, configure.ac, index.html, scripts/demo-script: paraslash
-         0.2.3
-
-2005-09-01 19:49  maan
-
-       * NEWS: [no log message]
-
-2005-09-01 14:14  maan
-
-       * audiod.c: typo
-
-2005-09-01 13:44  maan
-
-       * NEWS, audiod.c, audiod.ggo: new audiod option: --stream_delay,
-         used if new macro START_TIME() was given for stream_write_cmd.
-
-2005-09-01 13:43  maan
-
-       * compress.ggo: use maximal volume as default
-
-2005-09-01 03:04  maan
-
-       * NEWS: [no log message]
-
-2005-09-01 02:09  maan
-
-       * index.html: add list of releases to top of page
-
-2005-09-01 01:04  maan
-
-       * Makefile.in: make xxx.cmdline.o always the first prerequisite,
-         use multiple targets in gengetopt rule
-
-2005-09-01 00:33  maan
-
-       * Makefile.in, play.c, play.ggo: command line options for para_play
-
-2005-08-31 22:06  maan
-
-       * FEATURES, README: mention para_compress
-
-2005-08-31 21:49  maan
-
-       * NEWS, audiod.c, audiod.ggo: better syntax for stream/filter
-         commands
-
-2005-08-31 18:42  maan
-
-       * CREDITS, Makefile.in, NEWS: add para_compress
-
-2005-08-31 18:41  maan
-
-       * compress.c, compress.ggo: a dynamic range compressor, derived
-         from AudioCompress-1.5.2
-
-2005-08-31 18:40  maan
-
-       * recv.c: save header, delay sending until first data packet
-         _after_ header arrives
-
-2005-08-31 18:38  maan
-
-       * play.c: make it understand wave headers, use plug_swmix for
-         snd_pcm_open()
-
-2005-08-29 06:49  maan
-
-       * net.c: do not use fprintf in net.c, whitespace cleanup
-
-2005-08-29 04:09  maan
-
-       * mysql.c: whitespace cleanup
-
-2005-08-26 13:51  maan
-
-       * mysql.c: tell mysql version on init
-
-2005-08-26 03:55  maan
-
-       * play.c: do not exit on buffer underruns, reduce sleep time on
-         -EAGAIN
-
-2005-08-26 03:54  maan
-
-       * audiod.c: also kill filters in kill_decoder()
-
-2005-08-26 02:32  maan
-
-       * FEATURES, Makefile.in, NEWS, README, configure.ac: include
-         para_play
-
-2005-08-26 02:31  maan
-
-       * play.c: a tiny alsa player
-
-2005-08-26 00:10  maan
-
-       * NEWS, audiod.c, audiod.ggo: implement filtering inside audiod
-
-2005-08-25 01:11  maan
-
-       * NEWS, mysql.c, stat.c: cosmetics
-
-2005-08-25 01:11  maan
-
-       * Makefile.in: remove funny characters around codename in version
-         string
-
-2005-08-24 11:27  maan
-
-       * afs.c: whitespace clenaup ;)
-
-2005-08-19 22:08  maan
-
-       * signal.c: add GPL header
-
-2005-08-19 21:32  maan
-
-       * scripts/demo-script: make it download paraslash.0.2.0
-
-2005-08-19 10:03  maan
-
-       * client.conf.sample, fade.conf.sample, sdl_gui.conf.sample,
-         server.conf.sample: these are both outdated and really pointless.
-         Read the nice online help instead
-
-2005-08-19 06:30  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-08-19 06:28  maan
-
-       * versions/: paraslash-0.2.2.tar.bz2, paraslash-0.2.2.tar.bz2.asc:
-         paraslash-0.2.2
-
-2005-08-19 06:24  maan
-
-       * Makefile.in, NEWS, configure.ac: paraslash 0.2.2
-
-2005-08-19 02:24  maan
-
-       * mysql.c: cosmetics
-
-2005-08-19 02:16  maan
-
-       * NEWS, mysql.c: new command snp (set numplayed)
-
-2005-08-19 01:57  maan
-
-       * mysql.c: cosmetics
-
-2005-08-19 01:50  maan
-
-       * mysql.c: com_mv(): also update the name in the dir-table
-
-2005-08-19 00:57  maan
-
-       * net.c:
-         work around a gcc-4.1 bug (?) that caused send_cred_buffer() to
-         send only zeros. With this workaround, para_audioc works again.
-
-2005-08-18 07:44  maan
-
-       * scripts/demo-script: use -N option for wget to overwrite any
-         tarballs downloaded earlier
-
-2005-08-18 05:37  maan
-
-       * client.c: whitespace cleanup
-
-2005-08-18 04:01  maan
-
-       * INSTALL: typo
-
-2005-08-18 03:59  maan
-
-       * INSTALL: correct instructions for manual streaming
-
-2005-08-18 03:38  maan
-
-       * exec.c: fix fd leak in para_exec() just introduced
-
-2005-08-18 03:22  maan
-
-       * exec.c: trivial simplifications
-
-2005-08-18 02:56  maan
-
-       * exec.c: whitespace cleanup
-
-2005-08-18 01:29  maan
-
-       * Makefile.in, NEWS, dbadm.c, exec.c, fade.c, gui.c, mysql.c,
-         para.h: change all remaining users of popen_*() to use para_exec
-         instead. nuke popen_* functions from exec.c
-
-2005-08-17 04:37  maan
-
-       * NEWS, mysql.c: add command ne (new entry)
-
-2005-08-17 04:33  maan
-
-       * README: [no log message]
-
-2005-08-17 03:40  maan
-
-       * mysql.c, para.h, string.c: new mysql command: mv (rename
-         entries). This needs another helper, para_dirname(), which was
-         added to string.c. Use const attribute for para_dirname() and add
-         it to para_basename too.
-
-2005-08-17 02:13  maan
-
-       * audiod.c, client.c, dbadm.c, oggdec.c, server.c: cosmetics
-
-2005-08-17 01:35  maan
-
-       * NEWS, mysql.c: new command: rm to remove entries from the
-         database
-
-2005-08-17 01:35  maan
-
-       * command.c: cosmetics
-
-2005-08-17 00:41  maan
-
-       * skencil/overview.sk: change 0.2.0 to 0.2.x
-
-2005-08-17 00:37  maan
-
-       * fade.c: cosmetics
-
-2005-08-17 00:35  maan
-
-       * string.c, fade.c: whitespace cleanup
-
-2005-08-17 00:24  maan
-
-       * audioc.c, audiod.c, client.c, daemon.c, fade.c, gui.c, para.h,
-         recv.c, sdl_gui.c, send.c, server.c, slider.c: rename clog to
-         para_log
-
-2005-08-17 00:11  maan
-
-       * audiod.c, gui.c, para.h, server.c, signal.c: simplify
-         para_signal_init()
-
-2005-08-17 00:01  maan
-
-       * afs.c, audiod.c, client.c, command.c, daemon.c, dopey.c, fade.c,
-         mp3.c, mysql.c, net.c, ogg.c, recv.c, send.c, server.c, signal.c,
-         stat.c, string.c: nuke superflous __func__
-
-2005-08-16 23:31  maan
-
-       * krell.c: whitespace cleanup, change email address
-
-2005-08-16 22:47  maan
-
-       * NEWS, afs.c, audiod.c, client.c, command.c, daemon.c, dopey.c,
-         fade.c, gui.c, mp3.c, mysql.c, net.c, ogg.c, para.h, recv.c,
-         send.c, server.c, signal.c, stat.c, string.c: new log macros that
-         do not require __func__ each time and can be optimized away
-         easily
-
-2005-08-16 21:07  maan
-
-       * para.h: whitespace cleanup
-
-2005-08-16 21:06  maan
-
-       * audiod.c, gui.c, para.h, server.c: nuke loglevel VERBOSE
-
-2005-08-16 21:06  maan
-
-       * Makefile.in: use -O3 and add -Wuninitialized and
-         -Wredundant-decls
-
-2005-08-16 20:58  maan
-
-       * scripts/demo-script: make it download paraslash-0.2.1
-
-2005-08-16 20:48  maan
-
-       * audiod.c: stream readers do not read from stdin
-
-2005-08-16 20:45  maan
-
-       * exec.c: serious thinko. Close the right fds in para_exec(). This
-         was obviously incorrect before. Strange that only gcc 4 produced
-         a non-working audiod..
-
-2005-08-16 20:36  maan
-
-       * client.c, command.c, crypt.c: fix some signedness issues
-
-2005-08-15 23:28  maan
-
-       * Makefile.in, NEWS, configure.ac: new codename, reset version to
-         cvs
-
-2005-08-15 23:09  maan
-
-       * versions/: paraslash-0.2.1.tar.bz2, paraslash-0.2.1.tar.bz2.asc:
-         paraslash-0.2.1
-
-2005-08-15 23:04  maan
-
-       * NEWS, configure.ac: paraslash 0.2.1
-
-2005-08-15 22:35  maan
-
-       * Makefile.in: older versions of libreadline and libssl come with
-         header files that produce lots of warnings about redundant
-         redeclarations. Deactivate -Wredundant-decls for now
-
-2005-08-15 22:23  maan
-
-       * audiod.c: make gcc happy
-
-2005-08-15 07:47  maan
-
-       * mp3.c: simplify mp3_seek_next_header(), use para_fread() instead
-         of plain fread() and check the retval. Nuke unused id3 genre.
-
-2005-08-14 20:15  maan
-
-       * audiod.c: fix serious memory leak
-
-2005-08-13 21:45  maan
-
-       * audiod.c: kill decoders and close status pipe in clean_exit()
-
-2005-08-13 21:43  maan
-
-       * client.c, crypt.c, crypt.h: remove some redundant ssl includes
-
-2005-08-13 20:36  maan
-
-       * command.c: kill extra newline
-
-2005-08-13 04:32  maan
-
-       * audiod.c, command.c: oops, the previous whitespace cleanup was
-         only for slider.c. Revert to previous version.
-
-2005-08-13 04:27  maan
-
-       * audiod.c, command.c, slider.c: whitespace cleanup slider.c
-
-2005-08-10 22:08  maan
-
-       * NEWS, audiod.c: nuke hup command. It is both pointless and
-         non-trivial to get it right
-
-2005-08-10 11:14  maan
-
-       * NEWS: [no log message]
-
-2005-08-10 03:23  maan
-
-       * gui_theme.c: whitespace cleanup
-
-2005-08-10 03:21  maan
-
-       * NEWS, audiod.c, command.c, daemon.c, gui_theme.c, para.h,
-         server.c, server.h, stat.c, string.c: new status item:
-         audiod_uptime. Reduce size of mtime string, reduce number of
-         audiod slots to 5, redirect stdout of audiod stream writer to
-         /dev/null
-
-2005-08-09 23:30  maan
-
-       * stat.c: whitespace cleanup
-
-2005-08-09 23:29  maan
-
-       * Makefile.in: maintainer-clean: remove tar.bz files
-
-2005-08-09 23:16  maan
-
-       * Makefile.in, configure.ac: deactivate -O and -Wuninitialized, add
-         crypt.h to the set of headers shipped in the tarball, use a macro
-         from the autoconf archive for detecting openssl.
-
-2005-08-09 08:45  maan
-
-       * FEATURES, README: [no log message]
-
-2005-08-09 07:15  maan
-
-       * INSTALL: mention --stream_write_cmd
-
-2005-08-09 06:21  maan
-
-       * mp3dec.c: whitespace cleanup
-
-2005-08-08 02:18  maan
-
-       * afs.c, command.c, mp3.c, ogg.c: fix some (hopefully all) format
-         string mismatches
-
-2005-08-08 02:04  maan
-
-       * audiod.c, client.c, command.c, fade.c, gui.c, net.c, para.h,
-         server.c, string.c: nuke some redundant redeclarations
-
-2005-08-08 02:04  maan
-
-       * Makefile.in: add -Wredundant-decls to CPPFLAGS
-
-2005-08-08 01:54  maan
-
-       * client.c, dbadm.c, sdl_gui.c, server.c: fix some missing voids.
-         Harmless
-
-2005-08-08 01:54  maan
-
-       * Makefile.in: add -Wstrict-prototypes
-
-2005-08-08 01:46  maan
-
-       * afs.c, audiod.c, command.c, dbadm.c, fade.c, gui.c, mysql.c,
-         net.c, ogg.c, string.c: fix some potential uses of uninitialized
-         vars. Nothing serious I think
-
-2005-08-08 01:38  maan
-
-       * Makefile.in: make gcc a bit more anal: turn on warnings for
-         uninitialized vars
-
-2005-08-07 23:39  maan
-
-       * NEWS: [no log message]
-
-2005-08-07 23:34  maan
-
-       * sdl_gui.c, sdl_gui.ggo: new options: stat_cmd, pic_cmd, adapt to
-         new syntax of para_open_audiod_pipe(), use para_exec instead of
-         popen, add typestrings to options
-
-2005-08-07 23:32  maan
-
-       * gui.c, gui.ggo, para.h: new option: stat_cmd, adapt to new syntax
-         of para_open_audiod_pipe()
-
-2005-08-07 23:30  maan
-
-       * gui_common.c: let it take the command as an argument, use
-         para_exec instead of popen
-
-2005-08-07 23:29  maan
-
-       * audioc.ggo: oops, this option belongs to audiod
-
-2005-08-07 22:11  maan
-
-       * NEWS, audioc.ggo, audiod.c, audiod.ggo, server.ggo: implement
-         --user option also for audiod
-
-2005-08-07 21:50  maan
-
-       * NEWS, daemon.c, para.h, server.c, server.ggo: new option --user
-         to switch to the given user when invoked as root
-
-2005-08-07 21:48  maan
-
-       * mysql.c: do not log (null) as the username although it is
-         perfectly OK to pass a NULL pointer to mysql_real_connect()
-
-2005-08-07 20:15  maan
-
-       * daemon.c: whitespace cleanup
-
-2005-08-07 09:02  maan
-
-       * 1.0: [no log message]
-
-2005-08-07 09:02  maan
-
-       * dopey.c: too dopey to get dopey right: avoid theoretical double
-         free bug that can only bite when given a invalid dopey_dir
-
-2005-08-07 07:26  maan
-
-       * crypt.c: add GPL header
-
-2005-08-07 07:11  maan
-
-       * client.c, command.c: kill old encrypt/decrypt functions that used
-         fork and exec. Replace it by new functions from crypt.c. This
-         should be much faster and, more importantly, fully transparent,
-         i.e. para_client-0.2.0 can still connect to newer versions of
-         para_server and vice versa, even when using 0.1.x keys.
-
-2005-08-07 07:10  maan
-
-       * NEWS: [no log message]
-
-2005-08-07 07:02  maan
-
-       * configure.ac: cleanup, make it check for the libssl lib rather
-         than for the openssl command line utility
-
-2005-08-07 07:00  maan
-
-       * Makefile.in: major cleanup, link crypt.o and the ssl libraries to
-         server and client
-
-2005-08-07 06:56  maan
-
-       * mp3.c: kill noisy debug message
-
-2005-08-07 06:56  maan
-
-       * crypt.c, crypt.h: rsa encrypt/decrypt routines
-
-2005-08-07 06:52  maan
-
-       * scripts/demo-script: make it download paraslash-0.2.0 instead of
-         paraslash-cvs
-
-2005-08-06 21:11  maan
-
-       * skencil/overview.sk: replace mp3 by pcm
-
-2005-08-06 21:01  maan
-
-       * index.html: another typo
-
-2005-08-06 20:55  maan
-
-       * index.html: provide quick-links at top of page
-
-2005-08-06 20:20  maan
-
-       * index.html: typo
-
-2005-08-06 20:06  maan
-
-       * index.html: announce the overview and provide a link to the pdf
-         file
-
-2005-08-06 19:55  maan
-
-       * skencil/overview.sk: make it a bit smaller since xpdf does not
-         show the upper part of the sketch
-
-2005-08-06 19:31  maan
-
-       * skencil/overview.sk: a skencil file containing a sketch which
-         illustrates how the pieces of paraslash work together
-
-2005-08-06 18:58  maan
-
-       * index.html: update live demo to use 0.2.0
-
-2005-08-06 18:58  maan
-
-       * Makefile.in, NEWS, configure.ac: change codename, reset version
-         to cvs
-
-2005-08-06 18:31  maan
-
-       * versions/: paraslash-0.2.0.tar.bz2, paraslash-0.2.0.tar.bz2.asc:
-         for the history
-
-2005-08-06 18:24  maan
-
-       * NEWS, configure.ac: paraslash-0.2.0
-
-2005-08-05 21:37  maan
-
-       * Makefile.in: oggdec needs to be linked also against libvorbis on
-         some systems
-
-2005-08-03 20:48  maan
-
-       * audiod.c: kill noisy debug message
-
-2005-08-03 19:59  maan
-
-       * audiod.c: fix self-correcting time display
-
-2005-08-03 06:18  maan
-
-       * pics/screenshots/loglevel1-2005-03-23.txt,
-         versions/paraslash-0.0.99.tgz, versions/paraslash-0.0.99.tgz.asc,
-         versions/paraslash-0.1.0.tgz, versions/paraslash-0.1.0.tgz.asc,
-         versions/paraslash-0.1.1.tgz, versions/paraslash-0.1.1.tgz.asc,
-         versions/paraslash-0.1.2.tgz, versions/paraslash-0.1.2.tgz.asc,
-         versions/paraslash-0.1.3.tgz, versions/paraslash-0.1.3.tgz.asc,
-         versions/paraslash-0.1.4.tgz, versions/paraslash-0.1.4.tgz.asc,
-         versions/paraslash-0.1.5.tgz, versions/paraslash-0.1.5.tgz.asc,
-         versions/paraslash-0.1.6.tgz, versions/paraslash-0.1.6.tgz.asc,
-         versions/paraslash-0.1.7.tgz, versions/paraslash-0.1.7.tgz.asc:
-         just for the history
-
-2005-08-03 06:03  maan
-
-       * mp3.c: make it work again for vbr, whitespace cleanup
-
-2005-08-02 18:47  maan
-
-       * mp3.c: fix length computation (fixes Loser_Pils problem)
-
-2005-08-02 18:43  maan
-
-       * para.h: add prototype for tv_divide()
-
-2005-08-02 18:39  maan
-
-       * recv.c: rename bytes_written to header_status, add a comment
-
-2005-08-02 05:15  maan
-
-       * command.c: also before calling the command handler, mention
-         usename when logging with loglevel NOTICE
-
-2005-08-02 03:25  maan
-
-       * audiod.c, client.c, fade.c, server.c, string.c: activate format
-         string checks
-
-2005-08-02 03:25  maan
-
-       * daemon.c: typo
-
-2005-08-02 03:02  maan
-
-       * server.h: kill AFS_REREAD_DB_INFO. Refreshing the database info
-         is done from within the mysql commands that change the info.
-
-2005-08-02 03:00  maan
-
-       * command.c: only send error message to client if the cause of the
-         error was different from E_SEND
-
-2005-08-02 02:58  maan
-
-       * audiod.ggo, mp3.c, ogg.c, recv.c, recv.ggo, send.c, send.ggo,
-         server.ggo, audiod.c: simpler syntax for para_recv and para_send
-
-2005-08-02 02:54  maan
-
-       * afs.c: increase SW_RESTART_TIME and ANNOUNCE_TIME
-
-2005-08-02 01:06  maan
-
-       * send.c: simplify stupid control flow in do_io()
-
-2005-08-01 23:46  maan
-
-       * command.c: com_sc(): optional argument for iteration count
-
-2005-08-01 22:58  maan
-
-       * recv.c: make ogg work again
-
-2005-08-01 20:52  maan
-
-       * recv.ggo: nuke outfile option
-
-2005-08-01 20:51  maan
-
-       * recv.c: always write to stdout
-
-2005-08-01 20:39  maan
-
-       * NEWS: whitespace cleanup
-
-2005-08-01 19:42  maan
-
-       * send.c: read a full initial chunk, not just the header length and
-         send it out to ortp in little small pieces.
-
-2005-08-01 19:41  maan
-
-       * recv.c: fix return value, better timing
-
-2005-08-01 19:37  maan
-
-       * afs.c, command.c, mp3.c, server.h, ogg.c: introduce time barriers
-         for start streaming/exec the stream writer, new status flag W
-         (stream writer running). Make ogg work with really nasty vbr
-         files
-
-2005-08-01 19:32  maan
-
-       * index.html: announce live stream, anonymous cvs access and
-         nightly snapshots
-
-2005-08-01 19:15  maan
-
-       * scripts/demo-script: script to download and install a demo-client
-         to retrieve the stream from www.paraslash.org
-
-2005-08-01 04:25  maan
-
-       * time.c: whitespace cleanup
-
-2005-08-01 04:24  maan
-
-       * time.c: Use const where aprropriate in tv_diff(), really
-
-2005-08-01 03:34  maan
-
-       * para.h, time.c: new function ms2tv, convert milliseconds to
-         struct timeval. Use const where aprropriate in tv_diff()
-
-2005-07-30 05:34  maan
-
-       * mysql.c: cosmetics
-
-2005-07-30 05:26  maan
-
-       * gui.ggo: nuke obsolete stream-related command line options that
-         have been gone long ago
-
-2005-07-30 05:26  maan
-
-       * gui.c: replace help text as ':' is not mapped by default
-
-2005-07-30 05:16  maan
-
-       * client.c, client.ggo: new option: user
-
-2005-07-29 01:54  maan
-
-       * sdl_gui.c, afs.c: whitespace cleanup
-
-2005-07-29 01:51  maan
-
-       * command.c: whitespace cleanup
-
-2005-07-29 01:44  maan
-
-       * mysql.c: whitespace cleanup
-
-2005-07-29 01:37  maan
-
-       * audiod.c, gui.c: whitespace cleanup
-
-2005-07-29 01:33  maan
-
-       * server.c: whitespace cleanup
-
-2005-07-29 01:31  maan
-
-       * audiod.c: the test diff < max_diff was broken. Also, fix empty
-         line in stat output
-
-2005-07-29 01:29  maan
-
-       * para.h, time.c: make tv_convex_combination() accept also negative
-         coefficients
-
-2005-07-27 21:42  maan
-
-       * mp3.c: still one more unused variable
-
-2005-07-27 21:35  maan
-
-       * mp3.c: nuke another unused variable
-
-2005-07-27 21:30  maan
-
-       * mp3.c: simplify write_chunk()
-
-2005-07-27 21:23  maan
-
-       * mp3.c: nuke two unused variables
-
-2005-07-27 21:20  maan
-
-       * mp3.c: make it work for bitrates different from 44100, some
-         trivial cleanups
-
-2005-07-25 22:37  maan
-
-       * Makefile.in, NEWS: switch from gzip to bzip2
-
-2005-07-25 22:08  maan
-
-       * Makefile.in: remove some cruft that is not needed for nomal
-         compilation, so it's better done from private scripts
-
-2005-07-25 21:21  maan
-
-       * afs.c: make gcc 2.95 happy
-
-2005-07-25 20:32  maan
-
-       * command.c: cosmetics
-
-2005-07-25 19:39  maan
-
-       * PUBLIC_KEY: old public key, but added uid maan@systemlinux.org
-         and maan@paraslash.org
-
-2005-07-25 03:32  maan
-
-       * recv.ggo, server.ggo: delete some unused options
-
-2005-07-25 00:08  maan
-
-       * Makefile.in, afs.c, audioc.c, audiod.c, client.c, client.ggo,
-         fade.c, fade.ggo, gui.c, gui.ggo, mp3.c, ogg.c, para.h,
-         sdl_gui.c, sdl_gui.ggo, server.c, server.ggo, slider.c, string.c:
-         do not rely on environment HOME and LOGNAME. Use getuid() and
-         entries from /etc/passwd instead
-
-2005-07-24 21:21  maan
-
-       * recv.c: nuke unused have_header
-
-2005-07-24 21:19  maan
-
-       * ogg.c: fix pause, nuke initial_chunks
-
-2005-07-24 21:15  maan
-
-       * exec.c: para_exec: close all unneeded fds
-
-2005-07-24 21:14  maan
-
-       * command.c: thinko
-
-2005-07-24 21:14  maan
-
-       * audiod.c: increase max time deviation for fallback to server
-         stream time to 5 seconds, other smallish cleanups
-
-2005-07-24 21:11  maan
-
-       * afs.c: introduce announce delay when switching from pause to play
-
-2005-07-22 06:20  maan
-
-       * audiod.c, send.c: cosmetics
-
-2005-07-22 04:07  maan
-
-       * audiod.c, client.c, fade.c, gui.c, recv.c, send.c, server.c,
-         string.c: add __attribute__ format for printf-like functions
-         (commented out by now) and fix some mismatches
-
-2005-07-22 03:13  maan
-
-       * audiod.c: loglevel adjustments
-
-2005-07-22 01:09  maan
-
-       * stat.c: fix removal of stat clients
-
-2005-07-22 01:08  maan
-
-       * audiod.c: dup stderr of stream writer to /dev/null
-
-2005-07-21 22:52  maan
-
-       * INSTALL, audiod.c: rename standby to sb, it is much easier to
-         type
-
-2005-07-21 22:15  maan
-
-       * audiod.c: cosmetics
-
-2005-07-21 05:03  maan
-
-       * command.c: fix integer overflow in frame math for large mp3
-         files, print new status item current_time in com_stat.
-
-2005-07-21 05:02  maan
-
-       * mp3.c: fix integer overflow in frame math for large mp3 files
-
-2005-07-21 01:36  maan
-
-       * audiod.c: use new status item current_time to sync time with
-         server, so the stream_start status item info can also be used if
-         server and audiod clock differ
-
-2005-07-21 01:32  maan
-
-       * para.h, stat.c, time.c: new status item: current_time. New
-         functions for manipulating struct timeval
-
-2005-07-18 21:04  maan
-
-       * audiod.c: cosmetics
-
-2005-07-18 21:04  maan
-
-       * ogg.c: simplify get_chunk_size(), moderate debug logging
-
-2005-07-17 05:55  maan
-
-       * time.c: tv_diff(): compute a - b instead just returning -1 if a >
-         b
-
-2005-07-17 03:04  maan
-
-       * exec.c: remove some unused functions
-
-2005-07-17 02:43  maan
-
-       * time.c: this change should not make any difference, but it looks
-         saner this way
-
-2005-07-17 02:41  maan
-
-       * mp3.c: add an error message if file was not recognized as an mp3
-         file
-
-2005-07-17 02:40  maan
-
-       * exec.c: unused label
-
-2005-07-17 02:39  maan
-
-       * ogg.c: okay, the previous version did not recognize any ogg
-         files, valid or not. Fixed.
-
-2005-07-16 19:18  maan
-
-       * ogg.c: move initialization of ogg stuff to the beginning of the
-         function. That fixes a segfault for (non-ogg) files, when that
-         stuff got freed without being initialized
-
-2005-07-16 18:48  maan
-
-       * recv.c: nuke prebuffer code. Prebuffering should be done in the
-         player only
-
-2005-07-16 18:20  maan
-
-       * ogg.c: fix fake errors on eof, rename bytes to loaded_bytes and
-         make it global
-
-2005-07-16 17:42  maan
-
-       * audiod.c, send.c: signedness issues
-
-2005-07-16 17:38  maan
-
-       * afs.c, mp3.c: move log message from mp3.c to afs.c as it is
-         generic
-
-2005-07-16 17:33  maan
-
-       * net.c: signedness issues
-
-2005-07-15 04:27  maan
-
-       * audiod.c, gui_theme.c, para.h, sdl_gui.c, stat.c: replace
-         SI_DECODER_OUTPUT by SI_PLAY_TIME
-
-2005-07-15 04:09  maan
-
-       * gui.c: kill debug message
-
-2005-07-15 04:02  maan
-
-       * audiod.c: kill all decoders if server connection breaks down
-
-2005-07-15 03:45  maan
-
-       * NEWS: [no log message]
-
-2005-07-15 03:44  maan
-
-       * para.h, stat.c: typo
-
-2005-07-15 03:37  maan
-
-       * audiod.c: new function close_stat_pipe() that basically does
-         everything that com_off previously did. Besides from com_off it
-         is also called if connection to para_server breaks down
-
-2005-07-15 02:26  maan
-
-       * audiod.c: use quadratic growth rather than exponential growth for
-         penalty timings
-
-2005-07-15 01:46  maan
-
-       * command.c: fix com_ff
-
-2005-07-15 01:02  maan
-
-       * mp3.c: fix time display after pause
-
-2005-07-15 00:23  maan
-
-       * mp3.c: fix more potential segfaults (that actually never
-         triggered, but anyway)
-
-2005-07-15 00:11  maan
-
-       * mp3.c: fix computation of bitrate and frequency. Nuke
-         header_is_valid, it does not work for vbr. Fix potential segfault
-         in header_frequency()
-
-2005-07-14 23:14  maan
-
-       * audiod.c: simplify closing of stream fds. They are all closed
-         from one central function now. This fixes a problem when paused:
-         the write fd was not closed until the timeout was reached. With
-         this patch it is closed immediately
-
-2005-07-14 20:06  maan
-
-       * mp3.c: reset header_isvalid and id3_isvalid. Use a valid header
-         for computing the freq rather than the first header we can find
-
-2005-07-14 20:04  maan
-
-       * audiod.ggo: we do not read the HOSTNAME ENV anymore
-
-2005-07-14 20:03  maan
-
-       * para.h: make para_mkstemp public
-
-2005-07-14 20:02  maan
-
-       * mysql.c: cosmetics
-
-2005-07-14 19:55  maan
-
-       * mysql.c: do not leak fds on errors
-
-2005-07-14 19:18  maan
-
-       * mysql.c: cosmetics
-
-2005-07-14 19:12  maan
-
-       * mysql.c: com_upd: use para_mkstemp() for creating the infile in
-         /tmp
-
-2005-07-14 19:11  maan
-
-       * string.c: new function para_mkstemp()
-
-2005-07-14 18:30  maan
-
-       * string.c: add a comment for usage of para_tmpname()
-
-2005-07-14 18:21  maan
-
-       * afs.c: do not fall back to /tmp if $HOME is unset for security
-         reasons. Just bail out
-
-2005-07-14 17:59  maan
-
-       * net.c: delete some old code that was commented out anyway
-
-2005-07-14 17:55  maan
-
-       * server.ggo: add documentation for command line macros to -h
-         output
-
-2005-07-14 17:35  maan
-
-       * audioc.c: use para_hostname instead of getenv()
-
-2005-07-14 17:06  maan
-
-       * mp3dec.c: fix decoding of mono mp3s
-
-2005-07-14 06:43  maan
-
-       * command.c: increase event counter _after_ command has finished
-
-2005-07-14 06:13  maan
-
-       * mp3.c: write info string only at begin of file rather than on
-         each frame. This made an old bug show up: The header frequency
-         display was always wrong for the first time that info was sent by
-         com_stat.
-
-2005-07-14 05:21  maan
-
-       * mp3.c: fix computation of average bitrate for vbr mp3s
-
-2005-07-14 04:21  maan
-
-       * mysql.c: use para_tmpname instead of just the pid
-
-2005-07-13 19:45  maan
-
-       * afs.c: missing __func__, incorrect comment
-
-2005-07-13 09:21  maan
-
-       * audiod.c, gui_common.c, para.h: move open_stat_pipe from
-         gui_common.c to audiod.c and change it to use para_exec() instead
-         of obsolete popen_read_client_cmdline(). Nuke s->err_fd. New
-         functions check_reader() and check_writer(). They return whether
-         we should include the corresponding fd in the fd set for
-         select().
-
-2005-07-13 09:17  maan
-
-       * audioc.c: wrong indent
-
-2005-07-13 09:15  maan
-
-       * exec.c: remove an obsolete function
-
-2005-07-12 18:27  maan
-
-       * mp3dec.c: write wav header, switch to little endian
-
-2005-07-12 17:49  maan
-
-       * mp3dec.c: simplify read_chunk()
-
-2005-07-09 19:43  maan
-
-       * audiod.c: dec_out is no longer a good name
-
-2005-07-09 19:41  maan
-
-       * audiod.c: nuke decoder_output code
-
-2005-07-09 19:36  maan
-
-       * ogg.c: double initial chunks for mono
-
-2005-07-09 18:19  maan
-
-       * send.c: fix serious(?) off-by-one in header save_ov_headers()
-
-2005-07-09 18:17  maan
-
-       * ogg.c: simplify mainloop, introduce initial_chunks, number of
-         chunks that are sent immediately
-
-2005-07-09 18:13  maan
-
-       * audiod.c: fix time display if paused. Clear stat items when
-         stopped, dump audiod status in mainloop
-
-2005-07-09 18:13  maan
-
-       * para.h, stat.c: new function: dump_empty_status. To be called
-         when current status is known to be invalid
-
-2005-07-09 18:09  maan
-
-       * afs.c: introduce extra delay to announce the stream
-
-2005-07-09 01:26  maan
-
-       * afs.c, audiod.c, command.c, mp3.c, ogg.c, para.h, server.h,
-         stat.c: introduce mmd->stream_start as a fallback time source for
-         audiod when it starts its decoder in the middle of a audio file
-
-2005-07-08 04:11  maan
-
-       * net.c: use uname() to get the hostname instead of
-         getenv("HOSTNAME")
-
-2005-07-08 03:46  maan
-
-       * afs.c: use para_hostname() instead of getenv()
-
-2005-07-08 03:31  maan
-
-       * ogg.c: comment out noisy debug messages
-
-2005-07-08 03:29  maan
-
-       * ogg.c: use para_hostname() instead of getenv()
-
-2005-07-08 03:23  maan
-
-       * audiod.c: use para_hostname() instead of getenv()
-
-2005-07-08 03:12  maan
-
-       * oggdec.c: nuke time display as time is now computed by audiod
-
-2005-07-08 03:10  maan
-
-       * mp3dec.c: nuke print_status() as time is now computed by audiod
-
-2005-07-08 03:06  maan
-
-       * audiod.c: use para_malloc instead of malloc
-
-2005-07-08 02:38  maan
-
-       * stat.c: fix memory leak
-
-2005-07-08 01:53  maan
-
-       * audiod.c: use para_hostname instead of getenv
-
-2005-07-08 01:41  maan
-
-       * gui.c: use make_message instead of sprintf
-
-2005-07-07 03:38  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: change handling of repositioning
-         the stream. The corresponding timing is now done from within the
-         audio format handler
-
-2005-07-07 03:31  maan
-
-       * net.c: cosmetics
-
-2005-07-07 03:27  maan
-
-       * command.c: do not mess around with offset in command.c The offset
-         is better computed from the audio format handler
-
-2005-07-07 03:24  maan
-
-       * audioc.c: check config file
-
-2005-07-07 03:18  maan
-
-       * audiod.c: record start time of both reader and writer. Start
-         reader again even if writer exists
-
-2005-07-07 03:17  maan
-
-       * NEWS: [no log message]
-
-2005-07-07 03:16  maan
-
-       * Makefile.in: clean: remove also manual.txt
-
-2005-07-03 22:50  maan
-
-       * audiod.c: keep track of number of stat clients and refuse to
-         accept more stat connections if limit is reached
-
-2005-07-03 22:18  maan
-
-       * para.h, stat.c: client_add and() dump_stat_line() now return the
-         number of connected clients.
-
-2005-07-03 22:17  maan
-
-       * net.c: return positive value on success. Let's hope that this
-         does not break anything..
-
-2005-07-03 22:11  maan
-
-       * audiod.c: trivial cleanup, loglevel adjustments
-
-2005-07-03 20:51  maan
-
-       * audiod.c: close fd if ring_buffer_dump failed
-
-2005-07-03 20:33  maan
-
-       * audiod.c: oops. In handle_connect(), close fd only for invalid
-         commands as it is closed by each command handler if neccessary.
-
-2005-07-03 19:02  maan
-
-       * audiod.c: serious(?) typo. Nuke unused function
-
-2005-07-03 18:48  maan
-
-       * audiod.c: fix memory leak
-
-2005-07-03 18:41  maan
-
-       * audiod.c: rewrite audiod_status_string() to use the decoder that
-         was last started for time display rather than current_decoder
-         which is sent by server and might contain the new docoder while
-         the old one is still running. Send status string only if it has
-         changed
-
-2005-07-01 17:18  maan
-
-       * mp3.c: print average bitrate for vbr files, additional log
-         message at eof, more precise timing
-
-2005-07-01 17:14  maan
-
-       * para.h, time.c: new function: tv_scale()
-
-2005-06-27 18:57  maan
-
-       * command.c: fix com_ff()
-
-2005-06-26 17:42  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: handle REPOS in mainloop() rather
-         than in send_chunk(). Fix eof_delay for jump and next
-
-2005-06-22 00:29  maan
-
-       * command.c: serious bug: only install dummy SIGUSR1 signal handler
-         for com_stat(). This caused com_upd (and likely all other mysql
-         commands) to break if a server event occured during execution
-
-2005-06-21 23:45  maan
-
-       * mysql.c: do not nuke dirlist if find command did not find
-         anything
-
-2005-06-21 16:48  maan
-
-       * README: [no log message]
-
-2005-06-21 16:29  maan
-
-       * audiod.c: re-init streamio in com_hup()
-
-2005-06-21 15:36  maan
-
-       * Makefile.in, audiod.c: reactivate and fix penalty timings
-
-2005-06-21 14:46  maan
-
-       * audiod.c: clear_slot(): Only close stderr if it is a valid fd
-
-2005-06-20 23:43  maan
-
-       * README.mysql: nuke doku on DIR_LIKE macro.
-
-2005-06-20 23:39  maan
-
-       * mysql.c: nuke DIR_LIKE macro. Is the only one that requires an
-         O(n^2) query.
-
-2005-06-20 23:36  maan
-
-       * INSTALL, NEWS: [no log message]
-
-2005-06-19 21:55  maan
-
-       * index.html: reduce size of headline, fix some html errors to make
-         it valid html 4.01 transitional and include corresponding logo.
-
-2005-06-19 17:18  maan
-
-       * audiod.c: fix com_help(). This time for real.
-
-2005-06-19 17:11  maan
-
-       * audiod.c: fix com_help()
-
-2005-06-19 16:02  maan
-
-       * net.c, para.h: new function: para_hostname()
-
-2005-06-19 16:02  maan
-
-       * mysql.c: ps and ns change current_stream, so they should require
-         DB_WRITE privileges. Extend get_query() to optionally do a
-         cheaper O(n) query for a given stream if full pathnames are not
-         required and use this cheaper query where possible. Moreover, do
-         not store the full result and truncate afterwards, but use a
-         mysql limit clause to get only the required number of matches
-
-2005-06-19 15:57  maan
-
-       * mp3.c: use para_hostname instead of getenc()
-
-2005-06-19 15:52  maan
-
-       * exec.c: abort if fork fails
-
-2005-06-19 15:52  maan
-
-       * audiod.c: fix several memory leaks
-
-2005-06-02 20:30  maan
-
-       * client.c, client.conf.sample, client.ggo, fade.ggo, gui.ggo,
-         sdl_gui.ggo, server.ggo: replace USER by LOGNAME
-
-2005-06-02 20:30  maan
-
-       * stat.c: list.h never really existed
-
-2005-06-02 19:37  maan
-
-       * send.c: do not sleep at all. This is now done from within afs
-
-2005-06-02 19:36  maan
-
-       * recv.c: make prebuffer time dependent on audio format
-
-2005-06-02 19:16  maan
-
-       * mp3.c: terminate stream writer on repos. That is the easiest way
-         to circumwent sound clipping
-
-2005-06-02 19:15  maan
-
-       * gui_common.c: stat.h was only a (bad) idea..
-
-2005-06-02 19:14  maan
-
-       * gui.c: serious typo that caused segfaults for terminals that are
-         too small for the current theme
-
-2005-06-02 19:13  maan
-
-       * audiod.c: dump stderr to /dev/null
-
-2005-06-02 19:13  maan
-
-       * afs.c: extra sleep between song changes. Needs to be redone
-         properly
-
-2005-06-02 17:38  maan
-
-       * mysql.c: com_cs changes the database
-
-2005-06-02 15:45  maan
-
-       * stat.c: comment out noisy debug messages
-
-2005-05-30 20:06  maan
-
-       * audiod.c, para.h, stat.c: move stat_client related functions to
-         from audiod.c to stat.c
-
-2005-05-26 17:30  maan
-
-       * audiod.c, audiod.ggo, net.c: new option: force (unlinks socket)
-
-2005-05-26 16:59  maan
-
-       * NEWS: [no log message]
-
-2005-05-26 16:59  maan
-
-       * audiod.ggo: nuke unused option auto_decode
-
-2005-05-26 07:28  maan
-
-       * net.c: give credit to okir
-
-2005-05-26 07:12  maan
-
-       * audiod.c, audiod.ggo: add user_alow option
-
-2005-05-26 06:57  maan
-
-       * audioc.c: do not crash with no args
-
-2005-05-26 06:57  maan
-
-       * NEWS, README: [no log message]
-
-2005-05-26 06:27  maan
-
-       * audiod.c, audiod.ggo: rename fifo to socket
-
-2005-05-26 06:23  maan
-
-       * audioc.c, audioc.ggo: rename fifdor to tmpdir
-
-2005-05-26 06:05  maan
-
-       * audioc.c, audioc.ggo: replace fifo by socket
-
-2005-05-26 06:01  maan
-
-       * audioc.c: nuke fifo code
-
-2005-05-26 05:41  maan
-
-       * audiod.c: remove some old fifo code
-
-2005-05-26 05:36  maan
-
-       * audiod.c: simplify handle_connect()
-
-2005-05-26 05:35  maan
-
-       * net.c: zero out the user buffer, not the control buffer
-
-2005-05-26 05:05  maan
-
-       * audiod.c, net.c: zero-out all buffers, 255 byte is enough
-
-2005-05-26 04:52  maan
-
-       * Makefile.in, audioc.c, audiod.c, net.c, para.h: switch from
-         cmd_fifo to unix sockets using SCM_CREDENTIALS
-
-2005-05-25 19:37  maan
-
-       * slider.c: update to libzmw-0.1.0
-
-2005-05-20 19:10  maan
-
-       * command.c: fix com_next and com_nomore that used afs_status_flags
-         directly
-
-2005-05-20 18:39  maan
-
-       * mysql.c: fix com_csp
-
-2005-05-19 23:35  maan
-
-       * slider.c: decrease slider size. That makes sliders work again if
-         one of them is at 100%. Don't ask why..
-
-2005-05-19 20:21  maan
-
-       * gui.c: do not hog cpu if no audiod
-
-2005-05-19 20:06  maan
-
-       * gui.c, gui_common.c, para.h, sdl_gui.c: rename open_audiod_pipe
-         to para_open_audiod_pipe
-
-2005-05-19 19:56  maan
-
-       * audioc.c: unlink fifo just after select indicates that audiod has
-         opened that fifo
-
-2005-05-19 19:55  maan
-
-       * command.c: add missing stat items
-
-2005-05-19 01:53  maan
-
-       * Makefile.in, command.c, gui_common.c, gui_theme.c: remove
-         volatile from command.c, compute everything in one run instead.
-         Replace hardcoded status strings by their equivalent in
-         status_item_list[]. Do not print SI_LENGTH_MIN and SI_LENGTH_SEC
-         as SI_LENGTH should be enough for everyone.
-
-2005-05-19 01:50  maan
-
-       * stat.c: contains common code concerning the para_server stat and
-         para_audiod stat commands. Used by server, gui and sdl_gui
-
-2005-05-19 00:17  maan
-
-       * README: [no log message]
-
-2005-05-18 20:27  maan
-
-       * gui.c, gui_common.c, gui_theme.c, para.h, sdl_gui.c: make sdl gui
-         use para_audioc instead of directly contacting the server. Move
-         some generic code from gui.c to gui_common.c
-
-2005-05-18 20:22  maan
-
-       * afs.c: comment out noisy debug messages
-
-2005-05-18 17:01  maan
-
-       * string.c: s_a_r(): return an empty string if src is NULL
-
-2005-05-18 16:59  maan
-
-       * ogg.c: fix crash on sighup, use sw_pid = -1 for 'no
-         stream_writer'
-
-2005-05-18 16:58  maan
-
-       * mp3.c: use sw_pid = -1 for 'no stream_writer'
-
-2005-05-18 16:58  maan
-
-       * command.c, server.c: use new_afs_status_flags
-
-2005-05-18 16:58  maan
-
-       * audiod.c: It is not an error if we can not find a stream writer,
-         just do nothing in that case
-
-2005-05-18 16:57  maan
-
-       * afs.c: use sw_pid = -1 for 'no stream_writer', use
-         new_afs_status_flags
-
-2005-05-17 17:38  maan
-
-       * slider.c: use something like x - 1/x for the  score formula
-
-2005-05-17 17:38  maan
-
-       * server.ggo: use something like x - 1/x for the default score
-
-2005-05-17 17:37  maan
-
-       * recv.c: do not prebuffer so much
-
-2005-05-17 17:37  maan
-
-       * server.c: use new_afs_status_flags
-
-2005-05-17 17:34  maan
-
-       * send.c: fix brown paper bug that caused the beginning of an ogg
-         file to be corrupted, sleep _after_ shutting down ortp
-
-2005-05-17 17:32  maan
-
-       * server.h: new: mmd->new_status_flags, make afs_open_fifo take an
-         array of fds (APUE trick)
-
-2005-05-17 17:31  maan
-
-       * string.c: s_a_r does not modify any of its args, dup the string
-         if no match was found
-
-2005-05-17 17:19  maan
-
-       * para.h: s_a_r does not modify any of its args
-
-2005-05-17 17:18  maan
-
-       * ogg.c: fix start of playing, use Close also the new fifo, start
-         para_send on demand
-
-2005-05-17 17:16  maan
-
-       * mp3.c: cosmetics, compute af_open_cmd on demand instead of only
-         once. Fixes segfault on sighup. Close also the new fifo from the
-         APUE trick
-
-2005-05-17 17:07  maan
-
-       * krell.c: fetch exit status of para_stat
-
-2005-05-17 17:03  maan
-
-       * index.html: [no log message]
-
-2005-05-17 17:03  maan
-
-       * exec.c: remove some unused code
-
-2005-05-17 17:03  maan
-
-       * command.c: com_next(): do not set afs_status_flags directly,
-         rather use the new mmd->new_afs_status_flags in order to prevent
-         audiod to start the (possibly wrong) decoder too early
-
-2005-05-17 17:01  maan
-
-       * audiod.c: fix percentage display, move kill_xxx functions to top,
-         kill reader if writer dies unexpected. Assume that valid fds are
-         > 0
-
-2005-05-17 16:56  maan
-
-       * afs.c: introduce afl[i].show_offset, open stream write fifo also
-         for reading, prevent hard locks on invalid stream_write command
-
-2005-05-17 16:39  maan
-
-       * NEWS: [no log message]
-
-2005-05-12 20:58  maan
-
-       * audiod.c: fix sound artefacts while jumping
-
-2005-05-11 23:16  maan
-
-       * audiod.c: use para_exec instead of popen_read_cmdline_pid(),
-         switch from FILE *s->write to int s->write_fd
-
-2005-05-11 23:15  maan
-
-       * exec.c: typo that caused duping to /dev/null to break
-
-2005-05-11 22:21  maan
-
-       * audiod.c: use para_exec instead of popen_read_cmdline_pid(),
-         switch from FILE *s->read to int s->read_fd
-
-2005-05-11 22:02  maan
-
-       * audiod.c: cosmetics
-
-2005-05-11 21:40  maan
-
-       * gui_theme.c: include audiod status also in simple theme
-
-2005-05-11 21:40  maan
-
-       * audiod.c: use new syntax for para_exec
-
-2005-05-11 21:38  maan
-
-       * INSTALL: [no log message]
-
-2005-05-11 21:19  maan
-
-       * exec.c: triple choice for para_exec: dup fd, dup fd to /dev/null,
-         or leave fd alone
-
-2005-05-11 07:25  maan
-
-       * mp3dec.c: reduce buffer size and hence latency
-
-2005-05-11 07:24  maan
-
-       * mp3.c: use correct value for computing the length of the song,
-         now that is has become important...
-
-2005-05-11 07:23  maan
-
-       * gui_theme.c: dont print length of audio file twice
-
-2005-05-11 06:48  maan
-
-       * NEWS: [no log message]
-
-2005-05-11 05:28  maan
-
-       * krell.c: fix crash on server exit
-
-2005-05-11 04:52  maan
-
-       * audiod.c: clear slots and compute decoder flags based on
-         existence of the corresponding process rather than on whether we
-         have closed the pipe
-
-2005-05-11 04:32  maan
-
-       * audiod.c: typos
-
-2005-05-11 04:06  maan
-
-       * mp3.c: okay, the improved time magic wasnt better at all..
-
-2005-05-11 03:56  maan
-
-       * audiod.ggo: better explanation of stream_read_cmd
-
-2005-05-11 03:38  maan
-
-       * exec.c: para_exec_cmdline_pid(): use a temporary copy of the
-         given command line for split_args
-
-2005-05-11 03:37  maan
-
-       * client.c: oops, missing check for write errors
-
-2005-05-11 03:19  maan
-
-       * audiod.c, command.c, server.c, server.h: major change: server
-         sends full status info whenever a event occurs. audiod: Fix time
-         display for most cases. Clients connect only once to get status
-         info, manage a list of stat-clients and send info to each of them
-         whenever we read a line from para_server. Improve starting of
-         decoders: Restart reader as soon as old reader dies, but defer
-         starting of writer to the moment we read the first data
-
-2005-05-11 03:07  maan
-
-       * afs.c: introduce .show_offset flag. On for mp3, off for ogg.
-
-2005-05-11 03:05  maan
-
-       * mp3dec.c: fix last-chunk-wasnt-played bug
-
-2005-05-11 03:03  maan
-
-       * ogg.c: typo
-
-2005-05-11 03:03  maan
-
-       * gui_theme.c: reserve more space for time display
-
-2005-05-11 03:02  maan
-
-       * mp3.c: better time magic
-
-2005-05-11 03:01  maan
-
-       * gui_common.c, para.h: SI_LENGTH is there for ages
-
-2005-05-11 03:00  maan
-
-       * gui.c: dont do things twice. Use popen(audioc) instead to get the
-         status.
-
-2005-05-11 02:57  maan
-
-       * string.c: para_strcat(): it's okay to append to a NULL pointer
-
-2005-05-11 02:50  maan
-
-       * audioc.c: nuke SIGALARM crap, check for write errors. Check for
-         timeout once and use blocking reads afterwards.
-
-2005-05-11 02:47  maan
-
-       * README: [no log message]
-
-2005-05-08 18:31  maan
-
-       * mp3.c, ogg.c, recv.c, recv.ggo, send.c, send.ggo, server.ggo: use
-         different default ports depending on -a option
-
-2005-05-08 17:59  maan
-
-       * COPYING, README, afs.c, client.c, command.c, configure.ac,
-         daemon.c, dopey.c, fade.c, gui.c, gui_theme.c, index.html, mp3.c,
-         mp3dec.c, mysql.c, ogg.c, oggdec.c, para.h, recv.c, sdl_gui.c,
-         send.c, server.c, server.h, slider.c, string.c: change email
-         adress
-
-2005-05-08 17:41  maan
-
-       * FEATURES, INSTALL, NEWS, README: [no log message]
-
-2005-05-08 17:19  maan
-
-       * server.ggo: document new defaults
-
-2005-05-08 17:18  maan
-
-       * para.h, string.c: check for NULL pointers in s_a_r()
-
-2005-05-08 17:17  maan
-
-       * ogg.c: change default from para_ovsend to para_send
-
-2005-05-08 17:17  maan
-
-       * mp3.c: change default from poc to para_send
-
-2005-05-08 17:16  maan
-
-       * afs.c: use s_a_r to compute open_cmd
-
-2005-05-08 16:28  maan
-
-       * Makefile.in, configure.ac, recv.c, send.c: adapt to new names
-
-2005-05-08 16:16  maan
-
-       * recv.c, recv.ggo: former ovrecv
-
-2005-05-08 16:16  maan
-
-       * ovrecv.c, ovrecv.ggo: renamed to recv
-
-2005-05-08 16:09  maan
-
-       * send.c, send.ggo: former ovsend
-
-2005-05-08 16:08  maan
-
-       * ovsend.c, ovsend.ggo: renamed to send
-
-2005-05-08 15:59  maan
-
-       * command.c: fix time display
-
-2005-05-08 15:51  maan
-
-       * Makefile.in, configure.ac: ovsend/ovrec do no longer depend on
-         libvorbis
-
-2005-05-08 15:51  maan
-
-       * string.c: use const where appropriate
-
-2005-05-08 15:50  maan
-
-       * para.h: make s_a_r public
-
-2005-05-08 15:49  maan
-
-       * ovsend.ggo: specify header length by command line
-
-2005-05-08 15:49  maan
-
-       * ogg.c, ovsend.c: move code that depends on libvorbis from
-         ovsend.c to ogg.c
-
-2005-05-08 15:47  maan
-
-       * mp3.c: remove noisy log message
-
-2005-05-08 15:46  maan
-
-       * afs.c: cosmetics
-
-2005-05-08 15:41  maan
-
-       * audiod.c: always check for streamio, not only when playing
-
-2005-05-08 02:14  maan
-
-       * ovrecv.c, ovsend.c, ovsend.ggo, ovrecv.ggo: implement streaming
-         of mp3 files, remove all dependencies on libvorbisfile
-
-2005-05-07 16:28  maan
-
-       * mp3.c: do timing for sending data inside mp3.c rather than
-         sending data whenever the fd would not block
-
-2005-05-07 16:26  maan
-
-       * mp3dec.c: implement prebuffering
-
-2005-05-06 21:42  maan
-
-       * slider.c: cosmetics
-
-2005-05-06 21:13  maan
-
-       * slider.c: make input text field work again
-
-2005-05-06 18:11  maan
-
-       * mysql.c: typos, found by testing gcc 4.1.0
-
-2005-05-05 23:15  maan
-
-       * audiod.c: remove fifo on com_hup, new option: mode (to be used on
-         startup)
-
-2005-05-05 23:15  maan
-
-       * audiod.ggo: oops, forgot to check in
-
-2005-05-05 20:42  maan
-
-       * mp3dec.c: print output only once per second
-
-2005-05-05 19:58  maan
-
-       * audiod.c: nuke offset_change crap. Remember number of decoder
-         that was started last and use that as the current decoder
-         instead. Still doesn't work properly when jumping.
-
-2005-05-05 18:45  maan
-
-       * audiod.c: use APUE trick to prevent EOF from being generated when
-         #clients goes from 1 to 0. Log hostname, abort if cmd fifo
-         exists, remove cmd_fifo on exit
-
-2005-05-05 16:43  maan
-
-       * ogg.c: do not flood logs
-
-2005-05-05 16:42  maan
-
-       * gui_theme.c: cosmetics
-
-2005-05-05 16:42  maan
-
-       * Makefile.in: manual.txt: add audiod commands. audiod_objs: remove
-         repeated object file
-
-2005-05-05 16:41  maan
-
-       * 1.0, NEWS: [no log message]
-
-2005-05-05 06:13  maan
-
-       * gui.c: use one one (random) fifo for reading audiod stat info
-
-2005-05-05 04:10  maan
-
-       * slider.c: automatically translated to libzmw 0.0.12 by shipped
-         script zmw-upgrade.py. Seems to work.
-
-2005-05-04 19:57  maan
-
-       * server.c: read only once from signal pipe
-
-2005-05-04 19:11  maan
-
-       * gui.c: fix some memory leaks, simplify print_status_bar()
-
-2005-05-04 18:23  maan
-
-       * audiod.c: still more memory leaks in dump_ring_buffer()
-
-2005-05-04 18:15  maan
-
-       * audiod.c: fix more memory leaks
-
-2005-05-04 17:58  maan
-
-       * command.c: fix severe memory leak in com_stat()
-
-2005-05-04 17:45  maan
-
-       * index.html: typo
-
-2005-05-04 17:45  maan
-
-       * gui.ggo: new option: timeout
-
-2005-05-04 17:44  maan
-
-       * gui.c: detect if audiod is running and print error message if it
-         is not. Print the keysym for unknown keys.
-
-2005-05-04 17:39  maan
-
-       * audiod.c: help for com_hup, more log messages
-
-2005-04-28 22:30  maan
-
-       * gui.c: open also write fifo in read-only mode
-
-2005-04-28 21:03  maan
-
-       * audioc.c: do not block forever if audiod is not running
-
-2005-04-28 20:51  maan
-
-       * audiod.c: do not dup stdout when execing decoder
-
-2005-04-28 20:51  maan
-
-       * exec.c: improve para_exec(): It is now possible to dup only some
-         of the three std fds
-
-2005-04-28 20:14  maan
-
-       * para.h: status_item_list update, make more function declarations
-         public
-
-2005-04-28 20:14  maan
-
-       * gui_theme.c: set the new varbables on theme init. New functions:
-         prev_theme() and next_theme()
-
-2005-04-28 20:12  maan
-
-       * gui_common.c: remove old cruft, status_item_list update
-
-2005-04-28 20:11  maan
-
-       * command.c: simplify com_stat(), set offset in com_jmp()
-
-2005-04-28 20:07  maan
-
-       * audiod.c: new commands: help and cycle. Do not call kill() if pid
-         <=0, check for offset change in stat output
-
-2005-04-28 20:04  maan
-
-       * audioc.c: use make_message() and new concat_arg() instead of
-         sprintf
-
-2005-04-28 20:04  maan
-
-       * gui.c: make many hardcoded constants themable. Don't exec
-         para_audioc, implement it as a function instead
-
-2005-04-28 19:57  maan
-
-       * string.c: new function: concat_args
-
-2005-04-28 19:57  maan
-
-       * server.h: add chunk_offset to mmd
-
-2005-04-28 19:56  maan
-
-       * gui.ggo: nuke top_lines since it is now set by theme
-
-2005-04-28 19:55  maan
-
-       * afs.c: reset chunk_offset on song change
-
-2005-04-28 19:54  maan
-
-       * NEWS: [no log message]
-
-2005-04-28 19:54  maan
-
-       * Makefile.in: audiod depends on gui_common
-
-2005-04-26 04:21  maan
-
-       * gui.c: make para_gui themable
-
-2005-04-26 04:18  maan
-
-       * Makefile.in, gui.c, gui_common.c, para.h: make para_gui themable
-
-2005-04-26 04:11  maan
-
-       * gui_theme.c: contains color and position information for
-         displaying stat items
-
-2005-04-25 18:58  maan
-
-       * gui.c: remove stream I/O code. This is now done by para_audiod
-
-2005-04-25 18:21  maan
-
-       * FEATURES: [no log message]
-
-2005-04-25 18:06  maan
-
-       * server.c: remove unused variable, child closes socket after fork
-
-2005-04-25 18:05  maan
-
-       * audiod.c: why was that commented out? Reactivate
-
-2005-04-25 18:04  maan
-
-       * audioc.ggo: new option: timeout
-
-2005-04-25 18:04  maan
-
-       * audioc.c: new option: timeout. catch signals, use positive error
-         codes and 0 for success
-
-2005-04-25 17:56  maan
-
-       * NEWS, README: [no log message]
-
-2005-04-25 17:56  maan
-
-       * Makefile.in: make install: create vardir, stripping is done at
-         install time, no need to use -s option for gcc
-
-2005-04-25 17:54  maan
-
-       * INSTALL: major update
-
-2005-04-25 06:06  maan
-
-       * README: mention the new executables
-
-2005-04-25 06:03  maan
-
-       * daemon.c, para.h: remove an unused function
-
-2005-04-25 06:02  maan
-
-       * audiod.c: do not log if daemon and no logfile, ignore
-         conf.daemon_given on sighup
-
-2005-04-25 05:55  maan
-
-       * mp3.c: always use default if af->open_cmd is null
-
-2005-04-25 05:52  maan
-
-       * server.c: remove some dead code
-
-2005-04-25 05:49  maan
-
-       * server.c: do not log if daemon and no logfile, ignore
-         conf.daemon_given on sighup, become daemon earlier. That fixes
-         hangs on startup.
-
-2005-04-25 02:40  maan
-
-       * Makefile.in: add daemon.o to objs where neccessary. audioc
-         depends on audioc.cmdlin.o
-
-2005-04-25 02:38  maan
-
-       * audiod.c: new command: hup, use functions from daemon.c to
-         daemonize and for logging to specified log file. Die on sigint,
-         reload conf on sighup
-
-2005-04-25 02:35  maan
-
-       * para.h, server.h: make functions from daemon.c public
-
-2005-04-25 02:34  maan
-
-       * server.c: move daemon generic functions to daemon.c
-
-2005-04-25 02:33  maan
-
-       * daemon.c: contains common stuff between para_server and
-         para_audiod
-
-2005-04-25 02:32  maan
-
-       * ovrecv.c, ovsend.c: switch to void instead of static void to
-         avoid compiler warnings
-
-2005-04-25 02:31  maan
-
-       * fade.c: switch to logging with to fixed args, like in any other
-         paraslash executable
-
-2005-04-25 02:30  maan
-
-       * NEWS: [no log message]
-
-2005-04-25 02:29  maan
-
-       * audioc.c: add gengetopt command line options
-
-2005-04-25 02:29  maan
-
-       * audioc.ggo: command line options for the audio client
-
-2005-04-24 21:27  maan
-
-       * audiod.c: implement audiod commands: stat term on off standby
-
-2005-04-24 20:25  maan
-
-       * Makefile.in, configure.ac: add para_oggdec
-
-2005-04-24 20:25  maan
-
-       * oggdec.c: a teensy oggvorbis decoder
-
-2005-04-24 20:02  maan
-
-       * audioc.c: simple client that opens two fifos to communicatw with
-         para_audiod.
-
-2005-04-24 20:01  maan
-
-       * audiod.c: remove underscore to match other stat items
-
-2005-04-24 20:00  maan
-
-       * para.h, string.c: new function: para_tmpname(). Pseudo random.
-
-2005-04-24 18:55  maan
-
-       * audiod.c: add decoder flags in ring buffer dump output
-
-2005-04-24 18:14  maan
-
-       * audiod.c: always check err_fd, close err_fd in any case
-
-2005-04-24 05:04  maan
-
-       * audiod.c: read also stderr of stream writers
-
-2005-04-24 05:04  maan
-
-       * exec.c, para.h: add para_exec functions that redirect stdin,
-         stdout, and stderr
-
-2005-04-24 02:54  maan
-
-       * NEWS: [no log message]
-
-2005-04-24 02:53  maan
-
-       * slider.c: add gpl header
-
-2005-04-24 02:52  maan
-
-       * Makefile.in, configure.ac: add para_mp3dec
-
-2005-04-24 02:52  maan
-
-       * mp3dec.c: A teensy mp3 decoder
-
-2005-04-24 02:51  maan
-
-       * CREDITS: add Thierry Excoffier and Robert Leslie
-
-2005-04-24 02:17  maan
-
-       * server.c: fix brown paper bag bug that caused server to hang on
-         song change
-
-2005-04-24 02:16  maan
-
-       * afs.c, exec.c, para.h: move open_fifo from afs.c to exec.c
-
-2005-04-24 02:15  maan
-
-       * Makefile.in: add para_audiod
-
-2005-04-24 02:15  maan
-
-       * audiod.c: the audio daemon
-
-2005-04-21 17:39  maan
-
-       * exec.c, para.h: make some functions static
-
-2005-04-21 17:33  maan
-
-       * para.h: remove unused defines
-
-2005-04-21 17:33  maan
-
-       * Makefile.in, NEWS, configure.ac: change version, codename
-
-2005-04-18 20:30  maan
-
-       * NEWS, configure.ac: paraslash 0.1.7
-
-2005-04-18 20:08  maan
-
-       * slider.c: give -1000 points if file was just played
-
-2005-04-18 00:37  maan
-
-       * mysql.c: com_cam(): fix stupid cut and paste bug
-
-2005-04-18 00:08  maan
-
-       * Makefile.in: para_ovrecv depends on string.o
-
-2005-04-18 00:07  maan
-
-       * gui.ggo: re-increase default stream_timeout to 10 secs because 5
-         secs is not enough for 22kh mono
-
-2005-04-18 00:06  maan
-
-       * ovrecv.c, ovrecv.ggo: introduce prebuffering
-
-2005-04-18 00:04  maan
-
-       * ogg.c: nuke grace_time()
-
-2005-04-17 21:30  maan
-
-       * NEWS: [no log message]
-
-2005-04-17 21:27  maan
-
-       * gui.c, gui.ggo: new command line option: --stream_timeout
-
-2005-04-17 21:16  maan
-
-       * para.h, time.c: use const where apropriate, round tv_usec
-
-2005-04-17 21:15  maan
-
-       * mp3.c: cosmetics
-
-2005-04-17 21:12  maan
-
-       * NEWS, README: [no log message]
-
-2005-04-11 20:06  maan
-
-       * ogg.c: whitespace fixes, improve timings, move time-related stuff
-         to time.c
-
-2005-04-11 20:04  maan
-
-       * ovsend.c: whitespace fixes, improve timings, get rid of some
-         sleep
-
-2005-04-11 20:02  maan
-
-       * bash_completion: make it work without having sourced
-         bash_competion
-
-2005-04-11 20:01  maan
-
-       * afs.c: move tv2ms() from to time.c, do not sleep in
-         call_all_mainloops()
-
-2005-04-11 20:00  maan
-
-       * Makefile.in: server, ovsend, ovrecv depend on time.c/time.o
-
-2005-04-11 19:59  maan
-
-       * time.c: contains time-related functions: tv_add(), tv_diff()...
-
-2005-04-11 19:58  maan
-
-       * ovsend.ggo: set default header interval to 3 seconds
-
-2005-04-11 19:57  maan
-
-       * para.h, server.h: mv time-related declarations to para.h
-
-2005-04-10 14:55  maan
-
-       * ogg.c: fix pause
-
-2005-04-10 00:07  maan
-
-       * afs.c: cosmetics
-
-2005-04-09 23:27  maan
-
-       * afs.c, mp3.c, ogg.c: Fix nasty double free bug: If
-         get_file_info() from the first audio format (mp3) failed, it
-         closed the corresponding fd, so get_file_info() for the second
-         audio format (ogg) got passed an already closed fd. Even worse,
-         get_file_info() closed that fd again. Nasty thing here is that
-         neither ov_open() nor the second fclose() segfaulted but
-         corrupted memory in some way which caused subsequent mysql
-         queries to crash.
-
-         The fix is easy: Do not close the fd at the audio format layer
-         (ogg, mp3), but at the higher afs layer, and of course only if
-         _all_ get_file_info()'s failed.
-
-2005-04-09 23:11  maan
-
-       * NEWS: [no log message]
-
-2005-03-30 22:17  maan
-
-       * slider.c: two new sliders: lastplayed and numplayed
-
-2005-03-28 23:40  maan
-
-       * krell.c: comment out unneeded destroy_song_change()
-
-2005-03-28 23:26  maan
-
-       * server.c: avoid openssl zombies, make old gcc happy
-
-2005-03-28 23:26  maan
-
-       * command.c: avoid openssl zombies
-
-2005-03-28 22:28  maan
-
-       * krell.c: fix memory leak
-
-2005-03-28 21:42  maan
-
-       * mp3.c: make 2.95.4 happy
-
-2005-03-27 20:51  maan
-
-       * ogg.c: rewrite ogg_mainloop() in the style of mp3_mainloop()
-
-2005-03-27 20:50  maan
-
-       * mp3.c: only start stream writer when we have a valid file and
-         current audio format is mp3
-
-2005-03-27 20:49  maan
-
-       * afs.c: simplify afs_mainloop(), init afl[i].fifo
-
-2005-03-26 18:22  maan
-
-       * mp3.c: move check for AFS_NEXT from send_chunk to mainloop
-
-2005-03-26 17:31  maan
-
-       * mp3.c: reorder functions
-
-2005-03-26 17:23  maan
-
-       * README.mysql: typo
-
-2005-03-26 17:22  maan
-
-       * mp3.c: fix playing when switching from ogg to mp3
-
-2005-03-25 02:03  maan
-
-       * exec.c: fixup totally borked exec_vp_pid_bg()
-
-2005-03-25 01:47  maan
-
-       * server.h: add prototypes for afs_handle_sigchild() and
-         afs_open_fifo(), add fifoname, fifo to struct audio_format, kill
-         sw from struct audio_format()
-
-2005-03-25 01:46  maan
-
-       * server.c: call afs_handle_sigchild for each child that has died
-
-2005-03-25 01:45  maan
-
-       * ogg.c: switch from global variabless for fifo to af->fifo,
-         af->fifoname, move open_fifo() to afs.c
-
-2005-03-25 01:42  maan
-
-       * mp3.c: use fifo rather than stdin, handle repos request from
-         within mp3_mainloop() rather than in mp3_send_chunk()
-
-2005-03-25 01:36  maan
-
-       * afs.c: new function handle_sigchild() which resets af->sw_pid,
-         move open_fifo from ogg.c to afs.c since it is generic for all
-         audio formats now
-
-2005-03-25 01:31  maan
-
-       * NEWS: [no log message]
-
-2005-03-25 01:31  maan
-
-       * CREDITS: add Simon Morlat
-
-2005-03-23 22:31  maan
-
-       * ogg.c: do not compute timestamps if current audio format is not
-         ogg. Move open_fifo() up, some small other cleanups
-
-2005-03-23 22:28  maan
-
-       * server.c: nicer startup message
-
-2005-03-23 22:28  maan
-
-       * mp3.c: remove pointless logging
-
-2005-03-23 22:27  maan
-
-       * command.c: typo
-
-2005-03-23 22:27  maan
-
-       * afs.c: use 1 as first argument of afl[i].mainloop() iff i is
-         current audio format
-
-2005-03-23 22:25  maan
-
-       * README: mention para_ovsend/para_ovrecv
-
-2005-03-23 18:12  maan
-
-       * afs.c, ogg.c: create fifo if not present, detect if fifofile
-         exists but is no fifo, better handling of fifo errors from
-         ogg_mainloop(), loglevel adjustments
-
-2005-03-23 06:05  maan
-
-       * ovrecv.c: new function for handling extra packages, a lot of
-         cleanups
-
-2005-03-23 05:49  maan
-
-       * FEATURES, README: describe ovsend/ovrecv
-
-2005-03-23 03:18  maan
-
-       * ovsend.c: simplify do_io()
-
-2005-03-23 02:46  maan
-
-       * ovsend.c: whitespace cleanup
-
-2005-03-23 02:42  maan
-
-       * gui.c, para.h: sys/time.h is needed by ogg.c and server.c, afs.c,
-         gui.c. Move its inclusion to para.h
-
-2005-03-23 02:35  maan
-
-       * command.c: fix com_nomore()
-
-2005-03-23 02:31  maan
-
-       * afs.c: fix com_nomore()
-
-2005-03-23 01:32  maan
-
-       * server.h: rename afs_preselect(), add two timeval related
-         functions, change syntax of send_chunk()
-
-2005-03-23 01:28  maan
-
-       * server.ggo: change default stream writer from pogg-http to
-         para_ovsend
-
-2005-03-23 01:27  maan
-
-       * server.c: always call afs_mainloop() (not only when playing) to
-         obtain a fd and a timeout which is fed to select(). Close all
-         filedescriptors which are marked FD_CLOEXEC just after forking
-         which is needed for the ogg code
-
-2005-03-23 01:22  maan
-
-       * para.h, string.c: add para_fread(), a wrapper for fread() with
-         sane return values
-
-2005-03-23 01:21  maan
-
-       * ogg.c: complete rewrite. ogg.c does its own timing for writing
-         out data chunks rather than writing to a pipe until it's full
-         like the mp3 code does
-
-2005-03-23 01:19  maan
-
-       * mp3.c: adapt to new syntax (return always NULL as timestamp),
-         move para_fread() to string.c, cosmetics
-
-2005-03-23 01:13  maan
-
-       * configure.ac: check for libortp and libzmw
-
-2005-03-23 01:11  maan
-
-       * NEWS: [no log message]
-
-2005-03-23 01:11  maan
-
-       * autogen.sh: clean up before and after updating
-
-2005-03-23 01:09  maan
-
-       * afs.c: rename afs_preselect() to afs_mainloop(). Call each audio
-         format's mainloop to obtain a filedescriptor and a timestamp.
-         Return the data of the current audio format to main
-
-2005-03-23 01:05  maan
-
-       * Makefile.in: add targets for ovsend and ovrecv
-
-2005-03-23 01:05  maan
-
-       * ovrecv.c, ovrecv.ggo, ovsend.c, ovsend.ggo: two new executables
-         for sending/retrieving ogg vorbis files via rtp
-
-2005-03-22 04:19  maan
-
-       * Makefile.in: do not print silly release date
-
-2005-03-05 17:12  maan
-
-       * README.mysql: minor cleanup
-
-2005-03-05 15:38  maan
-
-       * NEWS, configure.ac: paraslash 0.1.6
-
-2005-03-05 15:12  maan
-
-       * autogen.sh: also build the package. Redirect messages to
-         /dev/null
-
-2005-03-05 15:12  maan
-
-       * Makefile.in, configure.ac: remove pob_ogg
-
-2005-03-05 15:11  maan
-
-       * pob-ogg.c, pob-ogg.ggo: no longer needed
-
-2005-03-05 14:50  maan
-
-       * ogg.c: call end_of_input() directly after freeing ogg_buf.
-         Introduce sleep(1) for now.
-
-2005-03-02 01:29  maan
-
-       * command.c: com_stat(): fix status bar
-
-2005-02-28 14:58  maan
-
-       * NEWS: [no log message]
-
-2005-02-28 00:58  maan
-
-       * mp3.c: close mp3 file on exit
-
-2005-02-28 00:16  maan
-
-       * gui.c: typo
-
-2005-02-27 23:49  maan
-
-       * server.ggo: cosmetics
-
-2005-02-27 23:18  maan
-
-       * server.c: cosmetics
-
-2005-02-27 23:18  maan
-
-       * NEWS: [no log message]
-
-2005-02-27 23:06  maan
-
-       * gui.c: make reload_Config() also reread stream_read_cmd and
-         stream_write_cmd
-
-2005-02-27 22:55  maan
-
-       * gui.c: delay start of decoder rather than going to sleep if
-         penalty > 0
-
-2005-02-27 20:12  maan
-
-       * ogg.c: localize use of var page
-
-2005-02-27 19:46  maan
-
-       * string.c: add a comment
-
-2005-02-27 19:32  maan
-
-       * afs.c: trivial cleanups
-
-2005-02-27 19:27  maan
-
-       * afs.c: simplify afs_preselect()
-
-2005-02-27 19:14  maan
-
-       * afs.c, mp3.c, ogg.c, server.h: handle afs flags NEXT and REPOS
-         inside send chunk
-
-2005-02-27 13:59  maan
-
-       * ogg.c: nuke unneeded clear_page()
-
-2005-02-27 13:34  maan
-
-       * ogg.c: ogg_close_audio_file() is only called from
-         ogg_end_of_input(). Move it there and nuke
-         ogg_close_audio_file().
-
-2005-02-27 13:30  maan
-
-       * ogg.c: submit_headers_to_stream() does not need an argument.
-
-2005-02-27 04:55  maan
-
-       * ogg.c: whitespace cleanup
-
-2005-02-27 04:53  maan
-
-       * ogg.c: move OggVorbis_File into struct para_ogg_state
-
-2005-02-27 04:46  maan
-
-       * ogg.c: rename vcut_state to para_ogg_state
-
-2005-02-27 04:41  maan
-
-       * ogg.c: process_headers() does not need an argument.
-
-2005-02-27 04:34  maan
-
-       * ogg.c: get_page() does not need an argument.
-
-2005-02-27 04:29  maan
-
-       * ogg.c: nuke var vcutstate since vcutstate==s always.
-
-2005-02-27 04:15  maan
-
-       * ogg.c: vcut_new() is only called from ogg_init(). Move it there
-         and nuke vcut_new().
-
-2005-02-27 03:47  maan
-
-       * ogg.c: headers_to_buf() is only called from
-         init_oggvorbis_file(). Move it there and nuke headers_to_buf().
-
-2005-02-27 03:34  maan
-
-       * ogg.c: get rid of sent_headers variable
-
-2005-02-25 21:36  maan
-
-       * ogg.c: do not kill stream writer, wait for it to die instead.
-
-2005-02-25 21:16  maan
-
-       * mysql.c: use load data infile rather than load data local infile.
-         Change tmpdir to /tmp and umask of tmpfile to 022 in order to
-         make it readable by mysqld.
-
-2005-02-23 06:37  maan
-
-       * ogg.c: nuke brain dead have_page logic
-
-2005-02-23 06:22  maan
-
-       * ogg.c: reset offset and packet_no on end of input. Fixes para
-         next
-
-2005-02-23 05:33  maan
-
-       * ogg.c: fix jumping
-
-2005-02-23 05:14  maan
-
-       * ogg.c: remove some unused stuff
-
-2005-02-23 05:12  maan
-
-       * ogg.c: remove old read_chunk/write_chunk pair
-
-2005-02-23 05:10  maan
-
-       * ogg.c: switch to send_chunk, major code reorganization (old code
-         still there to be removed soon).
-
-2005-02-23 05:08  maan
-
-       * afs.c, mp3.c, server.h: switch to send_chunk, rather than the
-         combination of read_chunk/write_chunk
-
-2005-02-22 23:49  maan
-
-       * ogg.c: do not request new page if offset_granulepos is invalid,
-         fix type of offset_granulepos, more logging
-
-2005-02-22 23:13  maan
-
-       * server.h: fix refreshing of stream_write command on sighup
-
-2005-02-22 17:45  maan
-
-       * krell.c: fix compiler warning
-
-2005-02-22 17:44  maan
-
-       * gui.c: use reasonable defaults, increase MAX_STREAM_SLOTS to 10,
-         adapt penalty timing, init slots on startup
-
-2005-02-22 16:13  maan
-
-       * Makefile.in: make clean: remove para_slider
-
-2005-02-22 15:51  maan
-
-       * pics/screenshots/: para_krell-2005-02.png,
-         para_slider-2004-12.png: two more screenshots
-
-2005-02-22 14:47  maan
-
-       * afs.c, server.c: stop playing on read/write errors, fix
-         refreshing of stream_write command on sighup
-
-2005-02-22 00:10  maan
-
-       * afs.c, server.c, server.h: send_chunk() knows the stream writer
-         fd, no need to pass it as an argument
-
-2005-02-21 20:06  maan
-
-       * gui.c: cosmetics
-
-2005-02-21 00:32  maan
-
-       * gui.c: whitespace cleanups
-
-2005-02-21 00:30  maan
-
-       * gui.c: replace num by slot
-
-2005-02-21 00:19  maan
-
-       * gui.c: logging improvements
-
-2005-02-20 23:30  maan
-
-       * gui.c: more checks for active curses
-
-2005-02-20 22:42  maan
-
-       * gui.c: various cleanups, fix siol vs afi mix up
-
-2005-02-13 22:28  maan
-
-       * fade.c: more logging
-
-2005-02-13 22:27  maan
-
-       * Makefile.in: gui depends on string.o
-
-2005-02-13 22:26  maan
-
-       * COPYING: 2004->2005
-
-2005-02-01 22:16  maan
-
-       * ogg.c: delete unnecessary packet_clear()
-
-2005-01-27 01:59  maan
-
-       * ogg.c: do header stuff during file init rather than on first
-         read, fix serious memory leak
-
-2005-01-25 20:20  maan
-
-       * ogg.c: function reordering
-
-2005-01-24 22:57  maan
-
-       * ogg.c: whitespace cleanup
-
-2005-01-24 22:53  maan
-
-       * ogg.c: nuke another unused variable
-
-2005-01-24 22:47  maan
-
-       * ogg.c: use update_sync() rather that using fread() directly
-
-2005-01-24 22:31  maan
-
-       * ogg.c: move out_state to vcutstate, destroy streams and sync_in,
-         more debug messages
-
-2005-01-24 18:58  maan
-
-       * ogg.c: nuke unneeded variables
-
-2005-01-23 20:45  maan
-
-       * ogg.c: simplify process_headers()
-
-2005-01-23 20:28  maan
-
-       * ogg.c: use para_malloc(), simplify vcut_new(), more debugging
-         messages
-
-2005-01-14 14:32  maan
-
-       * gui.c: some small cleanups
-
-2005-01-09 23:31  maan
-
-       * gui.c: major stream I/O reearrangements (slots)
-
-2005-01-09 23:29  maan
-
-       * mysql.c: com_pidel(): reset pic_id to 1 for all files having the
-         picid that is going to be deleted
-
-2005-01-09 17:01  maan
-
-       * ogg.c: fix it again by reverting the changes made in revision
-         1.39. They caused most ogg files to be skipped..
-
-2005-01-08 13:09  maan
-
-       * ogg.c: reset packetno only once
-
-2005-01-07 20:17  maan
-
-       * ogg.c: fix some memory leaks
-
-2005-01-03 20:29  maan
-
-       * server.c: always log startup message
-
-2005-01-02 20:42  maan
-
-       * afs.c, client.c, command.c, dopey.c, fade.c, gui.c, krell.c,
-         mp3.c, mysql.c, ogg.c, para.h, pob-ogg.c, sdl_gui.c, server.c,
-         server.h, string.c: change year to 2005
-
-2005-01-02 18:47  maan
-
-       * command.c: fix another memory leak in get_sb_string(). Again
-         serious for the same reason.
-
-2005-01-02 18:34  maan
-
-       * afs.c, mysql.c: cosmetics
-
-2005-01-02 18:34  maan
-
-       * command.c: fix memory leak in compute_status_volatile(). Serious
-         because it is called by com_stat().
-
-2005-01-01 21:49  maan
-
-       * mysql.c: fix com_picadd()
-
-2005-01-01 16:56  maan
-
-       * mysql.c, server.ggo: rename default database name from music to
-         paraslash
-
-2004-12-31 23:18  maan
-
-       * mysql.c: fix three small memory leaks
-
-2004-12-31 23:17  maan
-
-       * string.c: fix serious memory leak
-
-2004-12-31 20:04  maan
-
-       * mysql.c: fix memory leak in get_dbinfo()
-
-2004-12-31 18:55  maan
-
-       * NEWS, configure.ac: paraslash 0.1.5
-
-2004-12-31 18:44  maan
-
-       * configure.ac: do not check for glib. It is only needed for
-         para_slider
-
-2004-12-31 18:43  maan
-
-       * README.mysql, mysql.c: rename command create_db back to cdb
-
-2004-12-31 18:17  maan
-
-       * pob-ogg.c: remove some suerflous variabbles
-
-2004-12-31 18:08  maan
-
-       * FEATURES, gui.c, mysql.c: cosmetics
-
-2004-12-27 01:33  maan
-
-       * gui.c, net.c: cosmetics
-
-2004-12-27 01:01  maan
-
-       * gui.c: reap children as early as possible. Avoid double killing
-         children.
-
-2004-12-27 00:08  maan
-
-       * NEWS: [no log message]
-
-2004-12-27 00:08  maan
-
-       * server.c, slider.c: cosmetics
-
-2004-12-26 23:53  maan
-
-       * command.c, server.c, server.h: Reload dbtool on SIGHUP. Move
-         restoring of signals to handle_connect() in command.c. Make
-         handle_connect() return int
-
-2004-12-26 23:06  maan
-
-       * NEWS: [no log message]
-
-2004-12-26 22:51  maan
-
-       * gui.c: use signal handling from signal.c. Should be race-free
-         now. Reduce select timeout to 200ms as that feels much better and
-         does not hurt.
-
-2004-12-26 22:46  maan
-
-       * para.h: simplified install sighandler function, new:
-         reap_child(), reap_children()
-
-2004-12-26 22:45  maan
-
-       * signal.c: new: reap_child(), reap_children() that replace
-         handle_sigchild()
-
-2004-12-26 22:43  maan
-
-       * server.c: always use the generic signal handler. That allows to
-         simplify install_sighandler() and to get rid of internal
-         handlers. Also: restore signals to default values right after
-         fork().
-
-2004-12-26 20:06  maan
-
-       * para.h: add signal function declarations
-
-2004-12-26 20:06  maan
-
-       * Makefile.in: link signal.o to server and gui
-
-2004-12-26 20:05  maan
-
-       * server.c: move signal-related functions to own file (signal.c)
-
-2004-12-26 20:04  maan
-
-       * signal.c: contains signal handling functions pulled out from
-         server.c
-
-2004-12-24 19:57  maan
-
-       * README, README.mysql, index.html, slider.c: [no log message]
-
-2004-12-24 19:11  maan
-
-       * NEWS, README: [no log message]
-
-2004-12-24 18:23  maan
-
-       * README: [no log message]
-
-2004-12-24 18:19  maan
-
-       * NEWS, README: [no log message]
-
-2004-12-24 18:13  maan
-
-       * krell.c: replace some commands by others
-
-2004-12-24 18:11  maan
-
-       * configure.ac: add detection of glib and libzmw. Commented out for
-         now.
-
-2004-12-24 18:10  maan
-
-       * autogen.sh: run configure with given command line options
-
-2004-12-24 18:09  maan
-
-       * README: add slider doku
-
-2004-12-24 18:08  maan
-
-       * NEWS: [no log message]
-
-2004-12-24 18:08  maan
-
-       * Makefile.in: Add target para_slider. Not built automatically.
-         Change Codename.
-
-2004-12-24 18:07  maan
-
-       * FEATURES: only short descriptions here. Detailed info goes to
-         README. Add slider.
-
-2004-12-22 14:47  maan
-
-       * mysql.c: better online help for com_sl()
-
-2004-12-22 14:46  maan
-
-       * command.c: use proper err_list on permission errors
-
-2004-12-22 14:45  maan
-
-       * 1.0, INSTALL: [no log message]
-
-2004-12-20 19:15  maan
-
-       * net.c: use default loglevel info for network errors. Some of them
-         are perfectly ok, so they should not pollute the log.
-
-2004-12-20 19:12  maan
-
-       * README: typo
-
-2004-12-19 18:07  maan
-
-       * README.mysql: [no log message]
-
-2004-12-19 17:38  maan
-
-       * configure.ac: paraslash 0.1.4
-
-2004-12-19 17:37  maan
-
-       * NEWS: [no log message]
-
-2004-12-19 17:20  maan
-
-       * server.c: typo
-
-2004-12-19 17:14  maan
-
-       * server.c: On dbtool change, always shutdown and re-init, even if
-         old == new
-
-2004-12-19 17:12  maan
-
-       * mysql.c: return always success on init to give the user the
-         chance to create the database
-
-2004-12-19 17:10  maan
-
-       * README.mysql: [no log message]
-
-2004-12-19 16:58  maan
-
-       * README.mysql: [no log message]
-
-2004-12-19 07:31  maan
-
-       * mysql.c: print_results(): abort if send_buffer fails
-
-2004-12-19 06:54  maan
-
-       * krell.c, server.c: cosmetics
-
-2004-12-19 06:03  maan
-
-       * command.c: cosmetics
-
-2004-12-19 05:49  maan
-
-       * command.c: report error text also on errors during initial
-         handshake
-
-2004-12-19 04:50  maan
-
-       * command.c, server.h: make some functions static
-
-2004-12-19 04:49  maan
-
-       * command.c, server.c, server.h: move handle_connect from server.c
-         to command.c
-
-2004-12-19 04:42  maan
-
-       * client.c: terminate buffer
-
-2004-12-19 04:30  maan
-
-       * client.c: do not try do decrypt server response if it has length
-         not equal to 64. Regard response as error message in that case
-         and report it instead.
-
-2004-12-19 04:30  maan
-
-       * server.c: only send error text if we have a valid err_list
-
-2004-12-18 15:35  maan
-
-       * NEWS: [no log message]
-
-2004-12-17 22:55  maan
-
-       * command.c: report proper error messages also for internal
-         commands
-
-2004-12-17 22:33  maan
-
-       * command.c, mysql.c, pob-ogg.c, server.c, server.h: introduce
-         err_list, a list of error messages. Report error message
-         corresponding to return value of command handler to client if
-         this value is negative
-
-2004-12-17 22:29  maan
-
-       * gui.c: write chunk if buffer contains data even if reader has
-         died
-
-2004-12-17 22:28  maan
-
-       * NEWS: [no log message]
-
-2004-12-17 14:51  maan
-
-       * ogg.c: nuke unused dummy variable
-
-2004-12-17 14:06  maan
-
-       * NEWS: [no log message]
-
-2004-12-17 13:21  maan
-
-       * mysql.c: make stradd/picadd overwrite previous definition if it
-         exists, rather than returning errors
-
-2004-12-16 02:26  maan
-
-       * ogg.c: further simplify read_chunk(): nuke first_page logic
-
-2004-12-15 22:45  maan
-
-       * gui.c: oops, forgot to remove the if-statement
-
-2004-12-15 22:30  maan
-
-       * gui.c: remove unneeded variable stream_buf_bytes
-
-2004-12-15 20:32  maan
-
-       * ogg.c: always use ogg_stream_flush() which simplifies page_out()
-
-2004-12-15 20:14  maan
-
-       * ogg.c: further simplify read_chunk(), kill have_packet
-
-2004-12-15 19:40  maan
-
-       * ogg.c, pob-ogg.c, server.c: simplify read_chunk(), avoid frequent
-         page_out errors
-
-2004-12-15 16:45  maan
-
-       * gui.c: fix serious typo: stream WRITE command option was used for
-         READING.
-
-2004-12-15 03:41  maan
-
-       * ogg.c: trivial cleanups
-
-2004-12-13 17:05  maan
-
-       * krell.c: reap the children to avoid zombies. Fixes zombie flood
-         if started with no server running.
-
-2004-12-13 17:02  maan
-
-       * command.c: report server pid instead of current database tool as
-         the latter is already contained in output of stat
-
-2004-12-11 17:04  maan
-
-       * gui.c: reintroduce one second sleep on errors to avoid busy loops
-         if server is unavailable
-
-2004-12-11 17:01  maan
-
-       * mysql.c: fix playing in case no stream was selected
-
-2004-12-11 16:38  maan
-
-       * afs.c: enter stopped mode when only invalid files were found
-
-2004-12-11 16:06  maan
-
-       * gui.c: simplify SIGINT handling, get rid of sleep(1)
-
-2004-12-11 15:43  maan
-
-       * gui.c: Hm, signal handling must have changed in recent linux
-         kernels. Make SIGINT work again.
-
-2004-12-11 14:01  maan
-
-       * mysql.c: com_hist(): query got messed up, fix it.
-
-2004-12-10 22:37  maan
-
-       * gui.c: nuke query_stream command. Can be done by key mappings
-
-2004-12-10 22:32  maan
-
-       * mysql.c: com_strq(): Use current stream if invoked without args
-
-2004-12-10 18:01  maan
-
-       * mysql.c: com_upd(): comment out noisy debug message
-
-2004-12-10 17:55  maan
-
-       * mysql.c: check return value of send_buffer()
-
-2004-12-10 16:50  maan
-
-       * server.c: cosmetics
-
-2004-12-10 16:50  maan
-
-       * Makefile.in: change codename
-
-2004-12-10 16:49  maan
-
-       * mysql.c: oops, com_picadd() got lost during merging. Here it is
-         again.
-
-2004-12-10 15:42  maan
-
-       * server.c: simplify handle_connect()
-
-2004-12-10 14:35  maan
-
-       * NEWS: [no log message]
-
-2004-12-10 14:35  maan
-
-       * server.c: forgotten goto
-
-2004-12-10 14:20  maan
-
-       * server.c: cosmetics
-
-2004-12-10 13:21  maan
-
-       * NEWS, configure.ac: bump version to 0.1.3
-
-2004-12-10 13:04  maan
-
-       * NEWS: [no log message]
-
-2004-12-09 22:12  maan
-
-       * server.c, server.ggo: new option: dbtool. Choose dbtool which
-         dbtool is used on startup
-
-2004-12-09 21:21  maan
-
-       * mysql.c: update mmd->dbinfo on successful init
-
-2004-12-09 21:21  maan
-
-       * dopey.c: terminate song song list by NULL
-
-2004-12-09 20:46  maan
-
-       * mysql.c: kill superfluous semicolen that caused gcc-2.95.4 to
-         fail
-
-2004-12-09 20:28  maan
-
-       * dopey.c: make get_song_list() return NULL on errors, rather than
-         an empty list
-
-2004-12-09 20:19  maan
-
-       * command.c, dopey.c, mysql.c, server.c, server.h: handle dbtool
-         change by server. The client only sets mmd->dbt_change which is
-         noticed by server when select() returns.
-
-2004-12-09 19:35  maan
-
-       * command.c, mysql.c, server.h: nuke modus from struct command
-
-2004-12-09 19:33  maan
-
-       * mysql.c: rename cdb to create_db
-
-2004-12-09 19:13  maan
-
-       * FEATURES: [no log message]
-
-2004-12-09 19:13  maan
-
-       * server.ggo, mysql.c: prefix all mysql options by mysql_
-
-2004-12-09 19:02  maan
-
-       * INSTALL: [no log message]
-
-2004-12-09 18:46  maan
-
-       * README: [no log message]
-
-2004-12-09 18:30  maan
-
-       * server.c: fix autoplay, move mmd_lock before networ init
-
-2004-12-09 18:21  maan
-
-       * index.html, mysql.c: cosmetics/typos
-
-2004-12-09 18:21  maan
-
-       * gui.c, sdl_gui.c: replace obsolete score by uptime
-
-2004-12-09 18:20  maan
-
-       * command.c: do not include seconds in server's uptime and report
-         the uptime in com_stat()
-
-2004-12-09 17:56  maan
-
-       * README.mysql: update for 0.1.3
-
-2004-12-09 17:27  maan
-
-       * Makefile.in, index.html: change README.dbtool to README.mysql
-
-2004-12-09 17:25  maan
-
-       * NEWS, README.mysql: former README.dbtool
-
-2004-12-09 17:24  maan
-
-       * README.dbtool: renamed to README.mysql
-
-2004-12-09 17:04  maan
-
-       * command.c, server.c, server.h: field 'initialized' in struct
-         dbtool was a bad idea. We must allow switching to the mysql
-         dbtool even if its init routine failed because that's the only
-         way to initially create the database.
-
-2004-12-09 02:21  maan
-
-       * gui.c, sdl_gui.c: replace obsolete streams in stat_items[] by
-         dbtool
-
-2004-12-09 02:20  maan
-
-       * command.c: print current dbtool in com_stat()
-
-2004-12-09 02:07  maan
-
-       * NEWS: [no log message]
-
-2004-12-09 02:02  maan
-
-       * NEWS: [no log message]
-
-2004-12-09 01:03  maan
-
-       * command.c: new command: ctd: change dbtool on the fly
-
-2004-12-09 01:03  maan
-
-       * mysql.c: remove the silly try to report the cause of an error.
-         Provide shutdown function (deactivated for now)
-
-2004-12-09 01:00  maan
-
-       * dopey.c, server.c, server.h: init all dbtools on startup. Fall
-         back to dopey if all other dbtools fail to init.
-
-2004-12-08 23:34  maan
-
-       * mysql.c: rename cdb to create_db
-
-2004-12-08 23:23  maan
-
-       * dbtool.conf.sample: long obsolete
-
-2004-12-08 23:18  maan
-
-       * dbtool_template.c: got obsoleted by dopey
-
-2004-12-08 23:16  maan
-
-       * dbtool.c, dbtool.ggo: no longer needed
-
-2004-12-08 23:13  maan
-
-       * para.h, string.c: fix some compiler warnings
-
-2004-12-08 23:05  maan
-
-       * dopey.c: redirect stderr of find command to null
-
-2004-12-08 22:46  maan
-
-       * dopey.c: fix memory leak
-
-2004-12-08 22:46  maan
-
-       * Makefile.in, configure.ac: fixup autoconf stuff
-
-2004-12-08 22:15  maan
-
-       * net.c: remove unused variable
-
-2004-12-08 22:14  maan
-
-       * Makefile.in, configure.ac, server.c: Fix build on systems without
-         mysql. They'll get dopey ;) Do not build obsolete dbtool any
-         more.
-
-2004-12-08 21:58  maan
-
-       * dopey.c: dopey help update
-
-2004-12-08 21:56  maan
-
-       * Makefile.in, command.c, dopey.c, server.c, server.ggo, server.h:
-         the dopey database tool, codename 'ddt'
-
-2004-12-08 20:25  maan
-
-       * para.h, string.c: use const qualifiers in para_strcat()
-
-2004-12-08 20:25  maan
-
-       * command.c:  report dbtool info in com_si()
-
-2004-12-08 20:10  maan
-
-       * afs.c, command.c, mysql.c, server.c, server.h: switch from single
-         struct dbtool to array of struct dbtool as a preparation for
-         supporting several database tools.
-
-2004-12-08 04:24  maan
-
-       * afs.c, command.c, server.h: change type of mmd->mtime to time_t
-
-2004-12-08 04:10  maan
-
-       * command.c, server.c, server.h: kill num_connects(). A simple
-         variable is enough.
-
-2004-12-08 04:00  maan
-
-       * server.c: get_user(): scan at most 200 chars per entry avoiding
-         possible buffer overflow with bad user_list file
-
-2004-12-08 03:58  maan
-
-       * mysql.c: Fix high-quality double free bug which was sitting there
-         for quite some time since it was hard to trigger. This sucker
-         made _subsequent_ sql queries hang. Kill it with pleasure.
-
-2004-12-08 02:34  maan
-
-       * exec.c: fix dup2 stderr
-
-2004-12-08 02:16  maan
-
-       * mysql.c: rename _clean to clean
-
-2004-12-08 02:16  maan
-
-       * gui.c: do not kill writer immediately when reader dies.
-
-2004-12-08 02:01  maan
-
-       * gui.c: Nuke com_sl. Can be done via key mapping
-
-2004-12-08 00:49  maan
-
-       * command.c: nasty use before init bug
-
-2004-12-07 22:50  maan
-
-       * command.c: simplify cmd_perms_itohuman()
-
-2004-12-07 22:44  maan
-
-       * afs.c, command.c, server.h: move afs_get_status_flags() to
-         command.c. It's only needed there, so make it static. Use dynamic
-         buffer.
-
-2004-12-07 22:38  maan
-
-       * command.c: afs_status_tohuman(): return dynamic buffer
-
-2004-12-07 22:33  maan
-
-       * afs.c, command.c, server.h: move afs_status_tohuman() to
-         command.c. It's only needed there, so make it static.
-
-2004-12-07 22:24  maan
-
-       * server.c: report loglevel on startup
-
-2004-12-07 22:23  maan
-
-       * para.h: kill unused loglevel ALERT
-
-2004-12-07 22:23  maan
-
-       * afs.c: remove some old code
-
-2004-12-07 22:11  maan
-
-       * afs.c, mysql.c, ogg.c, server.c: some smallish cleanups
-
-2004-12-07 22:10  maan
-
-       * server.ggo: set default loglevel to 4
-
-2004-12-07 21:28  maan
-
-       * afs.c, server.c: simplify setup_stream_command(). Abort on
-         errors.
-
-2004-12-07 02:09  maan
-
-       * mysql.c: reintroduce com_la as it is needed by dbadm
-
-2004-12-07 01:56  maan
-
-       * command.c: do not divide by zero
-
-2004-12-07 01:51  maan
-
-       * ogg.c: loglevel adjustments
-
-2004-12-07 01:38  maan
-
-       * command.c: Delete unused stuff. Fairly impressive patch :)
-
-2004-12-07 01:32  maan
-
-       * command.c: sort commands alphabetically
-
-2004-12-07 01:23  maan
-
-       * command.c, server.c: clean up com_sc()
-
-2004-12-07 01:07  maan
-
-       * command.c: clean up com_stat() and compute_status_volatile()
-
-2004-12-07 00:18  maan
-
-       * command.c: make uptime_str() return dynamically allocated buffer.
-         Clean up com_si().
-
-2004-12-07 00:10  maan
-
-       * command.c: rename compute_sb_string() to get_sb_string() and
-         return dynamically allocated buffer.
-
-2004-12-06 23:37  maan
-
-       * command.c: rename compute_status() to get_status() and return
-         dynamically allocated buffer
-
-2004-12-06 23:00  maan
-
-       * command.c: make cmd_perms_itohuman() return dynamically allocated
-         4 char permission string. Used by com_help(). This makes
-         com_perms obsolete. Comment out
-
-2004-12-06 22:15  maan
-
-       * command.c: rename find_cmd(). No good name.
-
-2004-12-06 22:11  maan
-
-       * command.c: make find_command() also return the handler (server or
-         dbtool). That makes cmd_handler_itohuman() obsolete, and in turn
-         other functions unused. Comment out what is unneeded.
-
-2004-12-06 21:51  maan
-
-       * command.c: found still more lcl stuff, kill unused var:
-         dbtool_has_pic
-
-2004-12-06 21:49  maan
-
-       * command.c: comment out remaining lcl stuff
-
-2004-12-06 21:48  maan
-
-       * command.c, string.c: move chop() from command.c to string.c
-
-2004-12-06 21:45  maan
-
-       * command.c: comment out unused lcl_init()
-
-2004-12-06 21:42  maan
-
-       * server.c: deactivate lcl stuff
-
-2004-12-06 21:41  maan
-
-       * command.c: fix silly bug that crept in by the last patch
-
-2004-12-06 21:36  maan
-
-       * command.c: simplify send_descriptions(), find_command() and fix
-         com_help()
-
-2004-12-06 20:28  maan
-
-       * command.c: kill useless dummy entries, documentation update.
-
-2004-12-06 20:21  maan
-
-       * afs.c, command.c, server.c: Since we hold a dbtool pointer in mmd
-         there is no need to export it any longer
-
-2004-12-06 20:06  maan
-
-       * command.c: make com_perms() use the internal command list rather
-         than old linked command list
-
-2004-12-06 20:05  maan
-
-       * mysql.c: set name in init
-
-2004-12-06 20:04  maan
-
-       * server.c: set mmd->dbtool in do_inits()
-
-2004-12-06 20:03  maan
-
-       * server.h: New field in struct dbtool: name to be used by
-         com_perms(). New field in mmd: dbtool, a pointer to the currently
-         selected database tool, also used in com_perms(). Currently,
-         there is only one, but this will change once ddt, the dopey
-         database tool, is born.
-
-2004-12-06 19:10  maan
-
-       * command.c: comment out obsolete server-based com_pic()
-
-2004-12-06 04:08  maan
-
-       * mysql.c: make com_cam() also copy the meta data
-
-2004-12-06 03:25  maan
-
-       * mysql.c: as we have extern struct conf in mysql.c there is no
-         need to shadow its values
-
-2004-12-06 03:11  maan
-
-       * mysql.c: some more cosmetics
-
-2004-12-06 03:04  maan
-
-       * mysql.c: only while space cleanups
-
-2004-12-06 02:57  maan
-
-       * mysql.c: nuke get_very_last() which was commented out anyway
-
-2004-12-06 02:52  maan
-
-       * mysql.c: reorganize functions,  simplify com_info()
-
-2004-12-06 02:31  maan
-
-       * mysql.c: new function: get_dir(). Switch back to three-line
-         dbinfo output
-
-2004-12-06 01:39  maan
-
-       * mysql.c: fix get_dbinfo()
-
-2004-12-06 01:20  maan
-
-       * mysql.c: simplify com_cdb() a bit
-
-2004-12-06 01:10  maan
-
-       * mysql.c: port the last dbtool command: com_picch()
-
-2004-12-06 00:56  maan
-
-       * mysql.c: port com_piclistl()
-
-2004-12-06 00:49  maan
-
-       * mysql.c: port com_picdel()
-
-2004-12-06 00:40  maan
-
-       * mysql.c: port com_picass()
-
-2004-12-06 00:22  maan
-
-       * mysql.c: port com_pic()
-
-2004-12-05 23:48  maan
-
-       * mysql.c: port com_verb()
-
-2004-12-05 23:37  maan
-
-       * mysql.c: simplify get_all_attributes(), fix two nasty
-         free()-related bugs
-
-2004-12-05 22:59  maan
-
-       * mysql.c: port com_cam()
-
-2004-12-05 20:51  maan
-
-       * mysql.c: port com_da()
-
-2004-12-05 20:46  maan
-
-       * mysql.c, para.h, string.c: introduce para_basename(), port
-         com_na()
-
-2004-12-05 17:20  maan
-
-       * mysql.c: port com_hist()
-
-2004-12-05 17:13  maan
-
-       * mysql.c: port mbox()
-
-2004-12-05 16:48  maan
-
-       * mysql.c: port com_ls()
-
-2004-12-05 07:12  maan
-
-       * command.c: fix segfault on invalid commands
-
-2004-12-05 07:10  maan
-
-       * server.c: report error message if command failed
-
-2004-12-05 06:03  maan
-
-       * server.c: nuke unused get_song_list()
-
-2004-12-05 05:24  maan
-
-       * mysql.c: port com_last()
-
-2004-12-05 05:04  maan
-
-       * mysql.c: simplify and fix get_all_attributes(), port com_laa()
-
-2004-12-05 04:36  maan
-
-       * mysql.c: refresh mmd->dbinfo also in commands sa and cs
-
-2004-12-05 04:20  maan
-
-       * mysql.c: use mmd-based get_current_song() instead of mysql's
-         entry with newest lastplayed time. That matters in view of
-         com_skip().
-
-2004-12-05 03:46  maan
-
-       * command.c: clean up com_help
-
-2004-12-05 03:20  maan
-
-       * mysql.c: update help for vrfy and us
-
-2004-12-05 03:20  maan
-
-       * command.c: simplify lcl_find and switch to help for new commands
-
-2004-12-05 02:40  maan
-
-       * mysql.c: port com_sa(). Simplify get_last()/get_very_last().
-
-2004-12-05 00:36  maan
-
-       * mysql.c: port com_skip()
-
-2004-12-05 00:26  maan
-
-       * mysql.c: activate new cdb/clean/upd/vrfy commands
-
-2004-12-05 00:15  maan
-
-       * mysql.c: port com_strq()
-
-2004-12-04 23:59  maan
-
-       * mysql.c: port com_strdel(), change current stream to '(none)' if
-         it gets deleted by strdel
-
-2004-12-04 23:39  maan
-
-       * mysql.c: make conf an extern variable in mysql.c, so there is no
-         need to pass it via init. Port com_cdb(), several small fixes.
-
-2004-12-04 23:37  maan
-
-       * server.c, server.h: make conf an extern variable in mysql.c, so
-         there is no need to pass it via init
-
-2004-12-04 21:23  maan
-
-       * mysql.c: port com_vrfy/clean
-
-2004-12-04 20:43  maan
-
-       * mysql.c: port com_upd(). This is not backward compatible. It
-         requires modification of find_cmd in server.conf. So it is
-         disabled until vrfy/clean are also ported.
-
-2004-12-04 20:40  maan
-
-       * exec.c: only close fd[1] if we dup2'ed it
-
-2004-12-04 20:39  maan
-
-       * server.c: abort if dbtool init failed
-
-2004-12-04 15:42  maan
-
-       * mysql.c: port stradd/picadd
-
-2004-12-04 15:33  maan
-
-       * server.ggo: nuke sstream option
-
-2004-12-04 14:31  maan
-
-       * fade.c: use csp instead of play
-
-2004-12-04 14:27  maan
-
-       * mysql.c: new command: csp (change stream and play). Needed since
-         com_play() no longer knows about streams and ignores its
-         arguments.
-
-2004-12-04 14:24  maan
-
-       * afs.c: update_mmd(): fix logging message for unknown audio format
-
-2004-12-04 14:15  maan
-
-       * afs.c: get_song(): log filenames as they are tried to open
-
-2004-12-04 02:23  maan
-
-       * mysql.c: fix com_sl. It used row[1] in results which contained
-         only one column. Fix help of com_streams().
-
-2004-12-04 02:21  maan
-
-       * afs.c: comment out obsolete popen dabtool us
-
-2004-12-03 21:36  maan
-
-       * mysql.c: omit current_stream in ps/ns/streams
-
-2004-12-03 21:28  maan
-
-       * command.c, server.h: com_stat: don't print score
-
-2004-12-03 20:51  maan
-
-       * mysql.c: make ps/ns refresh dbinfo
-
-2004-12-03 20:36  maan
-
-       * mysql.c: port com_ps() and com_ns()
-
-2004-12-03 20:08  maan
-
-       * afs.c, command.c, server.c, server.h: comment out obsolete stream
-         handling code. Internal commands don't know about streams any
-         more
-
-2004-12-03 19:46  maan
-
-       * afs.c, mysql.c, server.c, server.h: make dbtool write directly to
-         shared memory area, get_song_list() now returns char** (full path
-         list without scores)
-
-2004-12-03 17:21  maan
-
-       * afs.c, server.c: comment out old update_dbinfo()
-
-2004-12-03 00:17  maan
-
-       * mysql.c: also print score and stream in dbinfo
-
-2004-12-02 23:24  maan
-
-       * afs.c, mysql.c: use info string provided by  dbtool's
-         get_song_list (overwrite output of old para_dbtool). Other
-         smallish fixes also.
-
-2004-12-02 20:54  maan
-
-       * mysql.c: after the fork we will never see when the stream changes
-
-2004-12-02 20:53  maan
-
-       * mysql.c: remove global variable current_stream. It just doesn't
-         work because
-
-2004-12-02 03:18  maan
-
-       * afs.c, mysql.c: port com_cs() and activate new stream handling
-
-2004-12-02 01:43  maan
-
-       * afs.c: new implementation of get_song() that calls dbtool's
-         get_song function directly rather than calling popen_dbtool()
-         (not yet activated since com_cs is not yet ported)
-
-2004-12-02 01:33  maan
-
-       * mysql.c: new function get_song list to be called directly by afs
-         on song change via server's glue function
-
-2004-12-02 01:29  maan
-
-       * server.c: get_song glue function
-
-2004-12-02 01:28  maan
-
-       * server.h: new struct: db_song_info
-
-2004-12-02 01:27  maan
-
-       * string.c: para_strdup: return empty string if arg is NULL
-
-2004-12-01 22:10  maan
-
-       * mysql.c: port com_streams()
-
-2004-12-01 21:57  maan
-
-       * mysql.c: activate new com_sl()
-
-2004-12-01 04:39  maan
-
-       * mysql.c: fix theoretical memory leak
-
-2004-12-01 04:23  maan
-
-       * mysql.c: get_query: make sure to return NULL on errors
-
-2004-12-01 04:20  maan
-
-       * mysql.c: remove length[] check. We cover length[0]==0 anyway.
-
-2004-12-01 04:11  maan
-
-       * mysql.c: cosmetics
-
-2004-12-01 04:04  maan
-
-       * mysql.c: remove unnused variable 'ret'. reduce size of the last
-         remaining static buffer for sscanf.
-
-2004-12-01 04:00  maan
-
-       * mysql.c: make get_query() return char*, reducing one more static
-         buffer allocation. Free also accept/deny_opts ;)
-
-2004-12-01 03:42  maan
-
-       * mysql.c: further simplify string handling in where_clause
-
-2004-12-01 03:35  maan
-
-       * mysql.c: simplify where_clause logic, free all variables
-
-2004-12-01 02:42  maan
-
-       * mysql.c: get_query: further removal of static buffers
-
-2004-12-01 02:32  maan
-
-       * mysql.c: get_query: remove underscores as it is no longer needed
-
-2004-12-01 02:30  maan
-
-       * mysql.c: Clean up get_query, part II
-
-2004-12-01 02:17  maan
-
-       * mysql.c, para.h, string.c: remove 'dest' argument from
-         s_a_r_list(). Clean up get_query, part I
-
-2004-12-01 00:43  maan
-
-       * mysql.c: port com_sl()
-
-2004-12-01 00:40  maan
-
-       * net.c, string.c: cosmetics
-
-2004-12-01 00:29  maan
-
-       * string.c: loglevel adjustments
-
-2004-12-01 00:25  maan
-
-       * string.c: fix s_a_r in case of several matches
-
-2004-11-30 23:58  maan
-
-       * para.h, string.c: no more static buffers in s_a_r() and friends
-
-2004-11-30 22:18  maan
-
-       * dbtool.c, para.h, string.c: port regex stuff from dbtool.c to
-         string.c
-
-2004-11-30 21:38  maan
-
-       * net.c, para.h: new function: send_va_buffer. Oh yeah!
-
-2004-11-30 21:14  maan
-
-       * Makefile.in: link string.o to all executables that need it
-
-2004-11-30 20:42  maan
-
-       * mysql.c: activate new com_info() (by changing _info to info)
-
-2004-11-30 20:41  maan
-
-       * command.c: com help: also print commands from new api
-
-2004-11-30 20:25  maan
-
-       * Makefile.in, mysql.c, para.h, string.c: contains string handling
-         functions that might be useful for any part of paraslash
-
-2004-11-30 20:10  maan
-
-       * command.c, server.c, server.h: make dbtool an extern variable in
-         command.c
-
-2004-11-30 03:07  maan
-
-       * mysql.c: small cleanups in com_info()
-
-2004-11-30 02:39  maan
-
-       * mysql.c: simplify sql query for dir.dir
-
-2004-11-30 02:30  maan
-
-       * mysql.c: simplify get_atts a lot
-
-2004-11-30 02:03  maan
-
-       * mysql.c: port com_info, introduce para_strcat and para_strdup
-
-2004-11-30 00:24  maan
-
-       * mysql.c: use dynamic buffers provided by new make_message().
-         Introduce para_malloc and para_realloc to avoid checking malloc's
-         return value for each malloc call
-
-2004-11-29 21:54  maan
-
-       * mysql.c: port com_us to the new api
-
-2004-11-29 02:17  maan
-
-       * mysql.c: cosmetics
-
-2004-11-29 02:08  maan
-
-       * Makefile.in, mysql.c, server.c, server.h: port the first command,
-         summary, to the new api. It is called _summary for now
-
-2004-11-29 01:10  maan
-
-       * mysql.c, server.c, server.h: check config file directly in
-         mysql_init
-
-2004-11-29 00:40  maan
-
-       * mysql.c, server.c, server.h: port config file parsing from
-         dbtool.c to mysql.c
-
-2004-11-28 23:47  maan
-
-       * command.c, server.c, server.h: pass dbtool pointer to parse_cmd
-         and actually search the (new, but still empty) dbtool list for
-         commands
-
-2004-11-28 23:29  maan
-
-       * command.c: first search the commands in (emtpy, by now) command
-         list which was returned by mysql_init
-
-2004-11-28 23:11  maan
-
-       * command.c, server.c, server.h: simplify command handling (let
-         functions return struct command* instead of struct
-         linked_cmd_list* which is going to be removed soon)
-
-2004-11-28 23:00  maan
-
-       * Makefile.in, NEWS, server.c, server.ggo, server.h: first
-         preparations (that shouldn't break anything) to include dbtool
-         inside para_server
-
-2004-11-28 22:58  maan
-
-       * mysql.c: this will replace dbtool.c
-
-2004-11-28 22:10  maan
-
-       * Makefile.in, NEWS, configure.ac: bump version to 0.1.2
-
-2004-11-28 21:41  maan
-
-       * afs.c: afs_send_chunk: close stream writer on errors
-
-2004-11-28 21:40  maan
-
-       * dbtool.c: cosmetics
-
-2004-11-28 20:15  maan
-
-       * dbtool.c: make all commands take 3 arguments as required by
-         struct command from server.h
-
-2004-11-28 20:04  maan
-
-       * dbtool.c: simplify decls
-
-2004-11-28 20:01  maan
-
-       * dbtool.c: remove unneeded #includes
-
-2004-11-28 19:57  maan
-
-       * dbtool.c: rename .func to .handler in struct com to match it more
-         closely to struct command defined in server.h
-
-2004-11-25 21:05  maan
-
-       * gui.c: C+L does not clear the screen. Do not claim it will do so
-         in online help
-
-2004-11-25 21:02  maan
-
-       * server.ggo: better online help
-
-2004-11-12 14:56  maan
-
-       * NEWS: [no log message]
-
-2004-11-12 14:56  maan
-
-       * dbtool.c: rename ca to cam (copy all meta data). It now also
-         copies numplayed and lastplayed time as well as the picture id.
-
-2004-11-05 15:40  maan
-
-       * NEWS, configure.ac: bump version to 0.1.1
-
-2004-11-05 15:28  maan
-
-       * NEWS: [no log message]
-
-2004-11-05 15:27  maan
-
-       * ogg.c: add gpl header and mention that it is derived from vcut.c
-
-2004-11-05 15:25  maan
-
-       * CREDITS: add Michael Smith (vcut)
-
-2004-11-05 15:17  maan
-
-       * dbtool.c: new command: mbox
-
-2004-11-05 13:44  maan
-
-       * server.c: set afs_status_flags to AFS_PLAYING | AFS_NEXT if -a
-         and -s were given
-
-2004-11-01 19:20  maan
-
-       * NEWS: [no log message]
-
-2004-10-27 18:39  maan
-
-       * README: add para_krell
-
-2004-10-25 04:21  maan
-
-       * mp3.c, ogg.c: set default values for open_cmd. Fixes segfault
-         with empty config file.
-
-2004-10-25 03:51  maan
-
-       * dbtool.c: get_a: return -1 if database has no attributes
-
-2004-10-25 00:09  maan
-
-       * sdl_gui.c: refresh pic after executing command
-
-2004-10-24 23:32  maan
-
-       * FEATURES: [no log message]
-
-2004-10-24 23:29  maan
-
-       * autogen.sh: simple script for autoconf etc
-
-2004-10-24 20:50  maan
-
-       * addons/gkrellm_cmd_display/Makefile: remove para_ctrl stuff
-
-2004-10-24 20:49  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: it is now called krell.c
-         and belongs to the main tree
-
-2004-10-24 20:41  maan
-
-       * configure.ac: check for gtk2 and build para_krell only if it was
-         found
-
-2004-10-24 20:40  maan
-
-       * Makefile.in: new target: para_krell
-
-2004-10-24 20:39  maan
-
-       * krell.c: former para_ctrl
-
-2004-10-24 05:01  maan
-
-       * dbtool.c: cosmetics
-
-2004-10-22 21:26  maan
-
-       * Makefile.in: change codename
-
-2004-10-22 21:26  maan
-
-       * NEWS: [no log message]
-
-2004-10-22 18:42  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: new macro PRINTF
-
-2004-10-22 18:04  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: More duku, __func__
-
-2004-10-22 15:15  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: do not eat cpu if there
-         is no para_server
-
-2004-10-22 13:18  maan
-
-       * configure.ac: bump version to 0.1.0
-
-2004-10-22 13:00  maan
-
-       * mp3.c, ogg.c: cosmetics
-
-2004-10-17 21:46  maan
-
-       * index.html: mention that all files are signed
-
-2004-10-17 21:20  maan
-
-       * PUBLIC_KEY: gnupg public key
-
-2004-10-17 19:59  maan
-
-       * dbtool.c: kill unused init_seed()
-
-2004-10-17 17:58  maan
-
-       * command.c: compute percentage defensively also in com_ff
-
-2004-10-17 17:55  maan
-
-       * gui.c: kill all decoders if status pipe dies
-
-2004-10-17 17:54  maan
-
-       * command.c: compute percentage defensively
-
-2004-10-17 17:05  maan
-
-       * README.dbtool: english improvements
-
-2004-10-14 23:17  maan
-
-       * dbtool.c: remove the magic recursive randomizer. It is no longer
-         used since Lastplayed is never null
-
-2004-10-14 22:45  maan
-
-       * gui.c: clear top win in init_wins. This should fix stray
-         characters in top.win after resizing the xterm
-
-2004-10-14 22:32  maan
-
-       * gui.c: kill unused #define
-
-2004-10-14 22:28  maan
-
-       * gui.c: do not include header files twice
-
-2004-10-14 21:42  maan
-
-       * README.dbtool, dbtool.c, dbtool.ggo: replace option emph_np by
-         more general default_score. This actually _reduces_ code size.
-
-2004-10-14 20:54  maan
-
-       * dbtool.c: simplify s_a_r_array
-
-2004-10-13 23:19  maan
-
-       * dbtool.c: insert _valid_ default value into colum lastplayed for
-         new songs
-
-2004-10-11 20:30  maan
-
-       * Makefile.in: use new picdir for the internal pics of para_server
-
-2004-10-11 20:21  maan
-
-       * pics/paraslash/: default.jpg, no_pics.jpg: moved from ..
-
-2004-10-11 20:20  maan
-
-       * pics/: default.jpg, no_pics.jpg: moved to pics/paraslash
-
-2004-10-11 20:18  maan
-
-       * pics/web/paraslash.png: moved from ../
-
-2004-10-11 20:18  maan
-
-       * pics/paraslash.png: moved to web
-
-2004-10-11 20:17  maan
-
-       * Makefile.in: small cleanups
-
-2004-10-11 19:57  maan
-
-       * pics/screenshots/: gui-2004-07-11.png, gui-2004-09-02.png,
-         gui-old.png, loglevel1-2004-07-28.txt, sdl_gui.jpg: more or less
-         old screenshots
-
-2004-10-11 19:16  maan
-
-       * index.html: it is now called manual.html, not manual.txt.html
-
-2004-10-11 19:15  maan
-
-       * Makefile.in: nuke targets tags and web as they only work on my
-         local machine anyway
-
-2004-10-11 17:59  maan
-
-       * pics/paraslash.png: logo from web page
-
-2004-10-11 17:20  maan
-
-       * index.html: the paraslash web page
-
-2004-10-10 23:10  maan
-
-       * NEWS: [no log message]
-
-2004-10-10 23:09  maan
-
-       * para.h: no more restrictions on comand length (I hope)
-
-2004-10-10 23:09  maan
-
-       * command.c: chop() can not be static since it is used by server
-         also
-
-2004-10-10 23:08  maan
-
-       * dbtool.c: cosmetics
-
-2004-10-10 23:02  maan
-
-       * client.c, server.c: no more restrictions on comand length (I
-         hope)
-
-2004-10-10 22:30  maan
-
-       * client.c: cosmetics
-
-2004-10-10 20:22  maan
-
-       * client.c: cosmetics
-
-2004-10-10 20:19  maan
-
-       * client.c: fix buffer overflow with long command lines
-
-2004-10-10 19:55  maan
-
-       * client.c: cosmetics
-
-2004-10-08 21:18  maan
-
-       * configure.ac: it runs fine with 2.53 (debian stable)
-
-2004-10-08 21:15  maan
-
-       * pob-ogg.ggo: forgotten to check it in
-
-2004-10-07 22:11  maan
-
-       * afs.c, gui.c, mp3.c, ogg.c, server.h: make gcc 2.95.4 happy
-
-2004-10-07 18:21  maan
-
-       * gui.c: close status pipe on read errors
-
-2004-10-07 18:10  maan
-
-       * command.c: free mem in correct order, fixes segfault on HUP
-
-2004-10-07 02:11  maan
-
-       * sdl_gui.c: make functions static where possible
-
-2004-10-07 00:18  maan
-
-       * README.dbtool: [no log message]
-
-2004-10-06 23:12  maan
-
-       * sdl_gui.c: make pictures work again
-
-2004-10-06 21:07  maan
-
-       * command.c, gui.c, server.c: cosmetics
-
-2004-10-06 20:35  maan
-
-       * command.c: make functions static where possible
-
-2004-10-06 20:32  maan
-
-       * command.c, server.h: use dynamically aloocated buffers in struct
-         command
-
-2004-10-06 19:33  maan
-
-       * dbtool.c: kill dead code in com_vrfy(): check for NULL entries
-
-2004-10-06 19:28  maan
-
-       * dbtool.c: always check return value of init()
-
-2004-10-06 19:24  maan
-
-       * dbtool.c: com_ca: shut down mysql connection
-
-2004-10-06 19:15  maan
-
-       * dbtool.c: fix memory leak
-
-2004-10-06 18:55  maan
-
-       * dbtool.c: free correct pointer
-
-2004-10-06 18:53  maan
-
-       * dbtool.c: refuse to insert too long stream/pic names
-
-2004-10-06 18:42  maan
-
-       * server.c: fix autoplay
-
-2004-10-06 18:33  maan
-
-       * README.dbtool: para_client hup is no longer necessary
-
-2004-10-06 18:30  maan
-
-       * afs.c, command.c, server.c, server.h: read stream list on demand
-         instead of keeping a (possibly obsolete) copy in memory. This
-         fixes com_stat which used stale pointers to current stream name
-         if server reloaded its stream_list due to SIGHUP. Another
-         advantage is that we don't need to send SIGHUP to the server any
-         more when modifying the stream list
-
-2004-10-04 03:14  maan
-
-       * command.c: fix integer overflow for very long (ogg) files
-
-2004-10-04 02:53  maan
-
-       * afs.c: return NULL on next request
-
-2004-10-04 02:52  maan
-
-       * ogg.c: end_of_input: close file and, more important, reset
-         short_write. This bug could cause files to be skipped. Ogg is
-         almost usable now
-
-2004-10-04 02:49  maan
-
-       * server.c: kill unneeded variable sw_pid
-
-2004-10-03 20:59  maan
-
-       * dbtool.c: set pic_id to 1 for new files
-
-2004-10-03 02:24  maan
-
-       * dbtool.c: com_sl: comment out debugging message
-
-2004-10-03 01:42  maan
-
-       * dbtool.c: set all but one filed as notnull. NULLs suck when
-         sorting
-
-2004-10-03 01:06  maan
-
-       * client.c: fix wrong order of arguments to clog that could cause
-         random segfaults
-
-2004-09-30 01:40  maan
-
-       * gui.c: fixup rereading of config file
-
-2004-09-27 02:53  maan
-
-       * command.c: fix integer overflow in stat output for very long ogg
-         files
-
-2004-09-27 02:27  maan
-
-       * ogg.c: dont update time when sending first page or when
-         repos_request
-
-2004-09-27 01:45  maan
-
-       * gui.c: reset buflen when deactivating stream io
-
-2004-09-27 01:28  maan
-
-       * gui.c: delete old stream io handling code
-
-2004-09-27 00:00  maan
-
-       * gui.c: loop over all audio formats in do_select and kill decoder
-         when inactive
-
-2004-09-26 19:09  maan
-
-       * gui.c: clog: print msg to stderr if curses not yet active
-
-2004-09-26 19:07  maan
-
-       * dbtool.c: so not duplicate info
-
-2004-09-26 18:44  maan
-
-       * ogg.c: do not log short writes
-
-2004-09-26 18:43  maan
-
-       * afs.c: set stream writer fd to non-blocking io
-
-2004-09-26 18:41  maan
-
-       * ogg.c: handle short writes properly (do not even try to rewrite)
-
-2004-09-26 16:20  maan
-
-       * server.c, server.ggo: new option: autoplay
-
-2004-09-26 15:44  maan
-
-       * afs.c: more fixes of the same kind
-
-2004-09-26 15:37  maan
-
-       * afs.c: do not segfault in audio_format_name() if no audio_format
-         was selected yet
-
-2004-09-26 14:33  maan
-
-       * fade.c: more detailed output about what is going on
-
-2004-09-24 13:45  maan
-
-       * gui.c: cosmetics
-
-2004-09-24 04:23  maan
-
-       * gui.c: kill reader if writer died and vice versa
-
-2004-09-24 03:09  maan
-
-       * afs.c, server.c: comment out noisy debugging messages
-
-2004-09-24 03:04  maan
-
-       * afs.c, mp3.c, ogg.c, server.c, server.h: major rearrangement of
-         struct audio_format
-
-2004-09-23 19:20  maan
-
-       * ogg.c: reintroduce first_page, clear vcut on errors
-
-2004-09-23 18:43  maan
-
-       * mp3.c: avoid double close on errors
-
-2004-09-23 01:06  maan
-
-       * fade.conf.sample: adapt to new syntax
-
-2004-09-23 01:01  maan
-
-       * gui.conf.sample: kill obsolete commands
-
-2004-09-23 00:52  maan
-
-       * gui.c: new internal key bindings for adjusting loglevel
-
-2004-09-22 22:27  maan
-
-       * ogg.c: rename label aaa, kill dead code
-
-2004-09-22 22:10  maan
-
-       * ogg.c: kill dead code
-
-2004-09-22 22:07  maan
-
-       * ogg.c: nuke first_page
-
-2004-09-22 21:54  maan
-
-       * ogg.c: get output of first page right (I hope)
-
-2004-09-22 21:10  maan
-
-       * gui.c: don't segfault when stopping decoder manually. Avoid
-         kill_decoder also.
-
-2004-09-22 20:32  maan
-
-       * ogg.c: nuke unneeded cutpoint
-
-2004-09-22 20:06  maan
-
-       * 1.0: [no log message]
-
-2004-09-22 13:41  maan
-
-       * INSTALL: typo
-
-2004-09-22 02:56  maan
-
-       * ogg.c: remove more useless variables
-
-2004-09-22 02:52  maan
-
-       * ogg.c: replace fprintf by clog
-
-2004-09-22 02:47  maan
-
-       * ogg.c: cosmetics
-
-2004-09-22 02:34  maan
-
-       * ogg.c: remove unused variables
-
-2004-09-22 01:27  maan
-
-       * gui.c: loglevel adjustment
-
-2004-09-22 00:47  maan
-
-       * gui.ggo: default loglevel: 4
-
-2004-09-22 00:46  maan
-
-       * README.dbtool: typo
-
-2004-09-22 00:03  maan
-
-       * gui.c, gui.ggo: new option loglevel
-
-2004-09-21 23:53  maan
-
-       * gui.c: new function clog, log more info to bot win instead of
-         status bar
-
-2004-09-21 22:01  maan
-
-       * gui.c: increase sleep time after reader/writer has died
-
-2004-09-21 21:16  maan
-
-       * gui.c: cosmetics
-
-2004-09-21 21:13  maan
-
-       * server.c: only send chunk if we are still playing
-
-2004-09-21 21:12  maan
-
-       * ogg.c: return early if there is no stream writer
-
-2004-09-21 21:10  maan
-
-       * pob-ogg.c: check return value of write and abort on errors. ifdef
-         out buffering for now.
-
-2004-09-21 20:53  maan
-
-       * gui.c, gui.ggo: introduce struct stream_io and siol[], an array
-         of such structs, each member holding all relevant information for
-         one audio format. Simplify options and add some docu for para_gui
-         -h.
-
-2004-09-21 18:12  maan
-
-       * gui.c: do not include \n in outputf
-
-2004-09-21 18:05  maan
-
-       * gui.c: print info top bot win when starting/stopping decoder
-
-2004-09-21 14:52  maan
-
-       * afs.c, command.c, gui.c, sdl_gui.c, server.h: print afs status
-         flags as extra line in stat output
-
-2004-09-20 03:23  maan
-
-       * ogg.c: loglevel adjustments
-
-2004-09-20 02:16  maan
-
-       * ogg.c: jumping appears to work
-
-2004-09-20 01:15  maan
-
-       * ogg.c: first version with jumping kind of working
-
-2004-09-19 23:31  maan
-
-       * command.c, mp3.c, server.h: long unsigned is enough
-
-2004-09-19 23:29  maan
-
-       * afs.c: return early from afs_send_chunk if there was a reposition
-         request
-
-2004-09-19 21:37  maan
-
-       * ogg.c: make granpos global and get rid of update_chunk_count
-
-2004-09-19 21:27  maan
-
-       * ogg.c: do not crash on jump in ogg files. Jumping still is not
-         working, but we are getting closer...
-
-2004-09-19 18:12  maan
-
-       * ogg.c: minor improvements
-
-2004-09-19 17:56  maan
-
-       * afs.c: use stop_stream instead of closing file manually
-
-2004-09-19 17:26  maan
-
-       * pob-ogg.c: skip initial crap before first ogg packet, more
-         logging, fix config_file thinko
-
-2004-09-19 15:42  maan
-
-       * pob-ogg.c: Oh boy, that was buggy. For instance, it included
-         client_cmdline.h instead of pob-ogg.cmdline.h
-
-2004-09-18 15:29  maan
-
-       * configure.ac: build para_pob-ogg if oggvorbis libs are found
-
-2004-09-18 15:08  maan
-
-       * mp3.c: comment out noisy debug message
-
-2004-09-18 15:07  maan
-
-       * afs.c: add audio format heuristics (based on filename)
-
-2004-09-18 13:44  maan
-
-       * command.c, mp3.c, para.h, server.c: nuke PETER_B ;) As in real
-         life, PETER_B happened to be useless for anything
-
-2004-09-18 13:09  maan
-
-       * mp3.c: stop stream writer on repos request. This gets rid of all
-         remaining cases of audio clipping, I hope
-
-2004-09-18 13:07  maan
-
-       * mp3.c: rename reposition_stream() to mp3_reposition_stream
-
-2004-09-18 13:06  maan
-
-       * mp3.c: split mp3_stop_stream, new function stop_stream_writer()
-
-2004-09-18 13:03  maan
-
-       * mp3.c: rename stop_stream to mp3_stop_stream
-
-2004-09-17 04:39  maan
-
-       * ogg.c: new function: get_page. Makes ogg_read_chunk much more
-         readable
-
-2004-09-17 04:14  maan
-
-       * ogg.c: remove dead code
-
-2004-09-17 04:12  maan
-
-       * ogg.c: no need to set have_page twice
-
-2004-09-17 04:08  maan
-
-       * ogg.c: avoid needless goto
-
-2004-09-17 03:30  maan
-
-       * ogg.c: new function page_out to clean up the cruft a bit
-
-2004-09-17 03:17  maan
-
-       * ogg.c: still crappy as hell, but a bit more stable. Repositioning
-         is not working yet.
-
-2004-09-17 01:27  maan
-
-       * fade.c: more verbose output
-
-2004-09-17 01:27  maan
-
-       * NEWS: [no log message]
-
-2004-09-17 01:10  maan
-
-       * gui.c: fix clipping when returning from external command
-
-2004-09-17 00:53  maan
-
-       * afs.c, dbtool.c, dbtool_template.c, gui.c: rename fut to sl
-         (score list). It did not print the future anyway, depending on
-         the particular score definition
-
-2004-09-15 22:13  maan
-
-       * afs.c, command.c, server.h: si: report supported audio formats
-
-2004-09-15 21:22  maan
-
-       * command.c: rename uptime to si (server info)
-
-2004-09-12 20:49  maan
-
-       * fade.c, fade.ggo: introduce fall asleep stream vs sleep_stream
-
-2004-09-12 20:33  maan
-
-       * mp3.c: nicer id3 tag info
-
-2004-09-12 20:33  maan
-
-       * command.c: use long unsigned for computing seconds etc. in
-         statusbar
-
-2004-09-12 20:32  maan
-
-       * ogg.c: it kinda works in some sense if you do not try to jump
-         around...
-
-2004-09-12 20:20  maan
-
-       * afs.c: cosmetics
-
-2004-09-12 20:20  maan
-
-       * INSTALL: better english
-
-2004-09-12 20:19  maan
-
-       * CREDITS: [no log message]
-
-2004-09-11 05:55  maan
-
-       * INSTALL: typo
-
-2004-09-07 21:47  maan
-
-       * server.h: function prototype changes
-
-2004-09-07 21:45  maan
-
-       * server.ggo: stream_write command may be given multiple times,
-         nuke afs_buf
-
-2004-09-07 21:44  maan
-
-       * pob-ogg.c: introduce OGG_BUFFSIZE and fill buffer halfways before
-         any output occurs
-
-2004-09-07 21:41  maan
-
-       * mp3.c: mostly rearrangement of functions and small adjustments
-
-2004-09-07 21:39  maan
-
-       * gui.c: support oggvorbis, not yet fit for more audio formats
-
-2004-09-07 21:36  maan
-
-       * command.c: adjustments due to removal of afs_buf and renaming of
-         mmd->frames_sent to mmd->chunks_sent
-
-2004-09-07 21:34  maan
-
-       * afs.c: nuke generic_write_chunk and move relevant bits  to mp3.
-         Iplement better syntax for stream_write_cmd (can be given
-         multiple times, one for each sound format). Many small changes
-
-2004-09-07 21:27  maan
-
-       * server.c: nuke afs_buf, send_chunk takes FILE* now
-
-2004-09-07 21:24  maan
-
-       * configure.ac: add checks for oggvorbis
-
-2004-09-07 21:23  maan
-
-       * Makefile.in: add targets for para_pob-ogg
-
-2004-09-04 13:51  maan
-
-       * FEATURES: [no log message]
-
-2004-09-02 20:19  maan
-
-       * server.h: remove superflous function prototypes
-
-2004-09-02 20:12  maan
-
-       * mp3.c: simplify open_stream
-
-2004-09-02 20:02  maan
-
-       * sdl_gui.c: stat: print audio format
-
-2004-09-02 19:51  maan
-
-       * command.c, gui.c: stat: print audio format, report size in KB
-         instead of bytes
-
-2004-09-02 19:26  maan
-
-       * command.c, server.c, server.h: move uptime_str() from server.c to
-         command.c, it is only used there
-
-2004-09-02 19:22  maan
-
-       * server.c: whitespace fixes
-
-2004-09-02 19:21  maan
-
-       * server.c, server.h: make more functions static
-
-2004-09-02 19:12  maan
-
-       * server.c, server.h: remove logfile from mmd
-
-2004-09-02 18:56  maan
-
-       * server.c, server.h: remove user_list from mmd
-
-2004-09-02 18:41  maan
-
-       * server.c: whitespace fixes
-
-2004-09-02 18:37  maan
-
-       * server.c, server.h: move config_file from mmd to parse_config
-
-2004-09-02 18:24  maan
-
-       * server.c: fix --config_file option
-
-2004-09-02 18:19  maan
-
-       * command.c, server.h: move dbtool_has_pic from mmd to command.c
-
-2004-09-02 18:10  maan
-
-       * command.c, server.c, server.h: move lcl_head from mmd to
-         command.c
-
-2004-09-02 18:09  maan
-
-       * afs.c: loglevel adjustment
-
-2004-09-02 18:06  maan
-
-       * gui.c: change stream_write_cmd_arg to mp3_write_cmd_arg
-
-2004-09-02 17:53  maan
-
-       * afs.c, server.c: cosmetics
-
-2004-09-02 17:41  maan
-
-       * gui.conf.sample, gui.ggo: rename stream_read_cmd to mp3_read_cmd,
-         dito for write
-
-2004-09-02 17:41  maan
-
-       * afs.c, command.c, server.h: commands stat and sb: print file type
-
-2004-09-02 17:39  maan
-
-       * 1.0: [no log message]
-
-2004-09-02 17:04  maan
-
-       * afs.c, server.c, server.h: mv alloc_afs_buf() from server to afs
-
-2004-09-02 16:41  maan
-
-       * afs.c, server.c, server.h: remove extern conf struct from afs.c.
-         Its only use was afs_buf_size which is now passed to
-         afs_send_chunk instead
-
-2004-09-02 16:38  maan
-
-       * NEWS: [no log message]
-
-2004-09-02 16:23  maan
-
-       * mp3.h: no longer needed
-
-2004-09-02 16:22  maan
-
-       * Makefile.in, afs.c, mp3.c, mp3.h, para.h, server.c, server.h:
-         move remaining mp3 specific stuff to mp3.c, introduce
-         open_stream, stop_stream function pointers for completely modular
-         audio format support. Make more functions static, get rid of
-         mp3.h
-
-2004-08-31 03:26  maan
-
-       * mp3.c: return 0 on eof instead of -1
-
-2004-08-31 02:32  maan
-
-       * afs.c, mp3.c, mp3.h, server.h: add infrastructure for dealing
-         with audio files of different format, many small cleanups
-
-2004-08-30 23:18  maan
-
-       * mp3.c, mp3.h: reduce mp3.h to bare minimum by moving everything
-         but function prototypes to mp3.c
-
-2004-08-30 22:46  maan
-
-       * Makefile.in, afs.c, mp3.c, mp3.h: move fread from afs.c to mp3.c
-         into new function mp3_read_chunk. Do some renaming also.
-
-2004-08-30 21:16  maan
-
-       * sdl_gui.ggo: kill option no_pics
-
-2004-08-30 21:15  maan
-
-       * mp3.c: print replacement texts if no id3 tag was found
-
-2004-08-30 21:14  maan
-
-       * command.c: move audio file info to status_volatile since it
-         contains bitrate which is not constant for vbr files
-
-2004-08-30 21:00  maan
-
-       * sdl_gui.c: adapt to new stat output
-
-2004-08-30 20:28  maan
-
-       * gui.c: adapt to new stat output
-
-2004-08-30 19:56  maan
-
-       * para.h: cosmetics
-
-2004-08-30 19:54  maan
-
-       * mp3.c: make functions static where possible
-
-2004-08-30 19:51  maan
-
-       * afs.c, mp3.c, mp3.h, server.h: move mp3info struct from mmd to
-         mp3.c where it belongs
-
-2004-08-30 19:21  maan
-
-       * afs.c, command.c, server.c, server.h: rename mp3_meta_data to
-         misc_meta_data
-
-2004-08-30 19:16  maan
-
-       * afs.c, command.c, mp3.c, mp3.h, server.h: move mp3 specific stuff
-         to mp3.c
-
-2004-08-30 16:52  maan
-
-       * afs.c: rename mp3_stream to audio_file
-
-2004-08-30 16:35  maan
-
-       * command.c, dbtool.c: cosmetics
-
-2004-08-30 16:25  maan
-
-       * Makefile.in: include manual.txt in tgz
-
-2004-08-30 16:19  maan
-
-       * INSTALL: [no log message]
-
-2004-08-30 15:43  maan
-
-       * Makefile.in, para.h: change mp3tech.[ch] to mp3.[ch]
-
-2004-08-30 15:40  maan
-
-       * mp3.c, mp3.h: former mp3tech.[ch]
-
-2004-08-30 15:40  maan
-
-       * mp3tech.c, mp3tech.h: renamed to mp3.[ch]
-
-2004-08-30 15:38  maan
-
-       * mp3tech.c, mp3tech.h: copyright fixes
-
-2004-08-30 15:19  maan
-
-       * Makefile.in: remove dependency on Makefile for cmdline stuff, so
-         that gengetopt is not required for build from tgz
-
-2004-08-30 05:44  maan
-
-       * Makefile.in, NEWS, afs.c, command.c, configure.ac, mp3tech.c,
-         mp3tech.h, para.h, server.c, server.h: major mp3-secific cleanup
-         and enhancements. This should fix timing issues with vbr mp3s
-
-2004-08-30 05:07  maan
-
-       * mp3info.h: most of it was never used, the rest went somewhere
-         else
-
-2004-08-30 05:05  maan
-
-       * textfunc.h: unnecessary
-
-2004-08-25 21:21  maan
-
-       * gui.c: whitespace cleanup
-
-2004-08-25 21:12  maan
-
-       * gui.c: fix memory leak in align_str, add some debugging code
-
-2004-08-23 23:59  maan
-
-       * 1.0: [no log message]
-
-2004-08-23 00:29  maan
-
-       * dbtool.c: comment out check for invalid null entries in
-         attributes as attribute columns are declared non-null anyway
-
-2004-08-21 17:18  maan
-
-       * server.c: minor main() cleanup
-
-2004-08-21 16:46  maan
-
-       * fade.ggo, server.ggo: beautify output of help option
-
-2004-08-21 15:44  maan
-
-       * client.c, dbtool.c, fade.c, gui.c, net.c, sdl_gui.c, server.c:
-         update to gengetopt 2.12.2 requires additional arg for
-         cmdline_parser_configfile
-
-2004-08-15 19:07  maan
-
-       * Makefile.in: Add ChangeLog to phony targets, cosmetics
-
-2004-08-15 19:04  maan
-
-       * dbtool.ggo: typo
-
-2004-08-15 18:35  maan
-
-       * afs.c: update_dbinfo: whitespace fixes
-
-2004-08-15 18:34  maan
-
-       * afs.c: update_dbinfo: always print three lines
-
-2004-08-15 04:01  maan
-
-       * dbtool.c: fix info: for songs not yet played, dir of song was not
-         shown
-
-2004-08-13 19:30  maan
-
-       * README.dbtool: another typo
-
-2004-08-13 19:17  maan
-
-       * README.dbtool: whitespace fixes to make grutatxt happy
-
-2004-08-13 19:13  maan
-
-       * README.dbtool: typo
-
-2004-08-13 19:08  maan
-
-       * NEWS: [no log message]
-
-2004-08-05 23:19  maan
-
-       * dbtool.c: use escaped_name also for updating numplayed
-
-2004-08-04 02:36  maan
-
-       * dbtool.c: fix skip command
-
-2004-07-29 12:50  maan
-
-       * server.c: fix log function for loglevel > VERBOSE
-
-2004-07-29 03:52  maan
-
-       * Makefile.in, NEWS: change codename
-
-2004-07-28 14:06  maan
-
-       * README: typo
-
-2004-07-25 17:41  maan
-
-       * VERSION: no longer needed
-
-2004-07-25 17:40  maan
-
-       * NEWS, configure.ac: bump version to 99
-
-2004-07-24 20:50  maan
-
-       * mp3tech.h: further simplify get_mp3_info
-
-2004-07-24 20:12  maan
-
-       * afs.c: skip song if mp3_info fails
-
-2004-07-24 19:22  maan
-
-       * mp3tech.c, mp3tech.h: further simplify get_mp3_info
-
-2004-07-24 17:51  maan
-
-       * mp3info.h, mp3tech.c: kill unneeded code
-
-2004-07-24 03:08  maan
-
-       * pob-ogg.c: simple http streamer for pogg-http
-
-2004-07-23 21:56  maan
-
-       * gui.c: simplify item placement in top.win
-
-2004-07-23 18:57  maan
-
-       * client.c, afs.c: __func__
-
-2004-07-23 18:50  maan
-
-       * client.c: whitespaces
-
-2004-07-23 18:47  maan
-
-       * net.c: __func__, whitespaces
-
-2004-07-23 18:43  maan
-
-       * server.c: typo
-
-2004-07-23 18:41  maan
-
-       * server.c: __func__
-
-2004-07-23 18:33  maan
-
-       * server.c: whitespace cleanup
-
-2004-07-23 18:31  maan
-
-       * server.c: cosmetics
-
-2004-07-23 18:26  maan
-
-       * INSTALL, Makefile.in, afs.c: cosmetics
-
-2004-07-21 23:46  maan
-
-       * Makefile.in: simplify install
-
-2004-07-21 23:45  maan
-
-       * dbtool.c, dbtool.ggo: new option: emph_np
-
-2004-07-21 23:43  maan
-
-       * README.dbtool: add section on pictures
-
-2004-07-21 23:29  maan
-
-       * README.dbtool: more details on scoring
-
-2004-07-21 20:29  maan
-
-       * afs.c: use proper types for return value of fread/write
-
-2004-07-20 20:15  maan
-
-       * gui.c: detect blocking writes by time, not by count
-
-2004-07-20 16:25  maan
-
-       * maint.mk: everything went to Makefile.in
-
-2004-07-20 16:24  maan
-
-       * INSTALL, Makefile.in, gui.c, maint.mk: INSTALL
-
-2004-07-20 15:37  maan
-
-       * FEATURES: [no log message]
-
-2004-07-20 04:34  maan
-
-       * configure.ac: cosmetics
-
-2004-07-20 03:46  maan
-
-       * dbtool.c: uncomment unused function
-
-2004-07-20 03:46  maan
-
-       * configure.ac: dbtool_template is no longer needed in extras
-         because it's always built
-
-2004-07-20 03:37  maan
-
-       * Makefile.in: introduce maintainer-clean for removing _all_
-         derived files
-
-2004-07-20 03:21  maan
-
-       * configure.ac: generated by autoscan, but heavily edited
-
-2004-07-20 02:52  maan
-
-       * gui.ggo: set default to auto-decoding
-
-2004-07-20 02:51  maan
-
-       * gui.c: reduce use of global variables
-
-2004-07-20 02:02  maan
-
-       * gui.ggo: input timeout is long gone
-
-2004-07-20 02:01  maan
-
-       * gui.c: cosmetics
-
-2004-07-20 01:20  maan
-
-       * gui.c: fix line numbering in COMMAND_MODE, use va_list for
-         print_in_bar
-
-2004-07-20 01:16  maan
-
-       * NEWS: [no log message]
-
-2004-07-20 01:16  maan
-
-       * Makefile.in: add -WAll to CCFLAGS, fix typo
-
-2004-07-19 21:21  maan
-
-       * para.h: don't include version.h
-
-2004-07-19 21:20  maan
-
-       * mp3info.h: resolve -DVERSION conflict
-
-2004-07-19 21:19  maan
-
-       * Makefile.in, maint.mk: move stuff from maint.mk to Makefile.in,
-         nuke paraslash_light
-
-2004-07-19 18:35  maan
-
-       * Makefile.in: use
-
-2004-07-19 18:07  maan
-
-       * install-sh: autoconf refuses to work without it, strange...
-
-2004-07-19 17:46  maan
-
-       * dbtool.c: include mysql/mysql.h instead of mysql.h
-
-2004-07-19 17:45  maan
-
-       * config.mk: no longer needed
-
-2004-07-19 17:44  maan
-
-       * SFont.c, SFont.h, sdl_gui.c: include SDL/SDL.h instead of SDL.h
-
-2004-07-19 17:43  maan
-
-       * Makefile: now created by autoconf from Maikefile.in
-
-2004-07-19 17:42  maan
-
-       * Makefile.in: switch to autoconf
-
-2004-07-19 12:19  maan
-
-       * gui.c: fix line numbering in command mode, cosmetics
-
-2004-07-19 12:18  maan
-
-       * afs.c: don't kill stream writer when song is finished
-
-2004-07-19 12:17  maan
-
-       * server.c: use IPC_PRIVATE, not hardcoded number for semget
-
-2004-07-15 20:57  maan
-
-       * command.c: fix com_ps
-
-2004-07-15 18:57  maan
-
-       * command.c: kill useless sleep, fix possible segfault
-
-2004-07-15 18:47  maan
-
-       * command.c: cosmetics
-
-2004-07-15 18:38  maan
-
-       * afs.c, command.c, server.c, server.h: fix play command by
-         introducing a new member in mmd: old_stream. It contains the
-         number of the stream that was selected as the song started.
-         Moreover, change mmd->stream from char[] to int. This makes the
-         patch so large.
-
-2004-07-14 02:01  maan
-
-       * dbtool_template.c, server.ggo: cosmetics
-
-2004-07-13 02:40  maan
-
-       * command.c: uptime: add afs_buf size to output
-
-2004-07-13 02:15  maan
-
-       * server.c: use PROCEED_MSG macro
-
-2004-07-13 02:03  maan
-
-       * server.c: cosmetics
-
-2004-07-13 01:57  maan
-
-       * server.c: re-allocate afs_buf on HUP, it's size might have
-         changed
-
-2004-07-13 01:56  maan
-
-       * afs.c: afs_send_chunk: rewrite on partial writes
-
-2004-07-13 00:03  maan
-
-       * fade.c: fade did not read config for silly reasons. Fix buffer
-         overflow while at it
-
-2004-07-12 22:48  maan
-
-       * dbtool.c, gui.c, sdl_gui.c: output also directory of sound file
-         in dbinfo
-
-2004-07-12 02:24  maan
-
-       * server.ggo: make help fit on 80 column terminals
-
-2004-07-12 02:23  maan
-
-       * server.c: move log message to where it belongs
-
-2004-07-12 02:23  maan
-
-       * para.h: cosmetics
-
-2004-07-12 02:19  maan
-
-       * gui.c: fix hang-when-stopped bug
-
-2004-07-12 02:18  maan
-
-       * afs.c: __func__
-
-2004-07-12 00:03  maan
-
-       * FEATURES: [no log message]
-
-2004-07-11 22:57  maan
-
-       * INSTALL, README, README.dbtool: various clarifications in
-         documentation
-
-2004-07-11 21:31  maan
-
-       * gui.c: init curses and winch cleanup
-
-2004-07-11 18:41  maan
-
-       * gui.c: hust re-order functions a bit
-
-2004-07-11 18:32  maan
-
-       * gui.c: dont close status pipe for external commands
-
-2004-07-11 17:33  maan
-
-       * gui.c: further simplify do_select logic
-
-2004-07-10 01:01  maan
-
-       * afs.c, server.c, server.ggo, server.h: make chunk buf size for
-         afs run-time configurable
-
-2004-07-09 02:39  maan
-
-       * gui.c: simplify logic in do_select
-
-2004-07-07 23:50  maan
-
-       * Makefile, command.c: output also build date, system and compiler
-         version in command 'version'
-
-2004-07-07 23:49  maan
-
-       * gui.c: cosmetics
-
-2004-07-07 22:57  maan
-
-       * fade.c: fix potential buffer overflow if /home/maan is too long
-
-2004-07-07 22:56  maan
-
-       * afs.c: cosmetics
-
-2004-07-07 22:55  maan
-
-       * gui.c: do_select: logical cleanup
-
-2004-06-30 13:26  maan
-
-       * fade.c: fix waketime in sweet_dreams
-
-2004-06-23 23:32  maan
-
-       * exec.c, server.c, server.ggo, server.h: move popeostream writer
-
-2004-06-23 23:31  maan
-
-       * afs.c: major send_chunk cleanup. Hot!
-
-2004-06-22 01:31  maan
-
-       * gui.c: cleanup dead code
-
-2004-06-20 18:13  maan
-
-       * server.ggo: kill unused option
-
-2004-06-20 18:10  maan
-
-       * FEATURES, INSTALL: some clarifications, typos
-
-2004-06-20 18:08  maan
-
-       * config.mk: cosmetics
-
-2004-06-20 18:06  maan
-
-       * fade.c: bail out if mixer cant be opened
-
-2004-06-20 17:55  maan
-
-       * fade.c: simplify sweet_dreams
-
-2004-06-20 16:28  maan
-
-       * command.c: improve play command: accepts stream as first
-         (optional) argument
-
-2004-06-20 16:25  maan
-
-       * gui.c: dont suck cpu if no server is available
-
-2004-06-12 03:10  maan
-
-       * gui.c: cosmetics
-
-2004-06-10 01:34  maan
-
-       * para.h: add prototype for exec_cmdline_pid_bg
-
-2004-06-09 03:04  maan
-
-       * gui.c: fix missing write on command exec
-
-2004-06-09 02:06  maan
-
-       * gui.c: check also stream write pipe in do_select, introduce ascii
-         logo
-
-2004-06-08 02:57  maan
-
-       * gui.c: it works fine with only 13 lines. Fix minor sigwinch bug
-         when resizing to very small windows
-
-2004-06-08 02:45  maan
-
-       * gui.c: fix color of separator
-
-2004-06-08 02:40  maan
-
-       * gui.c: kill com_exit. Leaving decoder in bg wont work anymore
-
-2004-06-08 02:36  maan
-
-       * gui.c: make it survive partial writes
-
-2004-06-08 02:08  maan
-
-       * gui.c: fix help output
-
-2004-06-08 02:05  maan
-
-       * gui.c: cosmetics
-
-2004-06-08 01:51  maan
-
-       * gui.c: retain screen content on window changes
-
-2004-06-07 23:20  maan
-
-       * gui.c: terminate decoder after 10 times EAGAIN
-
-2004-06-07 04:15  maan
-
-       * gui.c: better sigwinch handling
-
-2004-06-07 03:20  maan
-
-       * gui.c: it is pointless to have a fuction that only contains a
-         for(;;) loop
-
-2004-06-07 02:54  maan
-
-       * gui.c: avoid busy loop when there is nothing to do
-
-2004-06-07 02:12  maan
-
-       * gui.conf.sample: some more examples
-
-2004-06-07 02:02  maan
-
-       * gui.c: add some docu
-
-2004-06-07 01:44  maan
-
-       * gui.c: fix trailing whitespace
-
-2004-06-07 01:41  maan
-
-       * gui.c: no need to set nodelay every time
-
-2004-06-07 01:21  maan
-
-       * gui.ggo: short command line options
-
-2004-06-07 01:20  maan
-
-       * gui.c: simplify key_map, cosmetics
-
-2004-06-07 00:59  maan
-
-       * gui.conf.sample: update to new syntax
-
-2004-06-07 00:47  maan
-
-       * gui.c: cosmetics
-
-2004-06-07 00:43  maan
-
-       * gui.c: nuke GETSTR mode of do_select and com_decode. Many small
-         improvements
-
-2004-06-07 00:42  maan
-
-       * exec.c: two new exec functions without pipes. redirect unneded
-         fds to /dev/null
-
-2004-06-06 17:45  maan
-
-       * exec.c, gui.c, gui.ggo, para.h: implement own functions for
-         stream io
-
-2004-06-06 14:37  maan
-
-       * gui.c: new stop_decoder command
-
-2004-05-29 21:46  maan
-
-       * gui.c: declare more functions as static
-
-2004-05-29 21:43  maan
-
-       * gui.c: more cosmetics
-
-2004-05-29 21:28  maan
-
-       * gui.c: cosmetics
-
-2004-05-29 21:27  maan
-
-       * exec.c: dont mess with stderr
-
-2004-05-29 21:27  maan
-
-       * gui.c: bot window needs at least two lines
-
-2004-05-29 18:32  maan
-
-       * gui.c: color handling cleanup
-
-2004-05-29 17:36  maan
-
-       * gui.c: cosmetics
-
-2004-05-29 17:28  maan
-
-       * NEWS, gui.conf.sample: NEWS
-
-2004-05-29 17:21  maan
-
-       * fade.conf.sample: add fade options
-
-2004-05-29 17:21  maan
-
-       * gui.conf.sample: remove fade options
-
-2004-05-29 17:19  maan
-
-       * gui.c: remove dead code, small cleanups
-
-2004-05-29 17:05  maan
-
-       * exec.c, gui_common.c: move file_exists() from gui_common.c to
-         exec.c since fade also needs it
-
-2004-05-29 17:04  maan
-
-       * Makefile, config.mk: add para_fade
-
-2004-05-29 17:03  maan
-
-       * gui.c, gui.ggo: nuke soundcard stuff which was moved to fade
-
-2004-05-29 17:03  maan
-
-       * fade.c, fade.ggo: contains soundcard and linux specific stuff,
-         ripped out of gui.c, gui.ggo
-
-2004-05-29 14:12  maan
-
-       * gui.ggo: use typestr
-
-2004-05-29 13:45  maan
-
-       * README: add dbadm
-
-2004-05-27 23:48  maan
-
-       * gui_common.c, para.h: add prototypes of new exec functions
-
-2004-05-27 23:48  maan
-
-       * gui.c: use new exec functions to obtain pid of child
-
-2004-05-27 23:46  maan
-
-       * exec.c: fix up exec functions (use exec instead of return,
-         uhuhuhu), add variants of several functions that can return the
-         pid of child processes
-
-2004-05-27 23:44  maan
-
-       * dbadm.c: fix potential segfault
-
-2004-05-23 18:08  maan
-
-       * dbadm.c: fix menu in case of small window size
-
-2004-05-23 16:16  maan
-
-       * dbadm.c: use static functions
-
-2004-05-23 16:11  maan
-
-       * dbadm.c: fix filename printing for long filenames
-
-2004-05-23 15:41  maan
-
-       * Makefile, dbadm.c: use internal popen_xxx fuctions
-
-2004-05-22 16:23  maan
-
-       * Makefile, config.mk: include para_dbadm
-
-2004-05-22 16:22  maan
-
-       * client.c, server.c: increase limit for command line length
-
-2004-05-22 04:20  maan
-
-       * dbadm.c: color cleanups
-
-2004-05-22 03:38  maan
-
-       * dbadm.c: many small cleanups
-
-2004-05-22 02:44  maan
-
-       * dbadm.c: first usable version
-
-2004-05-22 01:02  maan
-
-       * dbadm.c: small tool to modify attributes
-
-2004-05-21 02:35  maan
-
-       * gui.c: new command: toggle auto decoding
-
-2004-05-21 01:54  maan
-
-       * gui.c: kill children on sigint
-
-2004-05-21 00:55  maan
-
-       * gui.c: close status pipe before executing external commands,
-         reopen afterwards
-
-2004-05-15 18:51  maan
-
-       * gui.c: typo
-
-2004-05-15 18:50  maan
-
-       * dbtool.c: free all dynamically allocated resources, static
-         functions, reduce use of exit()
-
-2004-05-12 21:23  maan
-
-       * gui.c: comment out debugging messages
-
-2004-05-12 21:18  maan
-
-       * gui.c: shutdown curses on external commands
-
-2004-05-12 20:30  maan
-
-       * NEWS: [no log message]
-
-2004-05-10 23:09  maan
-
-       * server.c: cosmetics
-
-2004-05-10 23:09  maan
-
-       * client.c: use C99 identifier __func__
-
-2004-05-10 22:12  maan
-
-       * para.h: avoid compiler warning
-
-2004-05-10 22:11  maan
-
-       * afs.c, command.c, server.c: update dbinfo only after commands
-         with perms & DB_WRITE != 0
-
-2004-05-10 04:20  maan
-
-       * gui.c: cosmetics
-
-2004-05-10 03:48  maan
-
-       * gui.c: make errmsg for unknown keys work again
-
-2004-05-10 03:39  maan
-
-       * gui.conf.sample: add standard key mappings
-
-2004-05-10 03:31  maan
-
-       * para.h: key is now a string, not a char
-
-2004-05-10 03:31  maan
-
-       * exec.c: typo
-
-2004-05-10 03:30  maan
-
-       * gui.c: make all commands runtime-configurable, make function keys
-         and control keys work in configfile, much nicer help page
-
-2004-05-09 22:12  maan
-
-       * gui.c: fix line numbering in help
-
-2004-05-09 22:08  maan
-
-       * server.c: move split_args function to exec.c
-
-2004-05-09 22:07  maan
-
-       * para.h: add new struct gui_command
-
-2004-05-09 22:06  maan
-
-       * gui.c: nicify help and improve key map handling
-
-2004-05-09 22:04  maan
-
-       * exec.c: move split_args from server.c, new function
-         popen_read_client_cmdline
-
-2004-05-09 16:29  maan
-
-       * gui.c, maint.mk: cosmetics
-
-2004-05-08 15:04  maan
-
-       * NEWS: [no log message]
-
-2004-05-08 15:01  maan
-
-       * gui.c: new command: V (print version)
-
-2004-05-08 14:31  maan
-
-       * maint.mk: set umask to 022 before creating files. Ugly
-
-2004-05-05 00:11  maan
-
-       * gui.c: cosmetics
-
-2004-05-04 23:40  maan
-
-       * gui.ggo: add new option: key_map to map keys to shell commands
-
-2004-05-04 23:26  maan
-
-       * gui.c: make reread config work again, cleanups
-
-2004-05-04 23:11  maan
-
-       * gui.c: kill key maps 0-9 (volume setting)
-
-2004-05-04 23:05  maan
-
-       * gui.conf.sample: add key_map comments
-
-2004-05-04 22:52  maan
-
-       * gui.c: implement key mappings, kill bogus transform_vol
-
-2004-05-04 02:31  maan
-
-       * client.c, dbtool.c, gui.c, gui.ggo, sdl_gui.c, server.c: update
-         to recent version of gengetopt
-
-2004-04-25 18:04  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: dont crash if no pic was
-         found
-
-2004-04-25 16:09  maan
-
-       * dbtool.c: get pic by identifier if name starts with '#'
-
-2004-04-25 16:08  maan
-
-       * bash_completion: add some more commands
-
-2004-04-25 16:01  maan
-
-       * README: cosmetics
-
-2004-04-25 04:52  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: info text, make it
-         survive theme cgange, cleanups
-
-2004-04-24 16:14  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: cosmetics
-
-2004-04-18 00:29  maan
-
-       * addons/gkrellm_cmd_display/: Makefile, cmd_display.c: again, too
-         much to document. Still buggy wrt opening/closing pipes
-
-2004-04-17 23:53  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: too much to tell
-
-2004-04-17 18:26  maan
-
-       * addons/gkrellm_cmd_display/para_ctrl.c: gkrellm plugin that
-         displays paraslash images
-
-2004-04-17 15:56  maan
-
-       * NEWS, command.c: new command: sc (song change)
-
-2004-04-10 13:10  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: make scrolling work
-         again and make scrolling speed configurable
-
-2004-04-10 12:36  maan
-
-       * dbtool.c: cleanups
-
-2004-04-08 20:39  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: make click left button
-         exec Mcommand
-
-2004-04-08 14:08  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: scroll text
-
-2004-04-05 02:08  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: cleanups
-
-2004-04-05 01:26  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: cleanups
-
-2004-04-05 00:51  maan
-
-       * addons/gkrellm_cmd_display/: cmd_display.c: Can't crash it any
-         more
-
-2004-04-04 21:30  maan
-
-       * addons/gkrellm_cmd_display/cmd_display.c: fix memory leak, make
-         mouse button work again
-
-2004-04-04 20:21  maan
-
-       * addons/gkrellm_cmd_display/: Makefile, cmd_display.c: too much to
-         mention
-
-2004-04-04 16:15  maan
-
-       * addons/gkrellm_cmd_display/: Makefile, cmd_display.c: former
-         gkrellm-fileread-2.00
-
-2004-03-17 13:42  maan
-
-       * maint.mk: cosmetics
-
-2004-03-17 13:41  maan
-
-       * Makefile: use type -p rather than which to find poc-fec
-
-2004-03-17 13:36  maan
-
-       * 1.0: [no log message]
-
-2004-02-24 14:35  maan
-
-       * NEWS: [no log message]
-
-2004-02-20 16:27  maan
-
-       * gui.c: new command: x (quit, but leave decoder)
-
-2004-02-20 00:50  maan
-
-       * client.c: handle binary input properly
-
-2004-02-14 00:38  maan
-
-       * dbtool.c: cosmetics
-
-2004-02-02 02:33  maan
-
-       * command.c: cosmetics
-
-2004-02-02 02:27  maan
-
-       * NEWS: [no log message]
-
-2004-02-02 02:25  maan
-
-       * gui.c: new key binding: u (uptime)
-
-2004-01-25 01:05  maan
-
-       * README.dbtool, config.mk: cosmetics
-
-2004-01-25 01:05  maan
-
-       * INSTALL: streaming capabilities are no longer required for mp3
-         decoder
-
-2004-01-25 01:04  maan
-
-       * FEATURES: reformat to make it look nicer in html
-
-2004-01-24 23:52  maan
-
-       * dbtool_template.c: help must print _three_ lines
-
-2004-01-24 23:51  maan
-
-       * gui.ggo: use proper default value for decode_cmd
-
-2004-01-24 23:50  maan
-
-       * INSTALL, Makefile, README, config.mk: dbtool.sample script ->
-         dbtool_template.c
-
-2004-01-24 23:03  maan
-
-       * dbtool_template.c: C-version of former dbtool.sample shell script
-
-2004-01-24 23:03  maan
-
-       * dbtool.sample: replaced by dbtool_template.c
-
-2004-01-24 13:19  maan
-
-       * server.c: ignore sigpipe, nicify log message for sigchld
-
-2004-01-24 12:20  maan
-
-       * server.c: remove crappy and unused get_exit_status, kill sigpipe
-         handling code
-
-2004-01-24 12:11  maan
-
-       * NEWS: [no log message]
-
-2004-01-23 09:35  maan
-
-       * gui.c: new command: A (list attributes)
-
-2004-01-23 09:34  maan
-
-       * server.h: add afs_preselect
-
-2004-01-23 09:34  maan
-
-       * server.c: proper signal handling using signal pipe
-
-2004-01-19 00:05  maan
-
-       * net.c: handle partial sends properly
-
-2004-01-18 01:01  maan
-
-       * dbtool.c: new macro: PICID
-
-2004-01-18 01:01  maan
-
-       * NEWS: [no log message]
-
-2004-01-17 23:28  maan
-
-       * README.dbtool: major reorganisation/update
-
-2004-01-13 02:30  maan
-
-       * command.c, net.c, para.h: new function: send_buffer_ll. Like
-         send_buffer, but allows to specify loglevel
-
-2004-01-13 02:29  maan
-
-       * afs.c: cosmetics
-
-2004-01-13 02:00  maan
-
-       * NEWS: [no log message]
-
-2004-01-13 02:00  maan
-
-       * gui.c: add S (list streams)
-
-2004-01-13 01:59  maan
-
-       * Makefile, maint.mk: fix -V (version)
-
-2004-01-13 01:23  maan
-
-       * README.dbtool: update creation of streams
-
-2004-01-13 01:08  maan
-
-       * dbtool.c: get_query: thinko
-
-2004-01-13 01:01  maan
-
-       * dbtool.c: get_query: make an empty stream definition select all
-         songs
-
-2004-01-13 00:48  maan
-
-       * NEWS: [no log message]
-
-2004-01-13 00:48  maan
-
-       * afs.c: afs_send_chunk: either write to poc pipe or kill it
-
-2004-01-09 22:07  maan
-
-       * para.h, server.h: move CHUNK_BUFFSIZE from para.h to server.h
-
-2004-01-09 22:06  maan
-
-       * exec.c: new function: popen_write_poc. It returns pipe fd _and_
-         pid of poc-fec
-
-2004-01-09 22:00  maan
-
-       * afs.c, server.c: new functions: kill_poc/do_get_song. Move poc
-         stuff completely to afs
-
-2004-01-09 21:57  maan
-
-       * NEWS: [no log message]
-
-2004-01-06 23:21  maan
-
-       * server.h: new flag: AFS_REREAD_DB_INFO
-
-2004-01-06 23:21  maan
-
-       * server.c: check semop for return value and retry semop on error
-
-2004-01-06 23:18  maan
-
-       * gui.c: add new key-binding: Q -> strq
-
-2004-01-06 23:17  maan
-
-       * dbtool.c: nuke scan for unnecessary 'end:' fix potential buffer
-         overflow
-
-2004-01-06 23:16  maan
-
-       * command.c: set AFS_REREAD_DB_INFO after each dbtool command to
-         make dbinfo current in stat/gui
-
-2004-01-06 22:52  maan
-
-       * afs.c: single out update of db_info to call it also from server
-         when new flag AFS_REREAD_DB_INFO was set by child
-
-2004-01-05 00:51  maan
-
-       * dbtool.sample: help: output 3 columns
-
-2004-01-05 00:50  maan
-
-       * dbtool.c: piclist: order pics by id
-
-2004-01-05 00:50  maan
-
-       * command.c: uptime: print current loglevel
-
-2004-01-05 00:49  maan
-
-       * README, README.dbtool: [no log message]
-
-2004-01-04 21:55  maan
-
-       * server.h: add get_poc_pipe, change afs_send_chunk
-
-2004-01-04 21:54  maan
-
-       * maint.mk: remove html files on distclean
-
-2004-01-04 21:53  maan
-
-       * config.mk: move BINARIES from Makefile to config.mk to let the
-         user remove targets she doesn't want to build
-
-2004-01-04 21:51  maan
-
-       * afs.c, server.c: move opening/closing of poc pipe to afs, reopen
-         poc_pipe on AFS_REPOS to avoid clipping and decoder crashes
-
-2004-01-04 21:49  maan
-
-       * NEWS: [no log message]
-
-2004-01-04 21:49  maan
-
-       * Makefile: major cleanup, should compile faster on rebuilds
-
-2004-01-04 21:49  maan
-
-       * INSTALL, README.dbtool: more explanation, reordering
-
-2004-01-04 21:47  maan
-
-       * COPYING: change year to 2004
-
-2004-01-04 07:27  maan
-
-       * CREDITS, INSTALL, NEWS, README, README.dbtool, command.c,
-         dbtool.c, server.h: major documentation update
-
-2004-01-04 07:27  maan
-
-       * maint.mk: add manual.txt and web targets
-
-2004-01-04 07:26  maan
-
-       * server.c: dont exit if no streams were found on startup
-
-2004-01-04 02:18  maan
-
-       * afs.c: use POC_FEC which was located by make rather than relying
-         on
-         /home/maan/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/local/scripts:/usr/local/mysql/bin:.:/usr/local/teTeX/bin/i586-pc-linux-gnu/:/home/maan/MIRROR/bin
-
-2004-01-04 02:17  maan
-
-       * Makefile: check also for poc-fec
-
-2004-01-04 02:07  maan
-
-       * README, afs.c, client.c, command.c, dbtool.c, gui.c, maint.mk,
-         para.h, sdl_gui.c, server.c, server.h: cosmetics
-
-2004-01-04 01:54  maan
-
-       * Makefile, config.mk, maint.mk: split Makefile into three parts
-
-2004-01-03 23:27  maan
-
-       * command.c, dbtool.c: cosmetics
-
-2004-01-03 22:38  maan
-
-       * NEWS: [no log message]
-
-2004-01-03 22:28  maan
-
-       * dbtool.c: new function: stdin2buf. make picadd accept jpeg data
-         from stdin. Change picch to only change name
-
-2004-01-03 20:00  maan
-
-       * gui.c: print key-bindings of c/C (next/previous stream) in help
-
-2004-01-03 19:59  maan
-
-       * dbtool.conf.sample: nuke streams
-
-2004-01-03 19:58  maan
-
-       * dbtool.c: strq: use memchr rather than strchr on potentially
-         binary data. stradd: properly escape stream definition
-
-2004-01-03 19:55  maan
-
-       * command.c: dbtool_cmd_handler: cleanup
-
-2004-01-03 19:54  maan
-
-       * client.c: cosmetics
-
-2004-01-03 19:53  maan
-
-       * afs.c: make get_song return -1 on errors rather than 0
-
-2004-01-03 19:52  maan
-
-       * README.dbtool: [no log message]
-
-2004-01-03 05:50  maan
-
-       * server.h: add modus to struct command
-
-2004-01-03 05:49  maan
-
-       * para.h: new #defines: AWAITING_DATA_MSG and PROCEED_MSG
-
-2004-01-03 05:49  maan
-
-       * net.c: new function: recv_bin_buffer
-
-2004-01-03 05:48  maan
-
-       * gui.c: bind ns to c again and ps to C
-
-2004-01-03 05:47  maan
-
-       * dbtool.ggo: nuke streams option
-
-2004-01-03 05:47  maan
-
-       * dbtool.c: new modus flag.  stream list is now stored inside mysql
-         db.  new commands: strq, stradd, strdel cdb: create all tables
-
-2004-01-03 05:44  maan
-
-       * command.c: new command flag: modus (read or write command). This
-         makes three command handlers: server/db_read/db_write.
-         Consequently, dbtool changed a lot.
-
-         new command: ps (reuse code of ns)
-
-2004-01-03 05:39  maan
-
-       * client.c: send stdin to server if server sends AWAITING_DATA_MSG
-
-2004-01-03 05:36  maan
-
-       * 1.0, FEATURES, NEWS, README: [no log message]
-
-2003-12-28 21:29  maan
-
-       * afs.c: reset mmd.vbr after each song. That seems to be
-         necessary..
-
-2003-12-28 04:16  maan
-
-       * Makefile, gui.c: cosmetics
-
-2003-12-28 02:35  maan
-
-       * gui.c: cosmetics
-
-2003-12-28 02:22  maan
-
-       * gui.c: hide cursor
-
-2003-12-27 21:07  maan
-
-       * FEATURES: [no log message]
-
-2003-12-27 14:49  maan
-
-       * Makefile, bash_completion, command.c, exec.c, gui.c,
-         gui_common.c, sdl_gui.c: replace remaining occurences of icc by
-         para
-
-2003-12-27 06:52  maan
-
-       * command.c, dbtool.sample, sdl_gui.c, sdl_gui.conf.sample: replace
-         more icc by para
-
-2003-12-27 06:35  maan
-
-       * Makefile, dbtool.sample, server.conf.sample: replace icc_server
-         by para_server
-
-2003-12-27 06:30  maan
-
-       * Makefile, client.c, dbtool.c, dbtool.sample, exec.c, gui.c,
-         gui_common.c, net.c, sdl_gui.c, server.h: replace icc.h by para.h
-
-2003-12-27 06:25  maan
-
-       * para.h: former icc.h
-
-2003-12-27 06:25  maan
-
-       * icc.h: renamed to para.h
-
-2003-12-27 06:21  maan
-
-       * CREDITS, INSTALL, Makefile, NEWS, README, README.dbtool,
-         client.c, client.conf.sample, client.ggo, command.c, dbtool.c,
-         dbtool.conf.sample, dbtool.ggo, gui.c, gui.ggo, icc.h, sdl_gui.c,
-         sdl_gui.ggo, server.c, server.ggo: replace icc by para
-
-2003-12-27 05:54  maan
-
-       * pics/default.jpg: no more stinky fingers as default, use
-         paraslash logo instead
-
-2003-12-27 00:12  maan
-
-       * xmms-1.2.7.titlebar_vbr_patch: no longer needed
-
-2003-12-27 00:11  maan
-
-       * NEWS: [no log message]
-
-2003-12-27 00:11  maan
-
-       * Makefile: remove dependencies on Makefile
-
-2003-12-26 21:51  maan
-
-       * VERSION: bump version number to 98
-
-2003-12-26 18:53  maan
-
-       * NEWS, README, README.dbtool, gui.c, icc.h: cosmetics
-
-2003-12-26 18:09  maan
-
-       * icc.h, server.h: move struct command from icc.h to server.h
-
-2003-12-26 18:07  maan
-
-       * icc.h, server.h: move struct user from icc.h to server.h
-
-2003-12-26 18:04  maan
-
-       * gui.c, gui_common.c, icc.h, sdl_gui.c: move open_stat_pipe to
-         gui_common.c
-
-2003-12-26 17:37  maan
-
-       * NEWS: [no log message]
-
-2003-12-26 17:37  maan
-
-       * sdl_gui.c: change placement to make all items fit on a 1024x768
-         screen. Also fix config file reading
-
-2003-12-26 17:37  maan
-
-       * afs.c, server.c: loglevel adjustments
-
-2003-12-26 17:37  maan
-
-       * gui_common.c: new function: file_exists
-
-2003-12-26 17:37  maan
-
-       * gui.c: simplify config_file exitst by using new function
-         file_exists
-
-2003-12-26 17:37  maan
-
-       * Makefile: tgz: make four tarballs
-
-2003-12-26 03:09  maan
-
-       * gui.c: base_key: return given value itsself rather that -1 if we
-         have no idea what is is
-
-2003-12-26 02:32  maan
-
-       * gui.c: print codename in Welcome message
-
-2003-12-26 02:32  maan
-
-       * Makefile: include CODENAME in version.h
-
-2003-12-26 02:10  maan
-
-       * server.h: nuke mmd->remaining since it is derived
-
-2003-12-26 02:10  maan
-
-       * gui.c: logical reordering of functions
-
-2003-12-26 02:09  maan
-
-       * server.c: cosmetics
-
-2003-12-26 02:08  maan
-
-       * afs.c, command.c: finetune former afs commands, nuke
-         mmd->remaining since it is derived
-
-2003-12-25 23:52  maan
-
-       * afs.c, server.c: send each file to separate poc process
-
-2003-12-25 21:32  maan
-
-       * gui.c: cosmetics
-
-2003-12-25 21:30  maan
-
-       * gui.c: simplify base_key function
-
-2003-12-25 20:41  maan
-
-       * gui.c: new command: CTRL+L to repaint screen
-
-2003-12-25 20:40  maan
-
-       * command.c: fix cr problem with sb
-
-2003-12-25 19:04  maan
-
-       * afs.c, command.c, server.h: move compute_sb_string from afs to
-         command.c allowing to nuke mmd->status_bar
-
-2003-12-25 18:39  maan
-
-       * afs.c, command.c, server.c, server.h: nuke mmd->afs_status, use
-         only status_flags instead
-
-2003-12-25 17:32  maan
-
-       * gui.c: new commands: F1-F10 to jmp, rename some commands
-
-2003-12-25 02:36  maan
-
-       * server.c, server.h: move gengetopt_args_info conf out of mmd
-
-2003-12-25 02:22  maan
-
-       * afs.c, server.h: get rid of file_status in mmd
-
-2003-12-25 02:06  maan
-
-       * afs.c, server.h: compute mmd->total and friends in
-         afs_send_chunk, not in update_mmd, get rid of mmd->soc
-
-2003-12-25 01:48  maan
-
-       * command.c, server.c, server.h: implement counter for currently
-         active connections
-
-2003-12-25 01:08  maan
-
-       * server.c, server.h: store semid for mmd locking in extern
-         variable, not in mmd itsself
-
-2003-12-25 01:05  maan
-
-       * command.c, server.c, server.h: move mmd lock functions from
-         command.c to server.c
-
-2003-12-25 00:54  maan
-
-       * afs.c, server.c: small logging cleanups
-
-2003-12-23 17:37  maan
-
-       * Makefile: add codename funny
-
-2003-12-23 17:11  maan
-
-       * afs.c, command.c, sdl_gui.c: stat prints wheather current mp3 has
-         vbr or not
-
-2003-12-23 05:25  maan
-
-       * server.c: init mmd->server_pid. That broke hup
-
-2003-12-23 05:23  maan
-
-       * afs.c: cosmetics
-
-2003-12-23 03:54  maan
-
-       * command.c: dont sleep after last status line has been printed
-
-2003-12-23 00:54  maan
-
-       * afs.c, server.c: add some logging
-
-2003-12-22 23:22  maan
-
-       * INSTALL, README: [no log message]
-
-2003-12-22 23:08  maan
-
-       * afs.c, client.c, command.c, net.c, server.c, server.h: change log
-         to clog to avoid compiler warnings
-
-2003-12-22 22:16  maan
-
-       * gui.conf.sample: adapt to new decode_cmd syntax
-
-2003-12-22 22:15  maan
-
-       * gui.c: introduce (and check in do_select) decoder_pipe to avoid
-         multiple instances of the decoder
-
-2003-12-22 16:08  maan
-
-       * server.h: adapt afs_send_chunk to new syntax
-
-2003-12-22 16:07  maan
-
-       * server.c: reopen poc pipe if afs_send_chunk failed
-
-2003-12-22 16:06  maan
-
-       * gui.c: ignore SIGCHLD and SIGPIPE
-
-2003-12-22 16:05  maan
-
-       * afs.c: make afs_send_chunk return negative value if write to
-         poc_pipe failed
-
-2003-12-22 04:59  maan
-
-       * server.c: fix stop command
-
-2003-12-22 04:58  maan
-
-       * command.c: fix stop command, log cleanups
-
-2003-12-22 04:57  maan
-
-       * afs.c: log cleanups
-
-2003-12-22 04:56  maan
-
-       * NEWS: aaa
-
-2003-12-22 04:56  maan
-
-       * Makefile: change THANKS to CREDITS
-
-2003-12-22 04:55  maan
-
-       * CREDITS: add Manuel Odendahl
-
-2003-12-22 02:55  maan
-
-       * CREDITS: former THANKS
-
-2003-12-22 02:55  maan
-
-       * THANKS: renamed to CREDITS
-
-2003-12-22 02:44  maan
-
-       * 1.0, INSTALL, Makefile, NEWS, README, afs.c, command.c, exec.c,
-         gui.c, gui.ggo, icc.h, server.c, server.conf.sample, server.ggo,
-         server.h: kick icecast, switch to poc
-
-2003-12-22 02:44  maan
-
-       * icecast.conf.sample, init_afs.c: no longer needed
-
-2003-12-20 15:48  maan
-
-       * server.c: kill pid_list code (was commented out)
-
-2003-12-20 15:44  maan
-
-       * gui.conf.sample: sample configuration file
-
-2003-12-20 15:43  maan
-
-       * NEWS: [no log message]
-
-2003-12-20 15:41  maan
-
-       * gui.c, gui.ggo: implement auto-decoding
-
-2003-12-20 15:39  maan
-
-       * afs.c: set status to playing _after_ the first chunk has been
-         streamed
-
-2003-12-18 01:48  maan
-
-       * command.c, server.c: fix term command which broke due to removal
-         of pid_list
-
-2003-12-17 00:54  maan
-
-       * SFont.c: include stdlib to avoid compiler warnings
-
-2003-12-17 00:28  maan
-
-       * NEWS: [no log message]
-
-2003-12-17 00:18  maan
-
-       * server.c: Pid was logged twice in debug mode. Fix it.
-
-2003-12-17 00:13  maan
-
-       * command.c, gui.c, init_afs.c, server.c: comment out racy pid_list
-         crap
-
-2003-12-04 23:22  maan
-
-       * dbtool.conf.sample: document gengetopt's strange behaviour
-         concerning backsslashes in arguments
-
-2003-12-04 01:29  maan
-
-       * dbtool.c: cosmetics
-
-2003-12-02 02:31  maan
-
-       * dbtool.c: fix help test format for verb
-
-2003-12-02 02:08  maan
-
-       * dbtool.c: print score of zero if it is undefined
-
-2003-12-02 00:37  maan
-
-       * dbtool.c: new command: verb
-
-2003-12-02 00:37  maan
-
-       * NEWS: [no log message]
-
-2003-12-01 23:20  maan
-
-       * SFont.h: Missing since ages. Reported by Thomas Forell
-
-2003-12-01 23:18  maan
-
-       * NEWS: insert release date of 0.0.97
-
-2003-11-23 21:19  maan
-
-       * gui.c: dont die on sigchld. That was silly. Instead, die on
-         sighup and change signal for rereading conf from hup to usr1
-
-2003-11-23 21:11  maan
-
-       * README.dbtool: document pic column
-
-2003-11-23 21:10  maan
-
-       * dbtool.c: test args_info.find_cmd_given, fix typo
-
-2003-11-20 00:22  maan
-
-       * gui.c: Die on sigchld. This should fix stale  processes that eat
-         up memory.
-
-2003-10-30 21:51  maan
-
-       * server.c, server.ggo: dont change stream on hup
-
-2003-10-26 14:11  maan
-
-       * VERSION: bump version number to 97
-
-2003-10-26 12:18  maan
-
-       * Makefile: add THANKS to txts
-
-2003-10-26 12:18  maan
-
-       * NEWS: cosmetics
-
-2003-10-26 00:46  maan
-
-       * NEWS: cosmetics
-
-2003-10-26 00:41  maan
-
-       * NEWS: nuke icc prefix
-
-2003-10-26 00:33  maan
-
-       * gui.c: com_sleep: change stream _after_ sleep
-
-2003-10-21 22:35  maan
-
-       * README.dbtool: some small changes
-
-2003-10-21 22:25  maan
-
-       * README: sdl_gui update
-
-2003-10-21 21:48  maan
-
-       * dbtool.ggo: gengetopt file for dbtool
-
-2003-10-21 21:46  maan
-
-       * THANKS: list of people who helped developing this project
-
-2003-10-21 21:23  maan
-
-       * gui.c: change snooze key from o to a (because it is easier to hit
-         on the keyboard when dazed and confused in the morning)
-
-2003-10-20 23:58  maan
-
-       * sdl_gui.ggo: add option w which is currently ignored but needed
-         to start sdl_gui from xscreensaver
-
-2003-10-20 23:57  maan
-
-       * gui.c: new function: outputf to print formated output. Avoid
-         sleep on startup, print message in bot window instead.
-
-2003-10-20 23:56  maan
-
-       * client.c: cosmetics
-
-2003-10-20 23:56  maan
-
-       * NEWS: [no log message]
-
-2003-10-20 22:33  maan
-
-       * gui.c: cosmetics
-
-2003-10-20 22:08  maan
-
-       * gui.c: do not cleanup in interrupt handler (that crap originally
-         came from some examples), set flag instead. This allows to omit
-         the strange sleep(1) in finish, so quiting quits immediately now.
-         Also, catch sigterm and exit gracefully.
-
-2003-10-20 21:43  maan
-
-       * gui.c: catch sighup and reread configuration on sighup
-
-2003-10-19 22:58  maan
-
-       * afs.c: cosmetics
-
-2003-10-19 17:11  maan
-
-       * gui.c: make sleep kinda work
-
-2003-10-11 18:49  maan
-
-       * dbtool.c: we only need basename of row[1]
-
-2003-10-11 18:46  maan
-
-       * command.c: help text update
-
-2003-10-11 18:46  maan
-
-       * NEWS: update
-
-2003-10-11 18:46  maan
-
-       * Makefile: Change default prefix to /usr/local
-
-2003-10-08 18:02  maan
-
-       * dbtool.c: update syntax of fut in help (reported by Thomas
-         Forell)
-
-2003-10-07 19:39  maan
-
-       * exec.c: dont dup stderr
-
-2003-10-07 19:38  maan
-
-       * gui.c, gui.ggo: add decode command
-
-2003-10-07 19:33  maan
-
-       * dbtool.c: remove useless printf
-
-2003-10-07 19:33  maan
-
-       * client.c: fix interactive mode (reported by Thomas Forell)
-
-2003-09-25 02:46  maan
-
-       * dbtool.c: minor cleanups
-
-2003-09-24 23:35  maan
-
-       * dbtool.conf.sample: put quotes around find_cmd
-
-2003-09-24 23:34  maan
-
-       * dbtool.c: switch to gengetopt
-
-2003-09-24 23:34  maan
-
-       * NEWS: update
-
-2003-09-24 23:34  maan
-
-       * Makefile: dbtool depends on dbtool.cmdline.c
-
-2003-09-23 20:46  maan
-
-       * Makefile: nuke unused TODO stuff
-
-2003-09-23 19:22  maan
-
-       * gui.ggo: gengetopt file for gui
-
-2003-09-23 19:17  maan
-
-       * command.c, icc.h, server.c: change all handlers from static int
-         com_foo(char *, int); to static int com_foo(int, int, char **);,
-         i.e. pass pointerarray instead of command line to handlers
-
-2003-09-23 19:13  maan
-
-       * afs.c: cosmetics
-
-2003-09-23 19:12  maan
-
-       * .tdldb: unused since May 2002
-
-2003-09-23 15:23  maan
-
-       * server.c: use popen_read_dbtool
-
-2003-09-23 15:23  maan
-
-       * sdl_gui.c: use popen_read_client
-
-2003-09-23 15:22  maan
-
-       * command.c, icc.h: cosmetics
-
-2003-09-22 22:04  maan
-
-       * gui.c: simplify cases (vol1 - vol9), add sleep command to help
-         text
-
-2003-09-22 21:46  maan
-
-       * gui.c: new function: configfile_exists.  do_select: change long
-         int timeout ro tong long timeout. Long timeouts didnt work
-         otherwise.  Fix thinko with select_timeout (introducing
-         default_select_timeout).  New function and command: reread
-         config.
-
-2003-09-22 20:59  maan
-
-       * afs.c: print two digits sor seconds in log info
-
-2003-09-22 17:18  maan
-
-       * gui.c: use gengetopt
-
-2003-09-22 17:18  maan
-
-       * Makefile: gui depends on gui.cmdline.c
-
-2003-09-22 02:50  maan
-
-       * command.c: cosmetics
-
-2003-09-22 02:43  maan
-
-       * dbtool.c: dokumentation update for na
-
-2003-09-22 02:30  maan
-
-       * afs.c: use mmd->size instead of equivalent
-         mmd->file_status.st_size
-
-2003-09-22 02:23  maan
-
-       * afs.c: cosmetics
-
-2003-09-22 02:08  maan
-
-       * icc.h: add popen_read_dbtool
-
-2003-09-22 02:08  maan
-
-       * command.c: use popen_read_dbtool, log correct fd in debug mode
-
-2003-09-22 02:06  maan
-
-       * afs.c: use popen_read_dbtool
-
-2003-09-22 02:06  maan
-
-       * exec.c: new function popen_read_dbtool
-
-2003-09-22 01:03  maan
-
-       * sdl_gui.c: use popen again for commands (with popoen_read,
-         commands having more than one arg doesnt work)
-
-2003-09-22 01:01  maan
-
-       * icc.h: add new function popen_read_client
-
-2003-09-22 01:01  maan
-
-       * gui.c: use new function popen_read_client
-
-2003-09-22 00:59  maan
-
-       * exec.c: new function: popen_read_client
-
-2003-09-22 00:59  maan
-
-       * command.c: adapt help text of ff to new syntax
-
-2003-09-22 00:58  maan
-
-       * Makefile: sdl_gui depends on exec.c
-
-2003-09-22 00:58  maan
-
-       * afs.c: make ff work again with negative offset
-
-2003-09-21 15:15  maan
-
-       * sdl_gui.c: use popen_read where it makes sense
-
-2003-09-19 14:19  maan
-
-       * server.conf.sample: replace never uptodate example configureation
-         by hint to read help
-
-2003-09-19 14:18  maan
-
-       * gui.c: use popen_read
-
-2003-09-19 14:17  maan
-
-       * exec.c: dont log anything, dont exit on errors, return NULL
-         instead
-
-2003-09-19 14:16  maan
-
-       * Makefile: icc_gui needs exec.c
-
-2003-09-18 03:35  maan
-
-       * gui.c: only use strlen if we really need to know the length
-
-2003-09-18 02:38  maan
-
-       * gui.c: cosmetics
-
-2003-09-18 02:24  maan
-
-       * gui.c: tidy up do_select
-
-2003-09-18 02:04  maan
-
-       * gui.c: fix output bug causing end of buffer being displayed on
-         next command
-
-2003-09-17 23:20  maan
-
-       * command.c: help: fix alphabetical ordering
-
-2003-09-17 22:53  maan
-
-       * server.c: use popen_read instead of popen
-
-2003-09-17 22:52  maan
-
-       * exec.c: dont write to stdout/stderr
-
-2003-09-17 22:52  maan
-
-       * command.c: cosmetics
-
-2003-09-17 22:38  maan
-
-       * command.c: lcl_init: use popen_read another time
-
-2003-09-17 22:35  maan
-
-       * command.c: lcl_init: use popen_read
-
-2003-09-17 22:07  maan
-
-       * server.c: always print strerror() text from exit status
-
-2003-09-17 22:07  maan
-
-       * icc.h: add prototype for popen_read_vp
-
-2003-09-17 22:06  maan
-
-       * exec.c: new function: popen_read_vp (ala execvp)
-
-2003-09-17 22:05  maan
-
-       * dbtool.c: change syntax of picass: Now ID has to be specified
-         instead of name.  escape filenames several times.
-
-2003-09-17 22:03  maan
-
-       * command.c: afs_cmd_handler: replace \n by space in args.  new
-         function: split args.  dbtool_cmd_handler: use popen_read_vp
-         instead of popen.
-
-2003-09-17 21:58  maan
-
-       * client.c: dont send addidtional space in commands
-
-2003-09-17 21:58  maan
-
-       * afs.c: dont log warnings on EINTR.
-
-2003-09-17 17:31  maan
-
-       * dbtool.c: info: dont continue if entry not found
-
-2003-09-17 17:26  maan
-
-       * dbtool.c: replace add_slashes by escape_str which calls
-         mysql_real_escape_string
-
-2003-09-17 00:49  maan
-
-       * NEWS: update
-
-2003-09-17 00:48  maan
-
-       * afs.c: replace popen by popen_read
-
-2003-09-17 00:32  maan
-
-       * icc.h: add popen_read
-
-2003-09-17 00:32  maan
-
-       * dbtool.c: modify add_slashes to return dynamically allocated
-         buffer.  use add_slashes in us and info.
-
-2003-09-17 00:30  maan
-
-       * afs.c: use popen_read instead of popen
-
-2003-09-17 00:30  maan
-
-       * Makefile: server depends on exec.c
-
-2003-09-17 00:29  maan
-
-       * exec.c: new: contains only popen_read for now
-
-2003-09-15 03:51  maan
-
-       * dbtool.c: cosmetics
-
-2003-09-15 03:16  maan
-
-       * dbtool.c: help text update
-
-2003-09-15 02:09  maan
-
-       * 1.0, command.c, server.conf.sample: print
-
-2003-09-15 02:01  maan
-
-       * dbtool.c: print mysql_error message in get_resuts, use strlen
-         only where neccessary
-
-2003-09-15 01:59  maan
-
-       * command.c: insert commands in alphabetical order into lcl
-
-2003-09-14 23:13  maan
-
-       * dbtool.c: cosmetics
-
-2003-09-14 20:15  maan
-
-       * dbtool.c: do not write to stderr
-
-2003-09-14 18:39  maan
-
-       * dbtool.c: fut: dont limit number of matches since we have to
-         consider all matches to make the magic randomizer work. That
-         hopefully fixed the bug that made output of fut appear
-         alphabetically ordered if there are many new songs.
-
-2003-09-14 17:30  maan
-
-       * server.h: nuke duplicated info in mmd.stream_name.  change pid_t
-         init_afs to void init_afs.  add pid_list_add.
-
-2003-09-14 17:28  maan
-
-       * server.ggo: use empty default value for stream
-
-2003-09-14 17:27  maan
-
-       * server.c: change static void pid_list_add(pid_t pid) to void
-         pid_list_add(pid_t pid).  exit on errors.  stream_list_init: make
-         sure that initial stream is valid.  simplify handle_sighup and
-         main.
-
-2003-09-14 17:24  maan
-
-       * init_afs.c: exit on errors.  set mmd->afs_pid
-
-2003-09-14 17:22  maan
-
-       * command.c:
-         replace mmd->stream_name by mmd->stream, handle_cmd: exit on
-         errors, new fuction: find_stream, simplify com_cs,
-
-2003-09-14 17:19  maan
-
-       * afs.c: replace mmd->stream_name by mmd->stream
-
-2003-09-13 19:50  maan
-
-       * server.c: new function: init_network
-
-2003-09-13 19:45  maan
-
-       * server.c: new function: init_network
-
-2003-09-13 19:44  maan
-
-       * icc.h, net.c: new functions: setserversockopts, do_bind
-
-2003-09-13 17:13  maan
-
-       * Makefile: make also light tarballs
-
-2003-09-13 16:47  maan
-
-       * NEWS, README, dbtool.c: small documentation update
-
-2003-09-13 16:38  maan
-
-       * Makefile: tgz include date in tarball
-
-2003-09-13 15:59  maan
-
-       * icc.h: do not include argtable.h
-
-2003-09-13 15:53  maan
-
-       * server.conf.sample: remove list of users
-
-2003-09-13 15:52  maan
-
-       * dbtool.conf.sample: change user name from maan to foo
-
-2003-09-13 15:51  maan
-
-       * INSTALL, README.dbtool: document cdb function
-
-2003-09-13 15:50  maan
-
-       * dbtool.c: new command: cdb (create database)
-
-2003-09-12 20:54  maan
-
-       * Makefile: add server.h to headers
-
-2003-09-12 20:28  maan
-
-       * net.c, server.c: logging adjustments
-
-2003-09-12 19:32  maan
-
-       * Makefile: clean: distclean: Remove also ChangLog.bak
-
-2003-09-12 19:24  maan
-
-       * dbtool.c: cosmetics
-
-2003-09-12 19:24  maan
-
-       * command.c: replace icc_server by server in help text
-
-2003-09-12 19:22  maan
-
-       * client.conf.sample: adapt name of keyfile
-
-2003-09-12 19:21  maan
-
-       * README.dbtool: typo
-
-2003-09-12 19:13  maan
-
-       * Makefile: adapt to new filenames without icc prefix
-
-2003-09-12 18:58  maan
-
-       * dbtool.sample: former icc_dbtool.sample
-
-2003-09-12 18:58  maan
-
-       * icc_dbtool.sample: rename to dbtool.sample
-
-2003-09-12 18:54  maan
-
-       * server.c: former icc_server.c
-
-2003-09-12 18:54  maan
-
-       * icc_server.c: rename to server.c
-
-2003-09-12 18:52  maan
-
-       * dbtool.c: former icc_dbtool.c
-
-2003-09-12 18:52  maan
-
-       * icc_dbtool.c: rename ro dbtool.c
-
-2003-09-12 18:49  maan
-
-       * gui.c: former icc_gui.c
-
-2003-09-12 18:49  maan
-
-       * icc_gui.c: renamed to gui.c
-
-2003-09-12 18:48  maan
-
-       * client.c: former icc_client.c
-
-2003-09-12 18:47  maan
-
-       * icc_client.c: renamed to client.c
-
-2003-09-12 18:16  maan
-
-       * afs.c, command.c, icc.h, icc_client.c, icc_dbtool.c, icc_gui.c,
-         init_afs.c, sdl_gui.c: nuke cvs keyword expansion
-
-2003-09-12 18:16  maan
-
-       * Makefile: ChangeLog: Dont show time
-
-2003-09-12 17:42  maan
-
-       * icc.h: nuke unused CONFIDENTIAL bit
-
-2003-09-12 17:41  maan
-
-       * INSTALL: update name of conf files
-
-2003-09-12 17:05  maan
-
-       * Makefile: clean: remove also HTML dir
-
-2003-09-12 17:02  maan
-
-       * Makefile, README: minor cleanups
-
-2003-09-12 16:51  maan
-
-       * dbtool.conf.sample: former icc_dbtool.conf.sample
-
-2003-09-12 16:51  maan
-
-       * icc_dbtool.conf.sample: rename to dbtool.conf.sample
-
-2003-09-12 16:50  maan
-
-       * server.conf.sample: former icc_server.conf.sample
-
-2003-09-12 16:49  maan
-
-       * icc_server.conf.sample: rename to server.conf.sample
-
-2003-09-12 16:48  maan
-
-       * icc_server.c: documentation cleanups
-
-2003-09-12 16:47  maan
-
-       * README.dbtool: comment new sa syntax
-
-2003-09-12 16:47  maan
-
-       * README: update
-
-2003-09-11 03:04  maan
-
-       * icc_server.c: use /dev/urandom to create random numbers
-
-2003-09-11 02:03  maan
-
-       * icc_server.c: cosmetics
-
-2003-09-11 01:53  maan
-
-       * command.c, icc_server.c: minor simplifyings/cleanups/log message
-         fixes
-
-2003-09-11 01:53  maan
-
-       * Makefile: change icc_bash_completion to bash_completion
-
-2003-09-11 01:25  maan
-
-       * bash_completion: former icc_bash_completion
-
-2003-09-11 01:25  maan
-
-       * icc_bash_completion: rename to bash_completion
-
-2003-09-11 01:24  maan
-
-       * icc_server.c, net.c: send_buffer: dont send nullbytes
-
-2003-09-11 01:24  maan
-
-       * icc_bash_completion: cosmetics
-
-2003-09-11 01:08  maan
-
-       * icc_server.c, net.c: modify init_sockaddr to suit needs of both
-         client and server
-
-2003-09-10 22:18  maan
-
-       * icc_bash_completion: adapt to new sa syntax
-
-2003-09-10 03:49  maan
-
-       * icc_dbtool.c: sa syntax conflixted with gengetopt. Fix that.
-
-2003-09-10 02:04  maan
-
-       * icc.h, icc_server.c, net.c: new function: do_accept
-
-2003-09-10 01:36  maan
-
-       * icc_server.c: new function handle_connect to reduce size of main
-
-2003-09-10 01:14  maan
-
-       * icc_server.c: decrease verbosity and avoid buffer overflow
-
-2003-09-10 00:39  maan
-
-       * net.c: fix silly bug
-
-2003-09-10 00:39  maan
-
-       * icc_server.c: use recv_buffer function from net.c
-
-2003-09-09 23:01  maan
-
-       * icc_server.c: daemon_init: reverse return value
-
-2003-09-09 21:48  maan
-
-       * afs.c, icc_server.c, init_afs.c, server.ggo, server.h: make hup
-         work again
-
-2003-09-09 17:17  maan
-
-       * sdl_gui.c: adapt to new syntax of check_buf_for_items
-
-2003-09-09 17:16  maan
-
-       * icc_gui.c: use check_buf_for_items whenever possible
-
-2003-09-09 17:14  maan
-
-       * icc.h: update check_buf_for_items()
-
-2003-09-09 17:13  maan
-
-       * gui_common.c: make check_buf_for_items work again...
-
-2003-09-09 17:12  maan
-
-       * command.c: afs_cmd_handler: write terminiating null byte
-
-2003-09-09 04:43  maan
-
-       * icc_gui.c, icc_server.c: some small cleanups
-
-2003-09-09 04:43  maan
-
-       * NEWS: update
-
-2003-09-09 04:04  maan
-
-       * icc_server.c: many small cleanups
-
-2003-09-09 03:27  maan
-
-       * icc_server.c: nuke unneded functions
-
-2003-09-09 03:23  maan
-
-       * server.ggo: gengetopt file for server
-
-2003-09-09 03:21  maan
-
-       * afs.c, icc_server.c, init_afs.c, server.h: switch to gengetopt
-
-2003-09-09 01:41  maan
-
-       * Makefile, afs.c, command.c, icc_server.c, init_afs.c: include
-         server.h instead of icc.h
-
-2003-09-09 01:41  maan
-
-       * icc.h: move server specific parts to server.h
-
-2003-09-09 01:40  maan
-
-       * server.h: server part of icc.h
-
-2003-09-09 01:16  maan
-
-       * Makefile: server: compile server.cmdline.c
-
-2003-09-09 00:43  maan
-
-       * Makefile: remove .c,.h files generated by gengetopt on distclean
-
-2003-09-09 00:33  maan
-
-       * client.conf.sample: former icc_client.conf.sample
-
-2003-09-09 00:33  maan
-
-       * icc_client.conf.sample: renamed to client.conf.sample
-
-2003-09-09 00:19  maan
-
-       * icc_client.c: setlinebuf is no longer needed
-
-2003-09-09 00:19  maan
-
-       * Makefile, icc.h, icc_gui.c, sdl_gui.c: use new gui_common
-
-2003-09-09 00:17  maan
-
-       * gui_common.c: common functions for sdl_gui and ncurses gui
-
-2003-09-08 23:16  maan
-
-       * icc_gui.c: make it work even when stat output contains null bytes
-
-2003-09-08 20:56  maan
-
-       * Makefile: filter out gengetopts c files from sources
-
-2003-09-08 20:42  maan
-
-       * sdl_gui.conf.sample: former icc_sdl_gui.conf.sample
-
-2003-09-08 20:41  maan
-
-       * icc_sdl_gui.conf.sample: rename to sdl_gui.conf.sample
-
-2003-09-08 20:40  maan
-
-       * Makefile, sdl_gui.c, sdl_gui.ggo: use sdl_gui.cmdline.c instead
-         of sdl_gui_cmdline.c, same for .h
-
-2003-09-08 20:35  maan
-
-       * client.ggo: gengetopt file for client
-
-2003-09-08 20:24  maan
-
-       * Makefile, command.c, icc.h: server: use net.c
-
-2003-09-08 20:17  maan
-
-       * net.c: new file, contains networking routines
-
-2003-09-08 20:16  maan
-
-       * Makefile, icc.h, icc_client.c: icc_client: use gengetopt and new
-         net.c
-
-2003-09-08 20:16  maan
-
-       * sdl_gui.c: typo
-
-2003-09-08 03:20  maan
-
-       * Makefile: remove version.h on distclean
-
-2003-09-08 03:15  maan
-
-       * icc_sdl_gui.conf.sample: sample configuration for sdl_gui
-
-2003-09-08 03:08  maan
-
-       * Makefile, sdl_gui.c: switch to gengetopts for sdl_gui
-
-2003-09-08 03:07  maan
-
-       * icc_dbtool.c: cosmetics
-
-2003-09-08 02:43  maan
-
-       * sdl_gui.ggo: gengetopt file for sdl_gui
-
-2003-09-07 21:30  maan
-
-       * NEWS: update
-
-2003-09-07 21:05  maan
-
-       * command.c, icc_server.c: cosmetics
-
-2003-09-07 20:42  maan
-
-       * command.c: cosmetics
-
-2003-09-07 20:25  maan
-
-       * command.c, icc.h, icc_server.c: nuke mmd->handler, rename
-         mmd->handler_fct to mmd->handler
-
-2003-09-07 20:10  maan
-
-       * command.c, icc.h, icc_server.c: nuke handle_cmd function since
-         all commands a now directly called via function pointers
-
-2003-09-07 18:41  maan
-
-       * command.c: use function pointers to execute internal commands
-
-2003-09-07 18:06  maan
-
-       * command.c, icc.h, icc_server.c: major lcl cleanup
-
-2003-09-07 15:43  maan
-
-       * command.c: simplify struct linked_cmd_list
-
-2003-09-07 15:16  maan
-
-       * command.c, icc.h: reordering of functions
-
-2003-09-07 14:37  maan
-
-       * icc_server.c, init_afs.c: logic was reversed in handle_sighup
-
-2003-09-07 14:34  maan
-
-       * command.c: own function for each internal command
-
-2003-09-07 05:56  maan
-
-       * afs.c, command.c, icc.h, icc_server.c, init_afs.c: major function
-         arguments cleanup
-
-2003-09-07 01:31  maan
-
-       * command.c, icc.h, icc_server.c, init_afs.c: move afs_server_pipe
-         and server_afs_pipe to mmd
-
-2003-09-07 00:59  maan
-
-       * command.c: C99 struct initializers
-
-2003-09-07 00:19  maan
-
-       * command.c: fix potential buffer overflow in compute_status
-
-2003-09-07 00:04  maan
-
-       * command.c: use static functions whenever possible
-
-2003-09-06 23:42  maan
-
-       * command.c: comment out unused lcl_log_list, cosmetics
-
-2003-09-06 23:29  maan
-
-       * command.c: change return type for lcl_del from int to void
-
-2003-09-06 23:17  maan
-
-       * command.c: fix potential buffer overflow, change return value of
-         lcl_add to void
-
-2003-09-06 23:02  maan
-
-       * command.c: cleanup crappy cmd_handler_itohuman
-
-2003-09-06 22:50  maan
-
-       * command.c: fix potential buffer overflow
-
-2003-09-06 22:43  maan
-
-       * command.c: cosmetics
-
-2003-09-06 22:40  maan
-
-       * command.c, icc.h, icc_server.c: handle_cmd doesnt need argument
-         mmd
-
-2003-09-06 22:35  maan
-
-       * afs.c: rename bear_server_pipe to afs_server_pipe
-
-2003-09-06 22:23  maan
-
-       * icc_server.c: rename cb_fd to afs_server_pipe
-
-2003-09-06 22:20  maan
-
-       * icc_server.c: cleanup crappy istrue function
-
-2003-09-06 20:37  maan
-
-       * afs.c, icc_server.c: cosmetics
-
-2003-09-06 20:32  maan
-
-       * afs.c: cosmetics
-
-2003-09-06 20:25  maan
-
-       * icc_server.c, afs.c, command.c, icc.h, init_afs.c: no need to
-         pass mmd via functions, it is an exported variable
-
-2003-09-06 20:07  maan
-
-       * icc_server.c: no need to pass mmd to handle_sighup
-
-2003-09-06 20:03  maan
-
-       * icc_server.c: use static functions whenever possible
-
-2003-09-06 19:52  maan
-
-       * icc_server.c: read_config: doesnt need return value, exit on
-         errors instead
-
-2003-09-06 19:45  maan
-
-       * icc_server.c: reverse logic after fork to have nicer idents
-
-2003-09-06 19:36  maan
-
-       * icc_server.c: minor cleanups
-
-2003-09-06 19:04  maan
-
-       * icc_server.c: handle_sighup doesnt need command line options
-
-2003-09-06 18:45  maan
-
-       * icc_server.c: read_config: close config file unconditionally
-
-2003-09-06 18:35  maan
-
-       * icc_server.c: read_config: close config file
-
-2003-09-06 18:29  maan
-
-       * icc.h: cmd_perms_itohuman doesnt need return value
-
-2003-09-06 18:25  maan
-
-       * icc_server.c, command.c: cmd_perms_itohuman doesnt need return
-         value
-
-2003-09-06 18:22  maan
-
-       * icc_server.c: fix potential buffer overflow, change challenge_nr
-         and chall_response to long unsigned
-
-2003-09-06 18:13  maan
-
-       * icc_server.c: indent main function
-
-2003-09-06 18:00  maan
-
-       * icc_server.c: C99 struct initializers
-
-2003-09-06 17:54  maan
-
-       * icc_server.c: simplify signal handling
-
-2003-09-06 16:47  maan
-
-       * icc_server.c: fix potential buffer overflow in encrypt
-
-2003-09-06 16:44  maan
-
-       * icc_server.c: fix potential buffer overflow in uptime_str
-
-2003-09-06 16:19  maan
-
-       * command.c, icc.h, icc_server.c: fix pidlist locking (I hope)
-
-2003-09-06 15:31  maan
-
-       * icc_server.c: use EXIT_FAILURE/EXIT_SUCCESS in exit()
-
-2003-09-06 15:26  maan
-
-       * icc_server.c: change int pid_list_add to void pid_list_add, same
-         with pid_list_unlock
-
-2003-09-06 15:07  maan
-
-       * icc_server.c: use size_t instead of int in set_conf()
-
-2003-09-06 15:06  maan
-
-       * afs.c: change int read_n_exec() to void read_n_exec()
-
-2003-09-06 14:45  maan
-
-       * afs.c: fix memory leak in mp3_info
-
-2003-09-06 14:00  maan
-
-       * afs.c, icc.h, icc_server.c: fix some minor bugs found by splint
-
-2003-09-06 13:11  maan
-
-       * icc.h: remove TRUE and FALSE
-
-2003-09-06 13:10  maan
-
-       * icc_gui.c: documentation update
-
-2003-09-06 13:10  maan
-
-       * icc_client.c: coding style
-
-2003-09-03 01:17  maan
-
-       * icc_client.c: still more coding style
-
-2003-09-02 23:45  maan
-
-       * icc_client.c: further coding style issues
-
-2003-09-02 23:17  maan
-
-       * icc_client.c: coding style cleanup
-
-2003-09-02 04:03  maan
-
-       * afs.c, icc_client.c: remove TRUE and FALSE
-
-2003-09-02 03:40  maan
-
-       * icc_client.c: indent main function
-
-2003-08-30 20:02  maan
-
-       * icc_gui.c: fix compile warning, change SNOOZE to 540 seconds
-
-2003-08-30 17:51  maan
-
-       * icc_dbtool.c: piclist now shows length of jpg files
-
-2003-08-30 17:18  maan
-
-       * VERSION: bump version number to 96
-
-2003-08-30 17:08  maan
-
-       * NEWS: update
-
-2003-08-30 17:06  maan
-
-       * icc_dbtool.c: vrfy/clean also checks for invalid pic pointers
-
-2003-08-30 16:13  maan
-
-       * icc_gui.c: update upper window also when fading volume
-
-2003-08-30 05:54  maan
-
-       * icc_dbtool.c: add some dokumentation
-
-2003-08-30 05:32  maan
-
-       * icc_dbtool.c: make picdel work when more than one id is given
-
-2003-08-30 05:26  maan
-
-       * icc_dbtool.c: simplify picadd
-
-2003-08-30 05:12  maan
-
-       * icc_dbtool.c: new command: picch, new function:
-         binfile2escaped_string
-
-2003-08-30 03:31  maan
-
-       * icc_dbtool.c: new command: picdel
-
-2003-08-30 01:40  maan
-
-       * sdl_gui.c: close icc pic pipes, default to pic mode, simplify
-         update_pic
-
-2003-08-30 01:37  maan
-
-       * icc_dbtool.c: new command: picass, new functions: get_very_last,
-         get_pic_id_by_name
-
-2003-08-30 01:33  maan
-
-       * command.c: simplify pic
-
-2003-08-29 22:14  maan
-
-       * icc_dbtool.c: new commands: picadd and piclist
-
-2003-08-29 17:18  maan
-
-       * Makefile: add fonts and pics to tgz
-
-2003-08-29 17:06  maan
-
-       * fonts/24P_Arial_Blue.png, fonts/24P_Arial_Metallic_Yellow.png,
-         fonts/24P_Arial_NeonBlue.png, fonts/24P_Arial_NeonYellow.png,
-         fonts/24P_Copperplate_Blue.png, pics/default.jpg,
-         pics/no_pics.jpg: initial import
-
-2003-08-29 17:04  maan
-
-       * sdl_gui.c: use icc_client pic to obtain the picture, new command
-         line option: pic
-
-2003-08-29 17:02  maan
-
-       * icc_server.c: lcl_init needs to know mmd to set dbtool_has_pic
-
-2003-08-29 17:01  maan
-
-       * icc_dbtool.sample: make it work again...
-
-2003-08-29 17:00  maan
-
-       * icc_dbtool.c: add preliminary pic command
-
-2003-08-29 16:59  maan
-
-       * icc_client.c: use write instead of printf to print output
-
-2003-08-29 16:58  maan
-
-       * icc.h: add dbtool_has_pic to mmd
-
-2003-08-29 16:58  maan
-
-       * command.c: add pic command, introduce send_bin_buffer to transfer
-         binary data
-
-2003-08-29 16:54  maan
-
-       * Makefile: install also fonts and pics
-
-2003-08-28 04:05  maan
-
-       * icc_dbtool.c: implement recursive permutation generator
-
-2003-08-28 00:20  maan
-
-       * sdl_gui.c: documentation update
-
-2003-08-25 05:01  maan
-
-       * sdl_gui.c: many small cleanups
-
-2003-08-25 00:37  maan
-
-       * sdl_gui.c: poll all events, not only one
-
-2003-08-24 23:19  maan
-
-       * sdl_gui.c: nuke shm crap
-
-2003-08-24 23:05  maan
-
-       * sdl_gui.c: use select instead of fork
-
-2003-08-24 19:42  maan
-
-       * sdl_gui.c: implement basic picture viewing
-
-2003-08-22 14:20  maan
-
-       * icc_gui.c: fix silly snooze bug
-
-2003-08-21 22:10  maan
-
-       * NEWS: add 'fix long outstanding case sensitivity bug'
-
-2003-08-21 22:09  maan
-
-       * README.dbtool: document binary format for initial creation
-
-2003-08-21 21:38  maan
-
-       * icc_dbtool.c: fix long outstanding case sensitivity bug
-
-2003-08-21 04:20  maan
-
-       * README: update
-
-2003-08-19 03:26  maan
-
-       * NEWS: update
-
-2003-08-19 03:14  maan
-
-       * icc_gui.c: small cleanups
-
-2003-08-19 03:13  maan
-
-       * icc_dbtool.c: vrfy/clean: check/replace NULL values in attributes
-
-2003-08-19 01:21  maan
-
-       * icc_dbtool.c: na: Create NOT NULL columns
-
-2003-08-18 06:02  maan
-
-       * icc_gui.c: do_select: implement getstr mode and timeout in getch
-         mode. Avoid remaining memory leaks
-
-2003-08-18 02:18  maan
-
-       * icc_gui.c: new fucntion: do_select. Fix memory leak while in
-         scroll mode
-
-2003-08-17 20:46  maan
-
-       * icc_gui.c: remove unneeded header includes
-
-2003-08-17 20:12  maan
-
-       * icc_gui.c: fix bug: some lines of stat's output could be missed
-
-2003-08-17 19:53  maan
-
-       * icc_gui.c: add new commands J, K
-
-2003-08-17 04:46  maan
-
-       * icc_gui.c: add comments on new functions
-
-2003-08-17 04:24  maan
-
-       * icc_gui.c: add past command
-
-2003-08-17 04:00  maan
-
-       * icc_gui.c: more status and error messages
-
-2003-08-17 02:06  maan
-
-       * icc_gui.c: reduce minimal top lines to 8
-
-2003-08-17 01:57  maan
-
-       * icc_gui.c: add f command, nicify help output
-
-2003-08-16 22:57  maan
-
-       * icc_gui.c: implement scrolling, fix sigint handling
-
-2003-08-16 21:34  maan
-
-       * icc_gui.c: implement '!' prefix for commands
-
-2003-08-16 21:11  maan
-
-       * icc_gui.c: add +/- to change size of top/bottom windows
-
-2003-08-16 18:14  maan
-
-       * icc_gui.c: add separator, add input window
-
-2003-08-16 17:30  maan
-
-       * icc_gui.c: kill fake shm struct, fix winch handling
-
-2003-08-16 17:08  maan
-
-       * icc_gui.c: kill shared mem crap, fix ':' command
-
-2003-08-16 16:15  maan
-
-       * icc_gui.c: major improvements, use select instead of fork, use
-         two windows
-
-2003-08-15 03:44  maan
-
-       * icc_gui.c: copy LINES and COLS to shm
-
-2003-08-15 03:25  maan
-
-       * icc_gui.c: use semaphore locking
-
-2003-08-14 23:35  maan
-
-       * icc_gui.c: fix potential buffer overflow in print_item
-
-2003-08-14 23:14  maan
-
-       * icc_gui.c: new function: handle_command
-
-2003-08-14 23:05  maan
-
-       * icc_gui.c: cleanup signal handling and shared mem init
-
-2003-08-14 22:50  maan
-
-       * icc_gui.c: new sigchld handler. Check which child has died
-
-2003-08-14 22:37  maan
-
-       * icc_gui.c: remove useless SIGTSTP handler, some small cleanups
-
-2003-08-14 22:18  maan
-
-       * icc_gui.c: exit gracefully and let the terminal intact
-
-2003-08-14 21:57  maan
-
-       * icc_gui.c:
-         use shmXXX instead of mmap to get shared memory area. Works with
-         2.2 kernels.
-
-         new variable: shm->winch to distinguish between winch and status
-         bar updates. So endwin is only called on winch events.
-
-         new function: print welcome. This is used by parent.
-
-2003-08-10 23:25  maan
-
-       * icc_dbtool.c: na: use default value 0 instead of NULL
-
-2003-07-28 04:27  maan
-
-       * icc_gui.c: fix memory leak, notice when invalid key is pressed
-
-2003-07-27 16:19  maan
-
-       * icc_gui.c: don't update status bar in interrupt handler
-
-2003-07-27 06:04  maan
-
-       * NEWS: add old news for 0.0.93 and some recent news
-
-2003-07-27 05:39  maan
-
-       * icc_gui.c: remove definition of row since this is now a comupted
-         value
-
-2003-07-27 05:13  maan
-
-       * icc_gui.c: make it work with 60x10 terminals, add dokumentation
-
-2003-07-27 04:14  maan
-
-       * icc_gui.c: modify transform vol slightly
-
-2003-07-27 04:10  maan
-
-       * icc_gui.c: kill unneeded print_help function
-
-2003-07-27 04:09  maan
-
-       * icc_gui.c: new command: help
-
-2003-07-27 03:38  maan
-
-       * icc_gui.c: adapt also lines to varying window size. Many small
-         cleanups and fixes
-
-2003-07-27 02:10  maan
-
-       * icc_gui.c: update status bar in sigwinch
-
-2003-07-27 01:52  maan
-
-       * icc_gui.c: make it respect varying window sizes
-
-2003-07-26 20:59  maan
-
-       * icc_gui.c: test window geometry and exit if too narrow
-
-2003-07-17 03:34  maan
-
-       * icc_dbtool.c: print_results: avoid trailing whitespace. Some
-         typos
-
-2003-07-13 02:35  maan
-
-       * Makefile: cleanup, introduce variables CC_FLAGS LD_FLAGS
-         SDL_INCLUDES MYSQL_INCLUDES
-
-2003-07-12 17:55  maan
-
-       * README.dbtool: add mysql commands for creating the tables data
-         and dir
-
-2003-07-09 02:28  maan
-
-       * icc_dbtool.c: more documentation, cosmetics
-
-2003-07-09 01:57  maan
-
-       * README.dbtool: replace LASTPLAYED by LASTPLAYED(). Same with
-         NUMPLAYED. Small cleanups
-
-2003-07-09 01:45  maan
-
-       * Makefile: clean: remove icc_dbtool
-
-2003-07-09 01:41  maan
-
-       * Makefile: nuke make_streams
-
-2003-07-09 01:40  maan
-
-       * make_streams: now implemented in icc_dbtool.c
-
-2003-07-08 23:12  maan
-
-       * icc_dbtool.c: fut: implement parsing functions for new
-         streams_def syntax
-
-2003-07-08 22:30  maan
-
-       * icc_dbtool.c: nuke randomize function which is no longer needed
-
-2003-07-08 22:29  maan
-
-       * icc_dbtool.c: streams: adapt to new syntax, change default
-         filename from streams to stream_defs
-
-2003-06-30 05:03  maan
-
-       * Makefile: icc_server depends on mp3info.h
-
-2003-06-30 04:57  maan
-
-       * Makefile: icc_sdl_gui depends on SFont.c
-
-2003-06-30 04:56  maan
-
-       * SFont.c: comment out unused functions
-
-2003-06-30 04:40  maan
-
-       * SFont.c: indent to kr style
-
-2003-06-30 04:34  maan
-
-       * SFont.c: add GPL header
-
-2003-06-29 22:05  maan
-
-       * INSTALL, README: change icc_dbtool.template to icc_dbtool.sample
-
-2003-06-29 22:04  maan
-
-       * Makefile: major cleanup and bugfixes
-
-2003-06-29 21:17  maan
-
-       * icc_dbtool.sample: former icc_dbtool.template
-
-2003-06-29 21:16  maan
-
-       * icc_dbtool.template: moved to icc_dbtool.sample
-
-2003-06-29 21:01  maan
-
-       * Makefile: Use cvs status to determine if local copy is up to date
-
-2003-06-29 19:44  maan
-
-       * VERSION: bump version number to 95
-
-2003-06-29 19:39  maan
-
-       * Makefile: install: also install make_streams
-
-2003-06-29 19:39  maan
-
-       * make_streams: bash script to produce the streams configuration
-         file
-
-2003-06-29 19:37  maan
-
-       * README.dbtool: document new syntax of streams.in
-
-2003-06-29 18:22  maan
-
-       * 1.0: improve change ssl text
-
-2003-06-29 18:19  maan
-
-       * Makefile: add NEWS and make_streams to sources
-
-2003-06-29 18:15  maan
-
-       * NEWS: contains release notes
-
-2003-06-29 17:53  maan
-
-       * icc_server.c: cosmetics
-
-2003-06-29 17:52  maan
-
-       * icc_bash_completion: use streams command to complete cs
-
-2003-06-28 13:58  maan
-
-       * afs.c: fix small race at end of song play
-
-2003-06-28 13:44  maan
-
-       * icc_server.c: set_conf: Use const char* variables for output of
-         messages to reduce space
-
-2003-06-28 13:12  maan
-
-       * icc_server.c: call wait directly from the sigchld interupt
-         handler to avoid zombies
-
-2003-06-28 13:10  maan
-
-       * command.c: cosmetics
-
-2003-06-22 06:58  maan
-
-       * icc_dbtool.c: fix order of rows bug
-
-2003-06-22 04:18  maan
-
-       * command.c: stat: print streamname continuously
-
-2003-06-22 04:13  maan
-
-       * 1.0, icc_dbtool.c: fix bug: fut n always prints all valid songs
-         instead of only n
-
-2003-06-22 03:51  maan
-
-       * icc_gui.c, sdl_gui.c: print score
-
-2003-06-22 03:51  maan
-
-       * icc_dbtool.template: fut: print dummy score
-
-2003-06-22 03:50  maan
-
-       * command.c: stat: print score
-
-2003-06-22 03:50  maan
-
-       * afs.c: get_song: adapt to new output of fut (contains score now)
-
-2003-06-22 03:48  maan
-
-       * icc_dbtool.c: simplify fut since ~/.icc/streams contains full
-         query now
-
-2003-06-22 03:47  maan
-
-       * icc.h: add mmd.score
-
-2003-06-19 17:18  maan
-
-       * icc_bash_completion: add ca (copy attributes)
-
-2003-06-19 17:12  maan
-
-       * icc_dbtool.c: add ca (copy attributes) command
-
-2003-06-15 20:44  maan
-
-       * sdl_gui.c: major cleanup, speed improvements and debugging
-
-2003-06-15 19:16  maan
-
-       * command.c: only send full volatile status when it has changed
-
-2003-06-15 19:02  maan
-
-       * command.c: only send full status when song has changed
-
-2003-06-15 02:42  maan
-
-       * icc_dbtool.c: com_us: use basename of given argument
-
-2003-06-15 01:47  maan
-
-       * icc_dbtool.c: fix do_update_song (introduce get_numplayed)
-
-2003-06-02 00:54  maan
-
-       * icc_dbtool.c: small cleanups
-
-2003-06-02 00:01  maan
-
-       * afs.c: flush id3 tags before updating
-
-2003-06-01 23:45  maan
-
-       * icc_dbtool.c: us: increase Numplayed, info: print Numplayed,
-         get_a: fix off by one bug
-
-2003-06-01 23:06  maan
-
-       * icc_dbtool.c: get_aa: skip first _three_ rows, since third row is
-         now reserved for number of times song has been played
-
-2003-06-01 22:43  maan
-
-       * icc_dbtool.c: return is not a function!
-
-2003-06-01 22:42  maan
-
-       * icc_dbtool.c: fix return value in get_a
-
-2003-05-18 18:33  maan
-
-       * sdl_gui.c: make non-interactive mode working. Some cleanups
-
-2003-05-18 04:11  maan
-
-       * sdl_gui.c: add command line options -w -h -f -i
-
-2003-05-05 01:47  maan
-
-       * VERSION: bump version number to 94
-
-2003-05-05 01:46  maan
-
-       * Makefile: start tag name with letter to make cvs happy
-
-2003-05-05 01:45  maan
-
-       * VERSION: decrease version to 93, since make version failed
-
-2003-05-05 01:45  maan
-
-       * Makefile: use backtick expansion rather than double dollars
-
-2003-05-05 01:42  maan
-
-       * VERSION: bump version number to 94
-
-2003-05-05 01:42  maan
-
-       * VERSION: decrease version to 93, since make version failed
-
-2003-05-05 01:41  maan
-
-       * Makefile: typo that caused cvs -q tag to fail
-
-2003-05-05 01:37  maan
-
-       * VERSION: bump version number to 94
-
-2003-05-04 00:24  maan
-
-       * icc_server.c: loglevel adjustments
-
-2003-05-04 00:09  maan
-
-       * icc_server.c: minor auth cleanup
-
-2003-05-03 23:21  maan
-
-       * icc_server.c: reduce # arguments for handle_sighup
-
-2003-05-03 21:28  maan
-
-       * icc_server.c: avoid use of TRUE and FALSE (tele-tubby)
-
-2003-05-03 21:10  maan
-
-       * icc_server.c: init mmd->num_played, mmd->num_commands and
-         mmd->stream_name early
-
-2003-05-03 21:08  maan
-
-       * icc.h: reduce number of arguments to init_afs
-
-2003-05-03 21:08  maan
-
-       * init_afs.c: reduce number of arguments to init_afs, move init of
-         some sane values of mmd where they belong, comment out silly
-         PETER_B log messages
-
-2003-05-03 20:44  maan
-
-       * icc.h: add prototype for afs_status_tohuman
-
-2003-05-03 20:44  maan
-
-       * command.c: don't duplicate afs_status_tohuman, use it
-
-2003-05-03 20:30  maan
-
-       * afs.c: insert missing function descriptors
-
-2003-05-03 20:30  maan
-
-       * icc.h: init afs returns pid_t
-
-2003-05-03 20:26  maan
-
-       * icc_server.c: fix missing exit for unknown commands. Strange...
-
-2003-05-03 20:13  maan
-
-       * init_afs.c: init afs returns pid_t
-
-2003-05-03 20:11  maan
-
-       * icc.h, icc_server.c, init_afs.c: move definition of conn to
-         init_afs
-
-2003-05-03 20:01  maan
-
-       * sdl_gui.c: unicolor, proposed by christian
-
-2003-05-03 19:58  maan
-
-       * icc_server.c: first bind socket, then fork. Avoids log flodding
-         if second instance of icc_server is started
-
-2003-05-03 19:51  maan
-
-       * icc_server.c: further cosmetics
-
-2003-05-03 19:34  maan
-
-       * icc_server.c: cosmetics
-
-2003-05-03 19:30  maan
-
-       * icc_server.c: kill dead code [send(...)]
-
-2003-05-03 19:13  maan
-
-       * icc_server.c: fix nasty bug introduced by use of send_buffer:
-         strlen(buf) might not give correct length in case of encrypted
-         data
-
-2003-05-03 00:47  maan
-
-       * icc_server.c: further small cleanups
-
-2003-05-03 00:34  maan
-
-       * icc.h: add prototype for send_buffer
-
-2003-05-03 00:23  maan
-
-       * icc_server.c: cosmetics
-
-2003-05-03 00:14  maan
-
-       * icc_server.c: use send_buffer() rather than send()
-
-2003-05-02 23:53  maan
-
-       * afs.c: compute_sb_string: use basename instead of weird strchr()
-         madness
-
-2003-05-02 23:22  maan
-
-       * afs.c: replace cleanexit by clean_n_jump
-
-2003-05-02 23:02  maan
-
-       * command.c: stat: output two digits for seconds
-
-2003-05-02 23:01  maan
-
-       * icc.h: replace BUFFSIZE by CHUNK_BUFFSIZE
-
-2003-04-30 00:39  maan
-
-       * afs.c: update meta data stream regularly (adjustable in icc.h)
-
-2003-04-29 22:54  maan
-
-       * icc_dbtool.c: Comment out noisy output in upd
-
-2003-04-20 17:40  maan
-
-       * README: nuke php description, add sdl_gui description
-
-2003-04-20 17:31  maan
-
-       * sdl_gui.c: add documentation
-
-2003-04-20 17:16  maan
-
-       * sdl_gui.c: add gpl header
-
-2003-04-20 17:14  maan
-
-       * sdl_gui.c: cut length of output lines if too long
-
-2003-04-20 16:49  maan
-
-       * afs.c, command.c, icc.h, icc_gui.c, sdl_gui.c: replace mmd.misc
-         by mmd.dbinfo
-
-2003-04-20 16:30  maan
-
-       * afs.c: enumerate Â«misc:» lines for status output
-
-2003-04-20 16:28  maan
-
-       * Makefile: also install icc_sdl_gui
-
-2003-04-20 15:57  maan
-
-       * SFont.c: needed by sdl_gui
-
-2003-04-20 15:56  maan
-
-       * sdl_gui.c: The gui of death that makes you blind
-
-2003-04-20 15:55  maan
-
-       * command.c: status: print id tags
-
-2003-04-20 15:55  maan
-
-       * afs.c: cleanups
-
-2003-04-20 15:53  maan
-
-       * Makefile: new target: sdl_gui
-
-2003-04-04 02:45  maan
-
-       * icc_gui.c: show also mp3tags
-
-2003-04-04 02:25  maan
-
-       * command.c: stat: print also id3 tags
-
-2003-04-04 02:12  maan
-
-       * Makefile, afs.c, command.c, icc.h: include mp3info struct in mmd
-         and remove duplication of information in mmd
-
-2003-04-04 01:19  maan
-
-       * mp3tech.c, mp3tech.h, mp3info.h, textfunc.h: taken from mp3info
-         package
-
-2003-04-04 01:18  maan
-
-       * afs.c: Use mp3tech instead of mp3info
-
-2003-04-04 01:18  maan
-
-       * Makefile: include mp3tech
-
-2003-04-03 04:20  maan
-
-       * icc_gui.c: add c (change stream) to status bar
-
-2003-04-03 03:53  maan
-
-       * icc_dbtool.c: further cosmetics
-
-2003-04-02 22:18  maan
-
-       * icc_dbtool.c: cosmetics
-
-2003-04-02 03:19  maan
-
-       * icc_dbtool.template: Omit streams in help's output
-
-2003-04-02 03:07  maan
-
-       * README.dbtool: Improve(?) Real life eample
-
-2003-04-02 02:40  maan
-
-       * icc_server.conf.sample: use 127.0.0.1 as default icecast ip
-
-2003-04-02 02:33  maan
-
-       * icc_dbtool.template: add nec. streams command and some more
-         comments
-
-2003-03-29 18:22  maan
-
-       * command.c, icc_gui.c: new command: ns (next stream)
-
-2003-03-29 17:37  maan
-
-       * command.c, icc_dbtool.c, icc_gui.c: info: print default text if
-         no attributes set or lastplayed=NULL
-
-2003-03-29 02:12  maan
-
-       * INSTALL, README: linguistic improvements (I hope)
-
-2003-03-29 00:43  maan
-
-       * VERSION: bump version number to 93
-
-2003-03-29 00:43  maan
-
-       * Makefile: add xmms-patch to sources
-
-2003-03-29 00:40  maan
-
-       * xmms-1.2.7.titlebar_vbr_patch: patch for xmms-1.2.7 to fix title
-         display for mp3s with vbr
-
-2003-03-29 00:32  maan
-
-       * afs.c, command.c: nicify afs status output
-
-2003-03-28 05:18  maan
-
-       * icc_gui.c: statusbar improvements
-
-2003-03-28 05:05  maan
-
-       * icc_gui.c: major optic overhowl. I like it colored
-
-2003-03-28 00:06  maan
-
-       * icc_gui.c: print also current stream
-
-2003-03-28 00:05  maan
-
-       * command.c: stat: print name of current stream
-
-2003-03-28 00:04  maan
-
-       * afs.c: cleanups
-
-2003-03-18 02:29  maan
-
-       * icc_gui.c: use linux/soundcard.h to set volume directly
-
-2003-03-17 22:29  maan
-
-       * icc_gui.c: cosmetics
-
-2003-03-17 22:22  maan
-
-       * icc_gui.c: additional delay for command '-'
-
-2003-03-17 22:18  maan
-
-       * init_afs.c: loglevel adjustments
-
-2003-03-17 22:17  maan
-
-       * icc_server.c: log: print server if mmd not yet defined
-
-2003-03-17 05:10  maan
-
-       * afs.c, icc_server.c: adjust some loglevels
-
-2003-03-17 02:16  maan
-
-       * command.c, icc.h, icc_server.c, init_afs.c: use semaphor-locking
-         to serialize access to server-afs pipes
-
-2003-03-17 01:19  maan
-
-       * init_afs.c: kill processgroup if afs dies
-
-2003-03-12 22:22  maan
-
-       * icc.h: add prototypes for pid_list_lock, pid_list_unlock
-
-2003-03-12 22:21  maan
-
-       * command.c: uptime: lock pid_list before accessing
-
-2003-03-12 21:51  maan
-
-       * icc_server.c: add semaphor locking for pid_list. Fix several bugs
-         causing corrupt pid_list
-
-2003-03-12 21:50  maan
-
-       * icc.h: add server and afs pids to mmd struct, split macro LICENSE
-
-2003-03-12 21:48  maan
-
-       * command.c: fix typo
-
-2003-03-11 00:43  maan
-
-       * afs.c, command.c, icc_server.c: forgotten cleanups
-
-2003-02-23 18:06  maan
-
-       * icc.h, icc_server.c: new loglevel: VERBOSE
-
-2003-02-23 16:54  maan
-
-       * icc_server.c: fix permission bug (commands were executed even if
-         permissions are not suff.)
-
-2003-02-23 16:46  maan
-
-       * Makefile: Change icebear.c and init_icebear.c to afs
-
-2003-02-23 16:45  maan
-
-       * init_afs.c: new, former init_icebear.c
-
-2003-02-23 16:44  maan
-
-       * init_icebear.c: renamed to init_afs.c
-
-2003-02-23 16:40  maan
-
-       * afs.c: new file, former icebear.c
-
-2003-02-23 16:39  maan
-
-       * icebear.c: will be renamed to afs.c
-
-2003-02-23 16:35  maan
-
-       * command.c, icc.h, icc_dbtool.c, icc_server.c, icebear.c: change
-         rest of occurences of icebear to afs
-
-2003-02-23 16:18  maan
-
-       * icc.h, icebear.c, init_icebear.c: change icebear function to afs
-
-2003-02-23 16:12  maan
-
-       * command.c, icc.h, icc_server.c, icebear.c, init_icebear.c: change
-         icebear_init to afs_init
-
-2003-02-23 15:57  maan
-
-       * INSTALL, command.c, icc.h, icc_server.c, icc_server.conf.sample,
-         icebear.c: change ICEBEAR to AFS
-
-2003-02-23 15:47  maan
-
-       * icc_dbtool.c, icebear.c: cosmetics
-
-2003-02-23 03:31  maan
-
-       * command.c: cosmetics
-
-2003-02-23 03:20  maan
-
-       * command.c, icc.h: change ICEBEAR_HANDLER to AFS_HANDLER
-
-2003-02-23 03:08  maan
-
-       * icc_server.c: cosmetics
-
-2003-02-23 03:00  maan
-
-       * command.c, icc.h, icc_server.c: replace icc_server: by server:
-
-2003-02-23 02:45  maan
-
-       * icebear.c: comment out unneeded signal handler code
-
-2003-02-23 02:17  maan
-
-       * icc_server.c: Fix bug: become deamon *before* we record our pid.
-
-2003-02-23 02:07  maan
-
-       * icc_server.c: make version the first line that is printed out
-
-2003-02-23 01:05  maan
-
-       * icebear.c: logical simplifications
-
-2003-02-23 00:34  maan
-
-       * icebear.c: new function: send_chunk. Many cleanups
-
-2003-02-22 23:01  maan
-
-       * icebear.c: new functions: get_song and compute_sb_string
-
-2003-02-22 22:51  maan
-
-       * icebear.c: new functions: get_song and compute_sb_string
-
-2003-02-22 21:56  maan
-
-       * icebear.c: cosmetics
-
-2003-02-22 21:46  maan
-
-       * icebear.c: only send metadata stream once
-
-2003-02-22 21:36  maan
-
-       * icebear.c: clear buffer before reading
-
-2003-02-18 01:48  maan
-
-       * icc_gui.c: thinko: add break statements
-
-2003-02-18 01:30  maan
-
-       * command.c: add comment
-
-2003-02-18 01:29  maan
-
-       * icc_gui.c: use case instead of ifs
-
-2003-02-18 00:34  maan
-
-       * command.c: comment out obsolete check for icebear_fd
-
-2003-02-18 00:32  maan
-
-       * icc_server.c: log pid of current process
-
-2003-02-17 23:43  maan
-
-       * init_icebear.c: cosmetics
-
-2003-02-17 23:30  maan
-
-       * icc_server.c: cosmetics
-
-2003-02-17 23:29  maan
-
-       * icc.h: add struct pid_list and macro FOR_EACH_PROC from server
-
-2003-02-17 23:27  maan
-
-       * command.c: uptime: add pid of chilren in output
-
-2003-02-17 03:47  maan
-
-       * icc_server.c: sigchd handler: Wait not only for one child. There
-         may be more...
-
-2003-02-17 02:36  maan
-
-       * icebear.c: make it terminate if read fails and ppid == 1
-
-2003-02-11 03:04  maan
-
-       * icc_gui.c: major cleanup. Wow, is that crappy
-
-2003-02-11 00:35  maan
-
-       * command.c, icc.h, icc_server.c, icebear.c: implement pid list to
-         keep track of children. Needed for hup since kill(0,sighup)
-         doesnt work in daemon mode
-
-2003-02-10 01:53  maan
-
-       * icc_server.c: cosmetics
-
-2003-02-10 01:51  maan
-
-       * icc_dbtool.template: add -print to find command, documentation
-         update
-
-2003-02-10 01:50  maan
-
-       * icc_dbtool.c: remove obsolete ass command
-
-2003-02-10 00:55  maan
-
-       * command.c, icc_server.c: new function send_buffer
-
-2003-02-10 00:04  maan
-
-       * command.c: new command: hup
-
-2003-02-09 23:33  maan
-
-       * icc.h, icc_server.c, icebear.c: handle sighup correctly (all
-         children have to reload log file)
-
-2003-02-09 18:30  maan
-
-       * icc.h, icc_server.c, icebear.c, init_icebear.c: reduce use of
-         global variables. Extend mmd and put info there instead
-
-2003-02-09 15:58  maan
-
-       * icc_server.c: print plural form correctly in uptime
-
-2003-02-09 15:46  maan
-
-       * icc_server.c: cosmetics
-
-2003-02-09 01:55  maan
-
-       * command.c: further cosmetics
-
-2003-02-09 01:18  maan
-
-       * command.c: cosmetics
-
-2003-02-08 23:16  maan
-
-       * icc_client.c: cosmetics
-
-2003-01-11 16:30  maan
-
-       * VERSION, icc_dbtool.c, icc_gui.c, icebear.c, init_icebear.c:
-         change 2002 to 2003
-
-2003-01-11 16:28  maan
-
-       * icc_client.c, icc_server.c: cleanup. use new macros LICENSE and
-         COPYRIGHT
-
-2003-01-11 16:27  maan
-
-       * icc.h: add macros COPYRIGHT, LICENSE
-
-2003-01-11 16:26  maan
-
-       * command.c: minor cleanups
-
-2003-01-11 16:24  maan
-
-       * README.dbtool: fix some misprints
-
-2003-01-11 16:23  maan
-
-       * README:
-         Minor clarifications
-
-2002-12-23 22:26  maan
-
-       * icebear.c: cosmetic cleanup
-
-2002-12-23 22:20  maan
-
-       * icc_server.c: cosmetic cleanups
-
-2002-12-23 22:15  maan
-
-       * icebear.c: set icc version in init_shout
-
-2002-12-15 23:11  maan
-
-       * Makefile: commit change of version number on target version
-         before calling cvs tag
-
-2002-12-15 23:04  maan
-
-       * Makefile: version now implies distclean
-
-2002-12-15 22:56  maan
-
-       * Makefile: enable cvs tagging
-
-2002-12-15 22:53  maan
-
-       * Makefile: prepare for cvs tagging
-
-2002-12-15 22:49  maan
-
-       * Makefile: fix version (no more tdl), new target: update
-
-2002-12-15 22:37  maan
-
-       * Makefile: tgz no longer implies version
-
-2002-12-15 21:20  maan
-
-       * icebear.c: minor cleanups
-
-2002-12-15 21:04  maan
-
-       * icc_server.c: remove unneeded comments
-
-2002-12-15 20:55  maan
-
-       * icc_server.c, icebear.c: move (parts of) initialization of conn
-         fromicebear to server
-
-2002-12-15 20:11  maan
-
-       * icc_server.c: minor code cleanup,  add documentation bits
-
-2002-12-15 18:05  maan
-
-       * INSTALL, icebear.c: icc_dbtool is no longer a script. Fix
-         accordingly
-
-2002-12-15 14:28  maan
-
-       * icc_server.c: cosmetic cleanup
-
-2002-12-14 21:51  maan
-
-       * icc.h, icebear.c: use new mmd.mdst (meta data stream text) to
-         display meta data
-
-2002-12-14 02:51  maan
-
-       * icebear.c: new function: update_mmd
-
-2002-12-14 02:02  maan
-
-       * 1.0, icebear.c: preparation for movement of large parts in
-         icebear to extra function
-
-2002-12-14 01:03  maan
-
-       * icebear.c: minor code cleanup
-
-2002-12-14 00:10  maan
-
-       * icebear.c: use icecast meta data streaming
-
-2002-12-13 22:53  maan
-
-       * icc_dbtool.c: remove #include <libgen.h>
-
-2002-12-13 22:50  maan
-
-       * icc.h: add documentation
-
-2002-12-12 23:23  maan
-
-       * icc_client.c: better ERROR logging
-
-2002-12-08 03:11  maan
-
-       * command.c: code cleanup
-
-2002-12-08 02:54  maan
-
-       * command.c: fix cs in case invalid stream name is given
-
-2002-12-01 01:47  maan
-
-       * icc_dbtool.c: unlink tempfile _after_ closing
-
-2002-12-01 00:55  maan
-
-       * command.c, icc.h, icc_client.c, icc_dbtool.c, icc_gui.c,
-         icc_server.c, init_icebear.c: add Keyword Expansion
-
-2002-12-01 00:48  maan
-
-       * .changelog_before_cvs: self explanatory
-
-2002-12-01 00:46  maan
-
-       * Makefile: New Changelog Format with cvs2cl
-
-2002-12-01 00:19  maan
-
-       * icebear.c: add Keyword Expansion
-
-2002-12-01 00:09  maan
-
-       * .tdldb, 1.0, COPYING, GPL, INSTALL, Makefile, README,
-         README.dbtool, VERSION, command.c, icc.h, icc_bash_completion,
-         icc_client.c, icc_client.conf.sample, icc_dbtool.c,
-         icc_dbtool.conf.sample, icc_dbtool.template, icc_gui.c,
-         icc_server.c, icc_server.conf.sample, icebear.c,
-         icecast.conf.sample, init_icebear.c: Initial revision
-
-2002-12-01 00:09  maan
-
-       * .tdldb, 1.0, COPYING, GPL, INSTALL, Makefile, README,
-         README.dbtool, VERSION, command.c, icc.h, icc_bash_completion,
-         icc_client.c, icc_client.conf.sample, icc_dbtool.c,
-         icc_dbtool.conf.sample, icc_dbtool.template, icc_gui.c,
-         icc_server.c, icc_server.conf.sample, icebear.c,
-         icecast.conf.sample, init_icebear.c: initial import into cvs
-
index 9091a9c28965ffa90a4ea588f1a767b05831eb5c..2889d20d852cf91b01aa442021562718e496f031 100644 (file)
@@ -14,15 +14,14 @@ config.log
 config.status
 Makefile
 TODO
-*_command_list.h
-*_command_list.man
-paraslash-git.tar.bz2
-skencil/overview.pdf
+paraslash-*.tar.bz2
+web/dia/overview.pdf
 *.swp
+*.rej
+*~
 error2.h
 web_sync
 confdefs.h
 conftest
 conftest.c
 git-version.h
-*_completion.h
index 04d8f32621edd1426e2651aab711dd11516d9080..50c74695da5e8c690127511a7b21958972d721e0 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
-# Doxyfile 1.6.3
+# Doxyfile 1.8.6
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
 # The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
 
 #---------------------------------------------------------------------------
 # Project related configuration options
 #---------------------------------------------------------------------------
 
 # This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
 
 PROJECT_NAME           = paraslash
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
 
 PROJECT_NUMBER         =
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
 
 OUTPUT_DIRECTORY       = web_sync/doxygen
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
 
 CREATE_SUBDIRS         = NO
 
 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
 # documentation generated by doxygen is written. Doxygen will use this
 # information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
+# The default value is: YES.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
 
 ABBREVIATE_BRIEF       =
 
 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# doxygen will generate a detailed section even if there is only a brief
 # description.
+# The default value is: NO.
 
 ALWAYS_DETAILED_SEC    = NO
 
@@ -98,152 +123,204 @@ ALWAYS_DETAILED_SEC    = NO
 # inherited members of a class in the documentation of that class as if those
 # members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
+# The default value is: NO.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
 
 FULL_PATH_NAMES        = YES
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
 STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
 
 STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
 
 JAVADOC_AUTOBRIEF      = YES
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
 
 TAB_SIZE               = 8
 
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
 
 ALIASES                =
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
-# Doxygen selects the parser to use depending on the extension of the files it parses.
-# With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this tag.
-# The format is ext=language, where ext is a file extension, and language is one of
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
 
 EXTENSION_MAPPING      =
 
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
 
 BUILTIN_STL_SUPPORT    = NO
 
 # If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
+# The default value is: NO.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
 
 IDL_PROPERTY_SUPPORT   = YES
 
@@ -251,1004 +328,1558 @@ IDL_PROPERTY_SUPPORT   = YES
 # tag is set to YES, then doxygen will reuse the documentation of the first
 # member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
+# The default value is: NO.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
 
 SUBGROUPING            = YES
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
 # with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
 # types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
 
 TYPEDEF_HIDES_STRUCT   = NO
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE      = 0
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
 
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
 
 EXTRACT_STATIC         = NO
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
 
 EXTRACT_LOCAL_CLASSES  = NO
 
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
 
 EXTRACT_LOCAL_METHODS  = NO
 
 # If this flag is set to YES, the members of anonymous namespaces will be
 # extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
 
 EXTRACT_ANON_NSPACES   = NO
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
 # allowed. This is useful if you have classes or files whose names only differ
 # in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
 
 HIDE_SCOPE_NAMES       = YES
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
 
 FORCE_LOCAL_INCLUDES   = NO
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
 
 SORT_MEMBER_DOCS       = NO
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
 
 SORT_BRIEF_DOCS        = NO
 
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
 
 SORT_MEMBERS_CTORS_1ST = NO
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
 
 SORT_GROUP_NAMES       = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
 
 ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
 
 MAX_INITIALIZER_LINES  = 30
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
 
 SHOW_NAMESPACES        = YES
 
 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
 # doxygen should invoke to get the current version for each file (typically from
 # the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
 
 FILE_VERSION_FILTER    =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
-# doxygen. The layout file controls the global structure of the generated output files
-# in an output format independent way. The create the layout file that represents
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name
-# of the layout file.
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
 
 LAYOUT_FILE            =
 
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
 #---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
 
 QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
 
 WARN_NO_PARAMDOC       = YES
 
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
 
 WARN_FORMAT            = "$file:$line: $text "
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
 
 WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
 
 INPUT                  = .
 
 # This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
 
 INPUT_ENCODING         = UTF-8
 
 # If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
 
 FILE_PATTERNS          = *.c \
                          *.h
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
 
 RECURSIVE              = NO
 
-# The EXCLUDE tag can be used to specify files and/or directories that should
+# The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
 EXCLUDE                = error2.h
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
+# The default value is: NO.
 
 EXCLUDE_SYMLINKS       = NO
 
 # If the value of the INPUT tag contains directories, you can use the
 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
 
 EXCLUDE_PATTERNS       = *.cmdline.* \
-                         gui* \
                          gcc-compat.h \
                          fade.c \
-                         config.h \
-                       *_command_list.h \
-                       *_completion.h
+                         *.command_list.h \
+                         *.completion.h
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
 # output. The symbol name can be a fully qualified name, a word, or if the
 # wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
 
 EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
 
 EXAMPLE_PATH           =
 
 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
 
 EXAMPLE_PATTERNS       =
 
 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
 
 IMAGE_PATH             =
 
 # The INPUT_FILTER tag can be used to specify a program that doxygen should
 # invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
 
 INPUT_FILTER           =
 
 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
 
 FILTER_PATTERNS        =
 
 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
 
 FILTER_SOURCE_FILES    = NO
 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
 #---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
 
 SOURCE_BROWSER         = YES
 
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
 
 STRIP_CODE_COMMENTS    = YES
 
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
 
 REFERENCED_BY_RELATION = YES
 
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
 
 REFERENCES_RELATION    = YES
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
 
 USE_HTAGS              = YES
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
 
 VERBATIM_HEADERS       = YES
 
 #---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
 
 ALPHABETICAL_INDEX     = NO
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 COLS_IN_ALPHA_INDEX    = 5
 
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_OUTPUT            = html
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
 # standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_HEADER            = web/header2.html
 
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FOOTER            = web/footer.html
 
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_STYLESHEET        =
 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
 # written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
-# are set, an additional index file will be generated that can be used as input for
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
-# HTML documentation.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_NAMESPACE          = org.doxygen.Project
 
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
-# For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_CUST_FILTER_NAME   =
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_CUST_FILTER_ATTRS  =
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_SECT_FILTER_ATTRS  =
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHG_LOCATION           =
 
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-#  will be generated, which together with the HTML files, form an Eclipse help
-#  plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_ECLIPSEHELP   = NO
 
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 DISABLE_INDEX          = NO
 
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_TREEVIEW      = NO
 
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-USE_INLINE_TREES       = NO
+ENUM_VALUES_PER_LINE   = 4
 
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 TREEVIEW_WIDTH         = 250
 
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-FORMULA_FONTSIZE       = 10
+EXT_LINKS_IN_WINDOW    = NO
 
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-SEARCHENGINE           = YES
+FORMULA_FONTSIZE       = 10
 
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
 SERVER_BASED_SEARCH    = NO
 
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
 #---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_OUTPUT           = latex
 
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PAPER_TYPE             = a4wide
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HEADER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PDF_HYPERLINKS         = NO
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
 # higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 USE_PDFLATEX           = NO
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_SOURCE_CODE      = NO
 
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
 #---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_LINKS              = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_SCHEMA             =
 
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_DTD                =
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_PROGRAMLISTING     = YES
 
 #---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options related to the DOCBOOK output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_AUTOGEN_DEF   = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_MAKEVAR_PREFIX =
 
@@ -1256,116 +1887,129 @@ PERLMOD_MAKEVAR_PREFIX =
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
 
 ENABLE_PREPROCESSING   = YES
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-MACRO_EXPANSION        = NO
+MACRO_EXPANSION        = YES
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SEARCH_INCLUDES        = YES
 
 # The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
 INCLUDE_PATH           =
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 INCLUDE_FILE_PATTERNS  =
 
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             = HAVE_MAD \
-                         HAVE_FAAD \
-                         HAVE_OGGVORBIS \
-                         __GNUC__=4 \
-                         __GNUC_MINOR__=4 \
-                         HAVE_UCRED \
-                        HAVE_CLOCK_GETTIME
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = __GNUC__=4 \
+                         __GNUC_MINOR__=4
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SKIP_FUNCTION_MACROS   = YES
 
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
 # TAGFILES = file1 file2 ...
 # Adding location for the tag files is done as follows:
-#
 # TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
 
 TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
 
 GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
 
 EXTERNAL_GROUPS        = YES
 
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
 # The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
 
 PERL_PATH              = /usr/bin/perl
 
@@ -1373,192 +2017,293 @@ PERL_PATH              = /usr/bin/perl
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
 # powerful graphs.
+# The default value is: YES.
 
 CLASS_DIAGRAMS         = YES
 
 # You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
 # the mscgen tool resides. If left empty the tool is assumed to be found in the
 # default search path.
 
 MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
 
 HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
 
 HAVE_DOT               = NO
 
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
 
-DOT_FONTNAME           = FreeSans
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+DOT_FONTNAME           =
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GROUP_GRAPHS           = YES
 
 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
 # collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 UML_LOOK               = NO
 
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALL_GRAPH             = NO
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DIRECTORY_GRAPH        = YES
 
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_IMAGE_FORMAT       = png
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_PATH               =
 
 # The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOTFILE_DIRS           =
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_GRAPH_MAX_NODES    = 50
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_TRANSPARENT        = NO
 
 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
 # files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_MULTI_TARGETS      = NO
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_CLEANUP            = YES
diff --git a/FEATURES b/FEATURES
deleted file mode 100644 (file)
index 6ad420d..0000000
--- a/FEATURES
+++ /dev/null
@@ -1,30 +0,0 @@
-Features
-========
-
-------------------------------------------------------------------------------
-
-       * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other
-         Unixes
-       * Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma and flac support
-       * Native Alsa, OSS, CoreAudio output support
-       * Support for ESD, Pulseaudio, AIX, Solaris, IRIX through libao
-       * Local or remote http, dccp and udp network audio streaming
-       * IPv6 support
-       * Forward error correction allows receivers to recover from packet losses
-       * Volume normalizer
-       * Stream grabbing at any point in the filter chain
-       * Stand-alone command line receiver/decoder/normalizer/player
-       * Stand-alone audio format handler utility
-       * Sophisticated audio file selector
-       * Small memory footprint
-       * Command line interface for easy scripting in high-level languages
-       * Interactive sessions offer command completion and command line history
-       * RSA user authentication
-       * Encrypted communications
-       * GPL licensed
-       * Written in C
-       * Well documented
-       * Complete API-Reference
-       * Easily extendible due to its modular design
-       * Curses-based gui
-       * Volume fader and alarm clock
index 32802232244a6a79258f2640e7e0f7e3ddd950a2..b8abff355e071af95ae8fc7aafd575e131495438 100755 (executable)
@@ -8,7 +8,7 @@ LF='
 '
 
 # First see if there is a version file (included in release tarballs),
-# then try git-describe, then default.
+# then try git-describe, then gitweb, then default.
 if [[ -f VERSION ]]; then
        VN=$(cat VERSION) || VN="$DEF_VER"
 elif [[ -d .git || -f .git ]] &&
@@ -16,12 +16,14 @@ elif [[ -d .git || -f .git ]] &&
        case "$VN" in
        *$LF*) (exit 1) ;;
        v[0-9]*)
-               git update-index -q --refresh
+               git update-index -q --refresh &>/dev/null
                test -z "$(git diff-index --name-only HEAD --)" ||
                VN="$VN-dirty" ;;
        esac
 then
        VN=$(echo "$VN" | sed -e 's/-/./g');
+elif [[ "$PWD" =~ paraslash-[a-f0-9]{1,} ]]; then
+       VN="${PWD#*-}"
 else
        VN="$DEF_VER"
 fi
diff --git a/INSTALL b/INSTALL
index 0f4c18b647e0e6c2fc921bbded745275458d70c7..4cc4b1feb5c882f069f09f9151df8a693336b15f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,13 +1,38 @@
 Any knowledge of how to work with mouse and icons is not required.
 
-From tarball:
+Installing osl
+~~~~~~~~~~~~~~
+       git clone git://git.tuebingen.mpg.de/osl
+       cd osl && make && sudo make install
+       (see http://people.tuebingen.mpg.de/maan/osl/)
 
+Installing paraslash from tarball
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        ./configure && make && sudo make install
 
-From git:
+Installing paraslash from git
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+       autoconf && autoheader && make && sudo make install
 
-       ./autogen.sh && sudo make install
+Example for cross-compiling
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+       export CROSS_COMPILE='armv6j-hardfloat-linux-gnueabi-'
+       export PATH="/usr/cross/arm/bin:$PATH"
+       export CC=${CROSS_COMPILE}gcc
+
+       export LDFLAGS='
+               -L/usr/sysroot/arm/lib
+               -L/usr/sysroot/arm/usr/lib
+               -L/usr/sysroot/arm/usr/lib/glibc/lib
+               -L/usr/sysroot/arm/usr/local/ssl/lib
+       '
+       autoconf
+       autoheader
+       ./configure --host=arm-linux-gnueabi --prefix /usr/sysroot/arm/usr/local
+       make CROSS_COMPILE=$CROSS_COMPILE
 
 For details see the user manual:
 
+       http://people.tuebingen.mpg.de/maan/paraslash/manual.html
+or
        http://paraslash.systemlinux.org/manual.html
index f82afd7b88f65a9ad2d75032ca401d0445025272..a8e2a8b90a400b08a36edeca459335ef766ba560 100644 (file)
 prefix := @prefix@
 exec_prefix := @exec_prefix@
 
-BINDIR := @bindir@
-VARDIR := /var/paraslash
-PKGDATADIR := @datarootdir@/@PACKAGE_NAME@
-MANDIR := @datarootdir@/man/man1
-PACKAGE_VERSION := @PACKAGE_VERSION@
-PACKAGE_STRING := @PACKAGE_STRING@
-install_sh := @install_sh@
-executables := @executables@
-ggo_descriptions_declared := @ggo_descriptions_declared@
-
-GENGETOPT := @gengetopt@
-HELP2MAN := @help2man@
-MKDIR_P := mkdir -p
-
-build_date := $(shell date)
-uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
-uname_rs := $(shell uname -rs)
-cc_version := $(shell $(CC) --version | head -n 1)
-
-GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-
-m4_ggo_dir := m4/gengetopt
-test_dir := t
-ifeq ("$(origin O)", "command line")
-       build_dir := $(O)
-else
-       build_dir := build
-endif
-ggo_dir := $(build_dir)/ggo
-object_dir := $(build_dir)/objects
-dep_dir := $(build_dir)/deps
-man_dir := $(build_dir)/man/man1
-cmdline_dir := $(build_dir)/cmdline
-
-DEBUG_CPPFLAGS += -g -Wunused -Wundef -W
-DEBUG_CPPFLAGS += -Wredundant-decls
-DEBUG_CPPFLAGS += -Wall -Wno-sign-compare -Wno-unknown-pragmas
-DEBUG_CPPFLAGS += -Wformat-security
-DEBUG_CPPFLAGS += -Wmissing-format-attribute
-# produces false positives
-# DEBUG_CPPFLAGS += -Wunreachable-code
-# DEBUG_CPPFLAGS += -Wwrite-strings
-
-# invalid option for gcc-3.3.3
-# DEBUG_CPPFLAGS += -Wextra
-# DEBUG_CPPFLAGS += -Wold-style-definition
-# DEBUG_CPPFLAGS += -Wdeclaration-after-statement
-# DEBUG_CPPFLAGS += -Wsuggest-attribute=const
-
-# many warnings about trivial stuff
-# CPPFLAGS += -Wconversion
-
-ifeq ($(uname_s),Linux)
-       CPPFLAGS += -fdata-sections -ffunction-sections
-       LDFLAGS += -Wl,--gc-sections
-       CPPFLAGS += -Wstrict-prototypes
-       CPPFLAGS += -Wshadow
-       # causes warnings on *BSD for the feature test macros
-       CPPFLAGS += -Wunused-macros
-endif
-CPPFLAGS += -Os
-CPPFLAGS += -Wuninitialized
-CPPFLAGS += -Wchar-subscripts
-CPPFLAGS += -DBINDIR='"$(BINDIR)"'
-CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
-CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
-CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
-CPPFLAGS += -Werror-implicit-function-declaration
-CPPFLAGS += -Wmissing-noreturn
-CPPFLAGS += -Wbad-function-cast
-CPPFLAGS += -fno-strict-aliasing
-CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
-CPPFLAGS += @arch_cppflags@
-CPPFLAGS += -I/usr/local/include
-CPPFLAGS += -I$(cmdline_dir)
-CPPFLAGS += @osl_cppflags@
-
-LDFLAGS += @clock_gettime_ldflags@
-
-man_pages := $(patsubst %, $(man_dir)/%.1, @executables@)
-
-autocrap := config.h.in configure
-tarball_pfx := @PACKAGE_TARNAME@-$(PACKAGE_VERSION)
-tarball_delete := $(addprefix $(tarball_pfx)/,\
-       web .changelog_before_cvs .changelog_cvs .gitignore\
-       skencil)
-tarball := @PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2
-
-# To put more focus on warnings, be less verbose as default
-# Use 'make V=1' to see the full commands
-ifeq ("$(origin V)", "command line")
-       Q :=
-else
-       Q := @
-endif
-
-.PHONY: dep all clean distclean maintainer-clean install man tarball
-all: dep @executables@ $(man_pages)
-dep: $(deps)
-man: $(man_pages)
-tarball: $(tarball)
-
-$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir):
-       $(Q) $(MKDIR_P) $@
-
--include $(m4_ggo_dir)/makefile
-
-# When in doubt, use brute force (Ken Thompson)
-TOUPPER = \
-$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,\
-$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,\
-$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,\
-$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,\
-$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,\
-$(subst z,Z,$1))))))))))))))))))))))))))
-
-%_command_list.h: %.cmd %.c
-       @[ -z "$(Q)" ] || echo 'GEN $@'
-       $(Q) ./command_util.sh h < $< >$@
-%_command_list.man: %.cmd %.c
-       @[ -z "$(Q)" ] || echo 'GEN $@'
-       $(Q) ./command_util.sh man < $< > $@
-%_completion.h: %.cmd %.c
-       @[ -z "$(Q)" ] || echo 'GEN $@'
-       $(Q) ./command_util.sh compl $(strip $(call TOUPPER,$(*F)))_COMPLETERS \
-               $(strip $(call TOUPPER,$(*F)))_COMMANDS < $< > $@
-
-server_command_list.h server_command_list.man server_completion.h: command.c
-afs_command_list.h afs_command_list.man afs_completion.h: afs.c aft.c attribute.c
-audiod_command_list.h audiod_command_list.man audiod_completion.h: audiod_command.c
-
-server_command_lists_man = server_command_list.man afs_command_list.man
-$(man_dir)/para_server.1: para_server $(server_command_lists_man) | $(man_dir)
-       @[ -z "$(Q)" ] || echo 'MAN $<'
-       $(Q) opts="-h --detailed-help -N `for i in $(server_command_lists_man); do printf "%s\n" "-i $$i"; done`"; \
-       $(HELP2MAN) $$opts ./para_server > $@
-
-$(man_dir)/para_audiod.1: para_audiod audiod_command_list.man | $(man_dir)
-       @[ -z "$(Q)" ] || echo 'MAN $<'
-       $(Q) $(HELP2MAN) -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@
-
-$(man_dir)/para_play.1: para_play play_command_list.man | $(man_dir)
-       @[ -z "$(Q)" ] || echo 'MAN $<'
-       $(Q) $(HELP2MAN) -h --detailed-help -N -i play_command_list.man ./para_play > $@
-
-$(man_dir)/%.1: % | $(man_dir)
-       @[ -z "$(Q)" ] || echo 'MAN $<'
-       $(Q) $(HELP2MAN) -h --detailed-help -N ./$< > $@
-
-$(object_dir)/crypt.o: crypt.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @openssl_cppflags@ $<
-$(object_dir)/spx_common.o: spx_common.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
-
-$(object_dir)/spxdec_filter.o: spxdec_filter.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
-
-$(object_dir)/spx_afh.o: spx_afh.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
-
-$(object_dir)/oggdec_filter.o: oggdec_filter.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
-
-$(object_dir)/ogg_afh.o: ogg_afh.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
-
-$(object_dir)/ogg_afh_common.o: ogg_afh_common.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ogg_cppflags@ $<
+# These two use prefix and exec_prefix
+bindir := @bindir@
+datarootdir := @datarootdir@
 
-$(object_dir)/mp3dec_filter.o: mp3dec_filter.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $<
-
-$(object_dir)/compress_filter.o: compress_filter.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) -O3 $<
-
-$(object_dir)/aacdec_filter.o: aacdec_filter.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
-
-$(object_dir)/aac_common.o: aac_common.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
-
-$(object_dir)/aac_afh.o: aac_afh.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
-
-$(object_dir)/mp3_afh.o: mp3_afh.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @id3tag_cppflags@ $<
-
-$(object_dir)/gui%.o: gui%.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @curses_cppflags@ $<
-$(object_dir)/ao_write.o: ao_write.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ao_cppflags@ $<
-
-$(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c $(CPPFLAGS) -Wno-unused-function -o $@ $<
-
-$(object_dir)/%.o: %.c | $(object_dir)
-       @[ -z "$(Q)" ] || echo 'CC $<'
-       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) $<
-
-$(dep_dir)/%.cmdline.d: $(cmdline_dir)/%.cmdline.c | $(dep_dir)
-       @[ -z "$(Q)" ] || echo 'DEP $<'
-       $(Q) ./depend.sh $(dep_dir) $(object_dir) $(cmdline_dir) \
-               $(CPPFLAGS) $< > $@
-
-$(dep_dir)/%.d: %.c | $(dep_dir)
-       @[ -z "$(Q)" ] || echo 'DEP $<'
-       $(Q) ./depend.sh $(dep_dir) $(object_dir) $(cmdline_dir) \
-               $(CPPFLAGS) $< > $@
-
-all_objs := @recv_objs@ @filter_objs@ @client_objs@ @gui_objs@ \
-       @audiod_objs@ @audioc_objs@ @fade_objs@ @server_objs@ \
-       @write_objs@ @afh_objs@ @play_objs@
-deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d))
-
-recv_objs := $(addprefix $(object_dir)/, @recv_objs@)
-filter_objs := $(addprefix $(object_dir)/, @filter_objs@)
-client_objs := $(addprefix $(object_dir)/, @client_objs@)
-gui_objs := $(addprefix $(object_dir)/, @gui_objs@)
-audiod_objs := $(addprefix $(object_dir)/, @audiod_objs@)
-audioc_objs := $(addprefix $(object_dir)/, @audioc_objs@)
-fade_objs := $(addprefix $(object_dir)/, @fade_objs@)
-server_objs := $(addprefix $(object_dir)/, @server_objs@)
-write_objs := $(addprefix $(object_dir)/, @write_objs@)
-afh_objs := $(addprefix $(object_dir)/, @afh_objs@)
-play_objs := $(addprefix $(object_dir)/, @play_objs@)
-
-ifeq ($(findstring clean, $(MAKECMDGOALS)),)
--include $(deps)
-endif
-
-para_recv: $(recv_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) $(recv_objs) -o $@ @recv_ldflags@ $(LDFLAGS)
-
-para_filter: $(filter_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) $(filter_objs) -o $@ @filter_ldflags@ $(LDFLAGS)
-
-para_client: $(client_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(client_objs) @client_ldflags@ $(LDFLAGS)
-
-para_gui: $(gui_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(gui_objs) @gui_ldflags@ $(LDFLAGS)
-
-para_audiod: $(audiod_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(audiod_objs) @audiod_ldflags@ $(LDFLAGS)
-
-para_audioc: $(audioc_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(audioc_objs) @audioc_ldflags@ $(LDFLAGS)
-
-para_fade: $(fade_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(fade_objs) @fade_ldflags@ $(LDFLAGS)
-
-para_server: $(server_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(server_objs) @server_ldflags@ $(LDFLAGS)
-
-para_write: $(write_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(write_objs) @write_ldflags@ $(LDFLAGS)
-
-para_afh: $(afh_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(afh_objs) @afh_ldflags@ $(LDFLAGS)
-
-para_play: $(play_objs)
-       @[ -z "$(Q)" ] || echo 'LD $@'
-       $(Q) $(CC) -o $@ $(play_objs) @play_ldflags@ $(LDFLAGS)
-
-clean:
-       @[ -z "$(Q)" ] || echo 'CLEAN'
-       $(Q) rm -f @executables@
-       $(Q) rm -rf $(object_dir)
-
-clean2: clean
-       @[ -z "$(Q)" ] || echo 'CLEAN2'
-       $(Q) rm -f *_command_list.* *_completion.h
-       $(Q) rm -rf $(build_dir)
-distclean: clean2 test-clean
-       @[ -z "$(Q)" ] || echo 'DISTCLEAN'
-       $(Q) rm -f Makefile autoscan.log config.status config.log
-       $(Q) rm -rf autom4te.cache
-       $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
-
-maintainer-clean: distclean
-       rm -f *.tar.bz2 \
-               config.h configure \
-               config.h.in skencil/*.pdf skencil/*.ps
-       rm -rf web_sync
+PACKAGE_TARNAME := @PACKAGE_TARNAME@
+PACKAGE_VERSION := @PACKAGE_VERSION@
 
-install: all man
-       $(MKDIR_P) $(BINDIR) $(MANDIR)
-       $(install_sh) -s -m 755 @executables@ $(BINDIR)
-       $(install_sh) -m 644 $(man_pages) $(MANDIR)
-       $(MKDIR_P) $(VARDIR) >/dev/null 2>&1 || true # not fatal, so don't complain
+INSTALL := @INSTALL@
+GENGETOPT := @GENGETOPT@
+HELP2MAN := @HELP2MAN@
 
-$(tarball):
-       rm -rf $(tarball_pfx).tar.bz2 $(tarball_pfx)
-       git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
-               | tar --delete $(tarball_delete) > $(tarball_pfx).tar
-       $(MKDIR_P) $(tarball_pfx)
-       ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
-       cp -r $(autocrap) $(tarball_pfx)
-       tar rf $(tarball_pfx).tar $(tarball_pfx)/*
-       bzip2 -9 $(tarball_pfx).tar
-       ls -l $(tarball_pfx).tar.bz2
-       rm -rf $(tarball_pfx)
-%.ps: %.sk
-       sk2ps $< > $@
-%.pdf: %.ps
-       ps2pdf - - < $< > $@
+ggo_descriptions_declared := @ggo_descriptions_declared@
+object_executable_matrix := @object_executable_matrix@
 
-include $(test_dir)/makefile.test
+executables := @executables@
+receivers := @receivers@
+filters := @filters@
+writers := @writers@
+
+recv_objs := @recv_objs@
+filter_objs := @filter_objs@
+client_objs := @client_objs@
+gui_objs := @gui_objs@
+audiod_objs := @audiod_objs@
+audioc_objs := @audioc_objs@
+fade_objs := @fade_objs@
+server_objs := @server_objs@
+write_objs := @write_objs@
+afh_objs := @afh_objs@
+play_objs := @play_objs@
+
+speex_cppflags := @speex_cppflags@
+opus_cppflags := @opus_cppflags@
+vorbis_cppflags := @vorbis_cppflags@
+arch_cppflags := @arch_cppflags@
+osl_cppflags := @osl_cppflags@
+id3tag_cppflags := @id3tag_cppflags@
+openssl_cppflags := @openssl_cppflags@
+gcrypt_cppflags := @gcrypt_cppflags@
+ogg_cppflags := @ogg_cppflags@
+mad_cppflags := @mad_cppflags@
+faad_cppflags := @faad_cppflags@
+curses_cppflags := @curses_cppflags@
+pthread_cppflags := @pthread_cppflags@
+ao_cppflags := @ao_cppflags@
+flac_cppflags := @flac_cppflags@
+samplerate_cppflags := @samplerate_cppflags@
+readline_cppflags := @readline_cppflags@
+alsa_cppflags := @alsa_cppflags@
+oss_cppflags := @oss_cppflags@
+mp4v2_cppflags := @mp4v2_cppflags@
+
+clock_gettime_ldflags := @clock_gettime_ldflags@
+id3tag_ldflags := @id3tag_ldflags@
+ogg_ldflags := @ogg_ldflags@
+vorbis_ldflags := @vorbis_ldflags@
+speex_ldflags := @speex_ldflags@
+opus_ldflags := @opus_ldflags@
+faad_ldflags := @faad_ldflags@
+mad_ldflags := @mad_ldflags@
+flac_ldflags := @flac_ldflags@
+oss_ldflags := @oss_ldflags@
+alsa_ldflags := @alsa_ldflags@
+pthread_ldflags := @pthread_ldflags@
+ao_ldflags := @ao_ldflags@
+readline_ldflags := @readline_ldflags@
+samplerate_ldflags := @samplerate_ldflags@
+osl_ldflags := @osl_ldflags@
+socket_ldflags := @socket_ldflags@
+nsl_ldflags := @nsl_ldflags@
+curses_ldflags := @curses_ldflags@
+core_audio_ldflags := @core_audio_ldflags@
+crypto_ldflags := @crypto_ldflags@
+iconv_ldflags := @iconv_ldflags@
+mp4v2_ldflags := @mp4v2_ldflags@
+
+include Makefile.real
diff --git a/Makefile.real b/Makefile.real
new file mode 100644 (file)
index 0000000..289e027
--- /dev/null
@@ -0,0 +1,385 @@
+# Implicit rules are implemented in make as suffix rules. The following rule
+# empties the suffix list to disable the predefined implicit rules. This
+# increases performance and avoids hard-to-debug behaviour.
+.SUFFIXES:
+MAKEFLAGS += -Rr
+ifeq ("$(origin CC)", "default")
+        CC := cc
+endif
+
+vardir := /var/paraslash
+mandir := $(datarootdir)/man/man1
+STRIP := $(CROSS_COMPILE)strip
+HOSTCC ?= cc
+MKDIR_P := mkdir -p
+prefixed_executables := $(addprefix para_, $(executables))
+
+build_date := $(shell date)
+uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
+uname_rs := $(shell uname -rs)
+cc_version := $(shell $(CC) --version | head -n 1)
+GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
+build_date := $(shell date)
+
+ifeq ("$(origin O)", "command line")
+       build_dir := $(O)
+else
+       build_dir := build
+endif
+ggo_dir := $(build_dir)/ggo
+object_dir := $(build_dir)/objects
+dep_dir := $(build_dir)/deps
+man_dir := $(build_dir)/man/man1
+cmdline_dir := $(build_dir)/cmdline
+cmdlist_dir := $(build_dir)/cmdlist
+m4depdir := $(build_dir)/m4deps
+help2man_dir := $(build_dir)/help2man
+hostbin_dir := $(build_dir)/host/bin
+m4_ggo_dir := m4/gengetopt
+test_dir := t
+
+# sort removes duplicate words, which is all we need here
+all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \
+       $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \
+       $(write_objs) $(afh_objs) $(play_objs))
+deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d)))
+m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(executables)))
+
+# now prefix all objects with object dir
+recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
+filter_objs := $(addprefix $(object_dir)/, $(filter_objs))
+client_objs := $(addprefix $(object_dir)/, $(client_objs))
+gui_objs := $(addprefix $(object_dir)/, $(gui_objs))
+audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs))
+audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs))
+fade_objs := $(addprefix $(object_dir)/, $(fade_objs))
+server_objs := $(addprefix $(object_dir)/, $(server_objs))
+write_objs := $(addprefix $(object_dir)/, $(write_objs))
+afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
+play_objs := $(addprefix $(object_dir)/, $(play_objs))
+
+man_pages := $(patsubst %, $(man_dir)/%.1, $(prefixed_executables))
+
+autocrap := config.h.in configure
+tarball_pfx := $(PACKAGE_TARNAME)-$(GIT_VERSION)
+tarball_delete := $(addprefix $(tarball_pfx)/, web .gitignore)
+tarball := $(tarball_pfx).tar.bz2
+
+.PHONY: dep all clean clean2 distclean maintainer-clean install man tarball
+all: dep $(prefixed_executables) $(man_pages)
+dep: $(deps)
+man: $(man_pages)
+tarball: $(tarball)
+
+include $(m4_ggo_dir)/makefile
+include $(test_dir)/makefile.test
+ifeq ($(findstring clean, $(MAKECMDGOALS)),)
+-include $(deps)
+-include $(m4_deps)
+endif
+
+$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir) \
+               $(help2man_dir) $(hostbin_dir) $(cmdlist_dir):
+       $(Q) $(MKDIR_P) $@
+
+# When in doubt, use brute force (Ken Thompson)
+TOUPPER = \
+$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,\
+$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,\
+$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,\
+$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,\
+$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,\
+$(subst z,Z,$1))))))))))))))))))))))))))
+
+CPPFLAGS += -DBINDIR='"$(bindir)"'
+CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
+CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
+CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
+CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
+CPPFLAGS += -I/usr/local/include
+CPPFLAGS += -I$(cmdline_dir)
+CPPFLAGS += -I$(cmdlist_dir)
+
+CFLAGS += -Os
+CFLAGS += -Wuninitialized
+CFLAGS += -Wchar-subscripts
+CFLAGS += -Werror-implicit-function-declaration
+CFLAGS += -Wmissing-noreturn
+CFLAGS += -Wbad-function-cast
+CFLAGS += -fno-strict-aliasing
+
+STRICT_CFLAGS = $(CFLAGS)
+STRICT_CFLAGS += -g -Wundef -W
+STRICT_CFLAGS += -Wredundant-decls
+STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
+STRICT_CFLAGS += -Wformat -Wformat-security
+STRICT_CFLAGS += -Wmissing-format-attribute
+
+LDFLAGS += $(clock_gettime_ldflags)
+
+ifeq ($(uname_s),Linux)
+       # these cause warnings on *BSD
+       CPPFLAGS += -Wunused-macros
+       STRICT_CFLAGS += -fdata-sections -ffunction-sections
+       STRICT_CFLAGS += -Wstrict-prototypes
+       STRICT_CFLAGS += -Wshadow
+       STRICT_CFLAGS += -Wunused -Wall
+       LDFLAGS += -Wl,--gc-sections
+endif
+
+# To put more focus on warnings, be less verbose as default
+# Use 'make V=1' to see the full commands
+ifeq ("$(origin V)", "command line")
+       Q :=
+else
+       Q := @
+endif
+
+$(cmdlist_dir)/%.command_list.h: %.cmd %.c | $(cmdlist_dir)
+       @[ -z "$(Q)" ] || echo 'GEN $@'
+       $(Q) ./command_util.bash h < $< >$@
+$(cmdlist_dir)/%.command_list.man: %.cmd %.c | $(cmdlist_dir)
+       @[ -z "$(Q)" ] || echo 'GEN $@'
+       $(Q) ./command_util.bash man < $< > $@
+$(cmdlist_dir)/%.completion.h: %.cmd | $(cmdlist_dir)
+       @[ -z "$(Q)" ] || echo 'GEN $@'
+       $(Q) ./command_util.bash compl $(strip $(call TOUPPER,$(*F)))_COMPLETERS \
+               $(strip $(call TOUPPER,$(*F)))_COMMANDS < $< > $@
+
+$(cmdlist_dir)/server.command_list.h \
+$(cmdlist_dir)/server.command_list.man \
+$(cmdlist_dir)/server.completion.h \
+: command.c
+
+$(cmdlist_dir)/afs.command_list.h \
+$(cmdlist_dir)/afs.command_list.man \
+$(cmdlist_dir)/afs.completion.h \
+: afs.c aft.c attribute.c
+
+$(cmdlist_dir)/audiod.command_list.h \
+$(cmdlist_dir)/audiod.command_list.man \
+$(cmdlist_dir)/audiod.completion.h \
+: audiod_command.c
+
+server_command_lists := $(cmdlist_dir)/server.command_list.man \
+       $(cmdlist_dir)/afs.command_list.man
+audiod_command_lists := $(cmdlist_dir)/audiod.command_list.man
+play_command_lists := $(cmdlist_dir)/play.command_list.man
+
+$(man_dir)/para_server.1: $(server_command_lists)
+$(man_dir)/para_audiod.1: $(audiod_command_lists)
+$(man_dir)/para_play.1: $(play_command_lists)
+
+$(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists)
+$(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
+$(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
+
+$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
+               git-version.h | $(man_dir) $(help2man_dir)
+       @[ -z "$(Q)" ] || echo 'MAN $<'
+       $(Q) \
+               COMMAND_LISTS="$(man_util_command_lists)" \
+               FILTERS="$(filters)" \
+               GENGETOPT=$(GENGETOPT) \
+               GGO_DIR=$(ggo_dir) \
+               HELP2MAN=$(HELP2MAN) \
+               HELP2MAN_DIR=$(help2man_dir) \
+               RECEIVERS="$(receivers)" \
+               VERSION="$(GIT_VERSION)" \
+               WRITERS="$(writers)" \
+               ./man_util.bash $@
+
+$(hostbin_dir)/error2: error2.c | $(hostbin_dir)
+       @[ -z "$(Q)" ] || echo 'HCC $<'
+       $(Q) $(HOSTCC) -o $@ $<
+error2.h: $(hostbin_dir)/error2 config.h
+       @[ -z "$(Q)" ] || echo 'ER2 $<'
+       @echo "$(object_executable_matrix)" | $< > $@
+
+$(object_dir)/%.o: %.c | $(object_dir)
+
+$(object_dir)/opus%.o $(dep_dir)/opus%.d: CPPFLAGS += $(opus_cppflags)
+$(object_dir)/gui.o $(object_dir)/gui%.o $(dep_dir)/gui%.d \
+: CPPFLAGS += $(curses_cppflags)
+$(object_dir)/spx%.o $(dep_dir)/spx%.d: CPPFLAGS += $(speex_cppflags)
+$(object_dir)/flac%.o $(dep_dir)/flac%.d: CPPFLAGS += $(flac_cppflags)
+
+$(object_dir)/mp3_afh.o $(dep_dir)/mp3_afh.d: CPPFLAGS += $(id3tag_cppflags)
+$(object_dir)/crypt.o $(dep_dir)/crypt.d: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/gcrypt.o $(dep_dir)/gcrypt.d: CPPFLAGS += $(gcrypt_cppflags)
+$(object_dir)/ao_write.o $(dep_dir)/ao_write.d: CPPFLAGS += $(ao_cppflags)
+$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d: CPPFLAGS += $(mp4v2_cppflags)
+$(object_dir)/alsa%.o $(dep_dir)/alsa%.d: CPPFLAGS += $(alsa_cppflags)
+
+$(object_dir)/interactive.o $(dep_dir)/interactive.d \
+: CPPFLAGS += $(readline_cppflags)
+
+$(object_dir)/resample_filter.o $(dep_dir)/resample_filter.d \
+: CPPFLAGS += $(samplerate_cppflags)
+
+$(object_dir)/oss_write.o $(dep_dir)/oss_write.d \
+: CPPFLAGS += $(oss_cppflags)
+
+$(object_dir)/ao_write.o $(dep_dir)/ao_write.d \
+: CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
+
+$(object_dir)/mp3dec_filter.o $(dep_dir)/mp3dec_filter.d \
+: CPPFLAGS += $(mad_cppflags)
+
+$(object_dir)/aacdec_filter.o $(dep_dir)/aacdec_filter.d \
+$(object_dir)/aac_common.o $(dep_dir)/aac_common.d \
+$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d \
+: CPPFLAGS += $(faad_cppflags)
+
+$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
+$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
+: CPPFLAGS += $(vorbis_cppflags)
+
+$(object_dir)/spx_common.o $(dep_dir)/spx_common.d \
+$(object_dir)/spxdec_filter.o $(dep_dir)/spxdec_filter.d \
+$(object_dir)/spx_afh.o $(dep_dir)/spx_afh.d \
+$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
+$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
+$(object_dir)/ogg_afh_common.o $(dep_dir)/ogg_afh_common.d \
+$(object_dir)/opus%.o $(dep_dir)/opus%.d \
+: CPPFLAGS += $(ogg_cppflags)
+
+$(object_dir)/afs.o $(dep_dir)/afs.d \
+$(object_dir)/aft.o $(dep_dir)/aft.d \
+$(object_dir)/attribute.o $(dep_dir)/attribute.d \
+$(object_dir)/blob.o $(dep_dir)/blob.d  \
+$(object_dir)/mood.o $(dep_dir)/mood.d \
+$(object_dir)/playlist.o $(dep_dir)/playlist.d \
+$(object_dir)/score.o $(dep_dir)/score.d \
+$(object_dir)/server.o $(dep_dir)/server.d \
+$(object_dir)/vss.o $(dep_dir)/vss.d \
+$(object_dir)/command.o $(dep_dir)/command.d \
+$(object_dir)/http_send.o $(dep_dir)/http_send.d \
+$(object_dir)/dccp_send.o $(dep_dir)/dccp_send.d \
+$(object_dir)/udp_send.o $(dep_dir)/udp_send.d \
+$(object_dir)/send_common.o $(dep_dir)/send_common.d \
+$(object_dir)/mm.o $(dep_dir)/mm.d \
+: CPPFLAGS += $(osl_cppflags)
+
+$(object_dir)/%.cmdline.o: CFLAGS += -Wno-unused-function
+$(object_dir)/compress_filter.o: CFLAGS += -O3
+
+$(object_dir)/%.o: %.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
+
+$(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
+
+# The compiler outputs dependencies either as foo.h or as some_directory/foo.h,
+# depending on whether the latter file exists. Since make needs the directory
+# part we prefix the dependency as appropriate.
+$(dep_dir)/%.d: %.c error2.h | $(dep_dir)
+       @[ -z "$(Q)" ] || echo 'DEP $<'
+       $(Q) $(CC) $(CPPFLAGS) -MM -MG -MP -MT $@ -MT $(object_dir)/$(*F).o $< \
+               | sed -e "s@ \([a-zA-Z0-9_]\{1,\}\.cmdline.h\)@ $(cmdline_dir)/\1@g" \
+               -e "s@ \([a-zA-Z0-9_]\{1,\}\.command_list.h\)@ $(cmdlist_dir)/\1@g" \
+               -e "s@ \([a-zA-Z0-9_]\{1,\}\.completion.h\)@ $(cmdlist_dir)/\1@g" > $@
+
+para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
+para_write para_play para_audiod \
+: LDFLAGS += $(ao_ldflags) $(pthread_ldflags) $(core_audio_ldflags)
+para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
+para_server: LDFLAGS += $(osl_ldflags)
+para_gui: LDFLAGS += $(curses_ldflags)
+para_server \
+para_client \
+para_audiod \
+:LDFLAGS += \
+       $(crypto_ldflags)
+
+para_audiod \
+para_filter \
+para_play \
+: LDFLAGS += \
+       $(mad_ldflags) \
+       $(faad_ldflags) \
+       $(samplerate_ldflags) \
+       -lm
+
+para_write \
+para_play \
+para_audiod \
+para_fade \
+: LDFLAGS += \
+       $(oss_ldflags) \
+       $(alsa_ldflags)
+
+para_server \
+para_filter \
+para_audiod \
+para_play \
+para_afh \
+para_recv \
+: LDFLAGS += \
+       $(ogg_ldflags) \
+       $(vorbis_ldflags) \
+       $(speex_ldflags) \
+       $(opus_ldflags) \
+       $(faad_ldflags) \
+       $(flac_ldflags)
+
+para_server \
+para_play \
+para_afh \
+para_recv \
+: LDFLAGS += \
+       $(mp4v2_ldflags)
+
+para_server \
+para_client \
+para_audioc \
+para_audiod \
+para_recv \
+: LDFLAGS += \
+       $(socket_ldflags) $(nsl_ldflags)
+
+para_afh para_recv para_server para_play: LDFLAGS += $(iconv_ldflags)
+
+$(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs)))
+$(prefixed_executables):
+       @[ -z "$(Q)" ] || echo 'LD $@'
+       $(Q) $(CC) $^ -o $@ $(LDFLAGS)
+
+clean:
+       @[ -z "$(Q)" ] || echo 'CLEAN'
+       $(Q) rm -f para_*
+       $(Q) rm -rf $(object_dir)
+
+clean2: clean
+       @[ -z "$(Q)" ] || echo 'CLEAN2'
+       $(Q) rm -rf $(build_dir)
+distclean: clean2 test-clean
+       @[ -z "$(Q)" ] || echo 'DISTCLEAN'
+       $(Q) rm -f Makefile autoscan.log config.status config.log error2.h
+       $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
+
+maintainer-clean: distclean
+       @[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN'
+       $(Q) rm -f *.tar.bz2 config.h configure config.h.in
+
+install: all man
+       $(MKDIR_P) $(bindir) $(mandir)
+       $(INSTALL) -s --strip-program $(STRIP) -m 755 \
+               $(prefixed_executables) $(bindir)
+       $(INSTALL) -m 644 $(man_pages) $(mandir)
+       $(MKDIR_P) $(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
+
+$(tarball):
+       $(Q) rm -rf $(tarball) $(tarball_pfx)
+       $(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
+               | tar --delete $(tarball_delete) > $(tarball_pfx).tar
+       $(Q) $(MKDIR_P) $(tarball_pfx)
+       $(Q) ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
+       $(Q) cp $(autocrap) $(tarball_pfx)
+       $(Q) tar rf $(tarball_pfx).tar $(tarball_pfx)/*
+       $(Q) bzip2 -9 $(tarball_pfx).tar
+       $(Q) ls -l $(tarball)
+       $(Q) ln -sf $(tarball) paraslash-git.tar.bz2
+       $(Q) rm -rf $(tarball_pfx)
diff --git a/NEWS b/NEWS
index 4127e38bcdf0434be750aed7da7fee83acad872e..a5bef9f633eb8f25319865ea48f79bd8e34785a6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,166 @@
+NEWS
+====
+
+------------------------------------------
+current master branch "cascading gradient"
+------------------------------------------
+
+       - para_afh learned to modify meta tags of mp3 wma ogg spx
+         opus flac aac files.
+       - afs commands propagate error codes to the client.
+       - The check command now also checks the attribute table for
+         inconsistencies.
+       - New -v flag for the version command (print verbose version string)
+       - New option --priority for para_server and para_audiod.
+
+
+--------------------------------------
+0.5.5 (2015-09-20) "magnetic momentum"
+--------------------------------------
+
+Many new features and a lot of other improvements.
+
+       - On Linux systems, local sockets are now created in the
+         abstract name space by default. This allows to get rid of
+         the socket specials in /var/paraslash.
+       - The --user-allow option of para_audiod now accepts also
+         usernames rather than only user IDs.
+       - New autoconf macros to avoid duplication in configure.ac.
+       - Status items (as shown by para_gui) are updated correctly
+         when the meta information of the current audio changes.
+       - para_server and para_audiod no longer refuse to start in
+         the background if no log file is given. Instead, all log
+         messages go to /dev/null in this case.
+       - Web page cleanup.
+       - New syntax for the -l and -s options of the ls command.
+         These options should now be specified as -l=v rather than
+         -lv, for example. The old syntax still works, but support
+         will be dropped in v0.6.0.
+
+Download: ./releases/paraslash-0.5.5.tar.bz2 (tarball)
+./releases/paraslash-0.5.5.tar.bz2.asc (signature)
+
+------------------------------------------
+0.5.4 (2015-01-23) "exponential alignment"
+------------------------------------------
+
+Another cleanup and bugfix release.
+
+       - New server command: tasks.
+       - Minor cleanups to daemon.c.
+       - New URLs for home page and git services.
+       - Improved error diagnostics for the mvblob commands.
+       - New sender subcommand: status.
+       - Improved help text for server and afs commands.
+       - audiod memory leak fixes.
+       - Miscellaneous improvements to the build system.
+       - oss_writer improvements.
+       - Improved handling of mp3 files with both id3v1 and id3v2 tags.
+
+Downloads: ./releases/paraslash-0.5.4.tar.bz2 (tarball)
+./releases/paraslash-0.5.4.tar.bz2.asc (signature)
+
 ---------------------------------------------
-0.?.? (to be announced) "invertible validity"
+0.5.3 (2014-08-01) "symbolic synchronization"
 ---------------------------------------------
 
+Not many new features, but lots of fixes and usability improvements.
+
+       - para_gui has been converted to use the paraslash scheduler.
+       - Various alsa-related fixes, mostly for the raspberry pi.
+       - Many scheduler improvements and cleanups.
+       - The test suite has been extended to include sanity checks
+         for the generated man pages.
+       - ao_writer fixes. This writer was in a quite bad shape. Many
+         serious bugs have been fixed.
+       - new audiod command: version.
+       - Minor improvements to the bitstream API.
+       - The cpsi command now prints a meaningful error message if
+         none of the given patterns matched any audio file.
+
+Downloads: ./releases/paraslash-0.5.3.tar.bz2 (tarball),
+./releases/paraslash-0.5.3.tar.bz2.asc (signature)
+
+----------------------------------------
+0.5.2 (2014-04-11) "orthogonal interior"
+----------------------------------------
+
+The new sync filter, the AES_CTR128 stream cipher and the overhauled
+network code are the highlights of this release. It also includes a
+fair number of smaller fixes and improvements not mentioned here.
+
+       - The new sync filter synchronizes playback between multiple
+         clients.
+       - Connections between para_server and para_client are now
+         encrypted by means of AES rather than RC4 if both sides
+         support it. RC4 is still available as a fallback. This
+         feature is fully transparent, i.e. no command line options
+         are necessary, and a client linked against openssl can
+         speak with a server linked against libgcrypt and vice versa.
+       - Major cleanup of the networking subsystem.
+       - Improvements to para_fade: the new set mode, multi-channel
+         initial volumes, better error logging.
+       - The man pages of para_audiod, para_filter, para_recv, and
+         para_write contain the relevant options for receivers, filters,
+         writers. This broke in 0.5.0.
+       - ogg/vorbis latency improvements.
+       - Improved user manual.
+       - Minor fixes to avoid clang warnings.
+
+Downloads: ./releases/paraslash-0.5.2.tar.bz2 (tarball),
+./releases/paraslash-0.5.2.tar.bz2.asc (signature)
+
+------------------------------------------
+0.5.1 (2013-12-20) "temporary implication"
+------------------------------------------
+
+Lots of fixes and improvements all over the place, and a major overhaul
+of the build system.
+
+       - Audiod improvements and fixes.
+       - Buffer tree robustness improvements.
+       - Cleanup of the mood subsystem.
+       - Fixes and cleanups for the flac decoder.
+       - Latency improvements for the ogg/opus decoder.
+       - Crypto support is now optional. On systems without
+         openssl/gcrypt, the build succeeds but para_server,
+         para_audiod, para_client won't be built.
+       - The build system now works for cross-compile setups.
+       - The dependency tree has been flattened, which speeds up
+         builds and avoids to recreate the man pages on every change.
+       - The error code helper has been rewritten from perl to C,
+         which further improves build time.
+       - Many small bugs in the build system have been identified
+         and fixed.
+
+Downloads: ./releases/paraslash-0.5.1.tar.bz2 (tarball),
+./releases/paraslash-0.5.1.tar.bz2.asc (signature)
+
+----------------------------------------
+0.5.0 (2013-08-23) "invertible validity"
+----------------------------------------
+
+Some API-breaking changes, one serious bug fix, and a lot of bike-shedding.
+
+       - The sideband compatibility code has been removed, hence
+         sideband connections (introduced in 0.4.11) are now mandatory.
+       - Addblob commands can produce output.
+       - The stat command no longer sends garbage when para_server was
+         compiled against libgcrypt.
+       - Dependencies for gengetopt files are computed automatically.
+         This eliminates a constant source of build bugs.
+       - The setatt command now accepts file name patterns rather than only
+         path names.
+       - overview.pdf is now based on dia, a simple diagram creation program.
+         The new version is much more detailed and contains descriptions of
+         the various programs of the paraslash package.
+       - The separator of all multi-word options has been changed from
+         underscore to dash. For example --log_color becomes --log-color.
+       - Overhauled web pages and the new logo.
+
+Downloads: ./releases/paraslash-0.5.0.tar.bz2 (tarball),
+./releases/paraslash-0.5.0.tar.bz2.asc (signature)
+
 --------------------------------------
 0.4.13 (2013-07-29) "spectral gravity"
 --------------------------------------
diff --git a/README b/README
index e991b7340e61bb2bee929fe145e39b0dee32bf8d..0a0aae881bf6a766d449550c5ddc51152a4800bc 100644 (file)
--- a/README
+++ b/README
@@ -1,26 +1,14 @@
-README
-======
+The paraslash package contains server and client software for network
+audio streaming and stand-alone utilities for decoding and playing
+audio files. See the user manual for details.
 
-----
-Paraslash is an acronym for
+Distribution of paraslash is covered by the GNU GPL, version 2 unless
+otherwise stated. See file COPYING.
 
-_Play, archive, rate and stream large audio sets happily_
+Web page:             http://people.tuebingen.mpg.de/maan/paraslash/
+Alternative web page: http://paraslash.systemlinux.org/
+Gitweb:               http://git.tuebingen.mpg.de/paraslash.git/
+Git URL:              git://git.tuebingen.mpg.de/paraslash.git
+Email:                Andre Noll <maan@tuebingen.mpg.de>
 
-The paraslash package contains server and client software for
-network streaming as well as stand-alone utilities for decoding
-mp3, ogg, aac and wma files. See the user manual for details.
-
-
--------
-LICENSE
--------
-
-Distribution of paraslash is covered by the GNU GPL, version 2. See file
-COPYING.
-
-----------
-THE AUTHOR
-----------
-
-Andre Noll <maan@systemlinux.org>
 Comments and bug reports are welcome.
diff --git a/aac.h b/aac.h
index 08bc8b1ef6d26fdf6486b0a3becd015a3b5e9b7f..f31d723a315557acf0fe3dd80fde6e628eafe4ff 100644 (file)
--- a/aac.h
+++ b/aac.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 2d04695a84305972c6a4d87b1aef77457a8606ad..d3e694548a3dbfaf08e40eb5cd8ba01bc10af7cf 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 /** \file aac_afh.c para_server's aac audio format handler. */
 
 #include <regex.h>
+#include <mp4v2/mp4v2.h>
 
 #include "para.h"
 #include "error.h"
 #include "afh.h"
 #include "string.h"
 #include "aac.h"
+#include "fd.h"
 
 static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip)
 {
@@ -44,23 +46,13 @@ static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip)
 
 static int atom_cmp(const unsigned char *buf1, const char *buf2)
 {
-       const unsigned char *b2 = (unsigned char *)buf2;
-
-       if (buf1[0] != b2[0])
-               return 1;
-       if (buf1[1] != b2[1])
-               return 1;
-       if (buf1[2] != b2[2])
-               return 1;
-       if (buf1[3] != b2[3])
-               return 1;
-       return 0;
+       return memcmp(buf1, buf2, 4)? 1 : 0;
 }
 
 static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char type[5])
 {
        int i;
-       uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+       uint64_t size = aac_read_int32(buf);
 
        memcpy(type, buf + 4, 4);
        type[4] = '\0';
@@ -111,15 +103,15 @@ static void read_tags(unsigned char *buf, size_t buflen, struct afh_info *afhi)
                q = p + ret + ret2 + 8;
                if (q + size2 > buf + buflen)
                        break;
-               if (!atom_cmp(type1, "©ART"))
+               if (!atom_cmp(type1, "\xa9" "ART"))
                        afhi->tags.artist = get_tag(q, size2);
-               else if (!atom_cmp(type1, "©alb"))
+               else if (!atom_cmp(type1, "\xa9" "alb"))
                        afhi->tags.album = get_tag(q, size2);
-               else if (!atom_cmp(type1, "©nam"))
+               else if (!atom_cmp(type1, "\xa9" "nam"))
                        afhi->tags.title = get_tag(q, size2);
-               else if (!atom_cmp(type1, "©cmt"))
+               else if (!atom_cmp(type1, "\xa9" "cmt"))
                        afhi->tags.comment = get_tag(q, size2);
-               else if (!atom_cmp(type1, "©day"))
+               else if (!atom_cmp(type1, "\xa9" "day"))
                        afhi->tags.year = get_tag(q, size2);
                p += size1;
        }
@@ -172,7 +164,7 @@ static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
        if (ret < 0)
                return ret;
        afhi->chunks_total = ret;
-       PARA_DEBUG_LOG("sz table has %lu entries\n", afhi->chunks_total);
+       PARA_DEBUG_LOG("sz table has %" PRIu32 " entries\n", afhi->chunks_total);
        afhi->chunk_table = para_malloc((afhi->chunks_total + 1) * sizeof(size_t));
        for (i = 1; i <= afhi->chunks_total; i++) {
                if (skip + 4 > numbytes)
@@ -187,7 +179,7 @@ static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
 }
 
 static int aac_set_chunk_tv(struct afh_info *afhi,
-               mp4AudioSpecificConfig *mp4ASC, long unsigned *seconds)
+               mp4AudioSpecificConfig *mp4ASC, uint32_t *seconds)
 {
        float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023;
        struct timeval total;
@@ -198,7 +190,7 @@ static int aac_set_chunk_tv(struct afh_info *afhi,
        ms = 1000.0 * afhi->chunks_total * tmp / mp4ASC->samplingFrequency;
        ms2tv(ms, &total);
        tv_divide(afhi->chunks_total, &total, &afhi->chunk_tv);
-       PARA_INFO_LOG("%luHz, %lus (%lu x %lums)\n",
+       PARA_INFO_LOG("%luHz, %lus (%" PRIu32 " x %lums)\n",
                mp4ASC->samplingFrequency, ms / 1000,
                afhi->chunks_total, tv2ms(&afhi->chunk_tv));
        if (ms < 1000)
@@ -263,6 +255,68 @@ out:
        return ret;
 }
 
+static int aac_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int fd, const char *filename)
+{
+       MP4FileHandle h;
+       const MP4Tags *mdata;
+       int ret = write_all(fd, map, mapsize);
+
+       if (ret < 0)
+               return ret;
+       lseek(fd, 0, SEEK_SET);
+       h = MP4Modify(filename, 0);
+       if (!h) {
+               PARA_ERROR_LOG("MP4Modify() failed, fd = %d\n", fd);
+               return -E_MP4V2;
+       }
+       mdata = MP4TagsAlloc();
+       assert(mdata);
+       if (!MP4TagsFetch(mdata, h)) {
+               PARA_ERROR_LOG("MP4Tags_Fetch() failed\n");
+               ret = -E_MP4V2;
+               goto close;
+       }
+
+       if (!MP4TagsSetAlbum(mdata, tags->album)) {
+               PARA_ERROR_LOG("Could not set album\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+       if (!MP4TagsSetArtist(mdata, tags->artist)) {
+               PARA_ERROR_LOG("Could not set album\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+       if (!MP4TagsSetComments(mdata, tags->comment)) {
+               PARA_ERROR_LOG("Could not set comment\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+       if (!MP4TagsSetName(mdata, tags->title)) {
+               PARA_ERROR_LOG("Could not set title\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+       if (!MP4TagsSetReleaseDate(mdata, tags->year)) {
+               PARA_ERROR_LOG("Could not set release date\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+
+       if (!MP4TagsStore(mdata, h)) {
+               PARA_ERROR_LOG("Could not store tags\n");
+               ret = -E_MP4V2;
+               goto tags_free;
+       }
+       ret = 1;
+tags_free:
+       MP4TagsFree(mdata);
+close:
+       MP4Close(h, 0);
+       return ret;
+}
+
 static const char* aac_suffixes[] = {"m4a", "mp4", NULL};
 /**
  * the init function of the aac audio format handler
@@ -273,4 +327,5 @@ void aac_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = aac_get_file_info,
        afh->suffixes = aac_suffixes;
+       afh->rewrite_tags = aac_rewrite_tags;
 }
index ab1fb50c259546755f23ca8838eeba1c63def529..fe9b7295f55cc7614278669c670f3bec39827347 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 3ff90834c62bb75f179e5778ef02f6d23feb0d09..ac8148e863d8568082dca4cb3aac68843e5893a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -80,9 +80,9 @@ static void aacdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static int aacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int aacdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
        struct private_aacdec_data *padd = fn->private_data;
        int i, ret;
diff --git a/acl.c b/acl.c
index 60f835aefc0e2c777dae34e1a0b80fb371e0268e..5ef9a51ca381e3f6aa3a21de21eb0e0d3b37114f 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -1,18 +1,24 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file acl.c Access control lists for paraslash senders. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 #include "string.h"
 #include "list.h"
 #include "net.h"
+#include "acl.h"
 
 /**
  * Describes one entry in the blacklist/whitelist of a paraslash sender.
@@ -57,7 +63,7 @@ static int acl_lookup(int fd, struct list_head *acl)
                PARA_ERROR_LOG("Can not determine peer address: %s\n", strerror(errno));
                goto no_match;
        }
-       v4_addr = extract_v4_addr(&ss);
+       extract_v4_addr(&ss, &v4_addr);
        if (!v4_addr.s_addr)
                goto no_match;
 
diff --git a/acl.h b/acl.h
index c44f26ab21fa042ca0be1093e97262c5d325ce2d..5bfa39f23b2c087456471e06ea3d55b816fffbaf 100644 (file)
--- a/acl.h
+++ b/acl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/afh.c b/afh.c
index 8e70750511381b425c33f0c7cf8237dc1a47af16..195b3788678549a5ab2554d0ac388e1a3a8f60f3 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -23,6 +23,97 @@ INIT_AFH_ERRLISTS;
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
 
+static inline bool tag_needs_update(bool given, const char *tag,
+               const char *arg)
+{
+       return given && (!tag || strcmp(tag, arg) != 0);
+}
+
+static int rewrite_tags(const char *name, int input_fd, void *map,
+               size_t map_size, int audio_format_id, struct afh_info *afhi)
+{
+       struct taginfo *tags = &afhi->tags;
+       bool modified = false;
+       char *tmp_name;
+       int output_fd = -1, ret;
+       struct stat sb;
+
+       if (tag_needs_update(conf.year_given, tags->year, conf.year_arg)) {
+               free(tags->year);
+               tags->year = para_strdup(conf.year_arg);
+               modified = true;
+       }
+       if (tag_needs_update(conf.title_given, tags->title, conf.title_arg)) {
+               free(tags->title);
+               tags->title = para_strdup(conf.title_arg);
+               modified = true;
+       }
+       if (tag_needs_update(conf.artist_given, tags->artist,
+                       conf.artist_arg)) {
+               free(tags->artist);
+               tags->artist = para_strdup(conf.artist_arg);
+               modified = true;
+       }
+       if (tag_needs_update(conf.album_given, tags->album, conf.album_arg)) {
+               free(tags->album);
+               tags->album = para_strdup(conf.album_arg);
+               modified = true;
+       }
+       if (tag_needs_update(conf.comment_given, tags->comment,
+                       conf.comment_arg)) {
+               free(tags->comment);
+               tags->comment = para_strdup(conf.comment_arg);
+               modified = true;
+       }
+       if (!modified) {
+               PARA_WARNING_LOG("no modifications necessary\n");
+               return 0;
+       }
+       /*
+        * mkstmp() creates the temporary file with permissions 0600, but we
+        * like it to have the same permissions as the original file, so we
+        * have to get this information.
+        */
+       if (fstat(input_fd, &sb) < 0) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("failed to fstat fd %d (%s)\n", input_fd, name);
+               return ret;
+       }
+       tmp_name = make_message("%s.XXXXXX", name);
+       ret = mkstemp(tmp_name);
+       if (ret < 0) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("could not create temporary file\n");
+               goto out;
+       }
+       output_fd = ret;
+       if (fchmod(output_fd, sb.st_mode) < 0) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("failed to fchmod fd %d (%s)\n", output_fd,
+                       tmp_name);
+               goto out;
+       }
+       ret = afh_rewrite_tags(audio_format_id, map, map_size, tags, output_fd,
+               tmp_name);
+       if (ret < 0)
+               goto out;
+       if (conf.backup_given) {
+               char *backup_name = make_message("%s~", name);
+               ret = xrename(name, backup_name);
+               free(backup_name);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = xrename(tmp_name, name);
+out:
+       if (ret < 0 && output_fd >= 0)
+               unlink(tmp_name); /* ignore errors */
+       free(tmp_name);
+       if (output_fd >= 0)
+               close(output_fd);
+       return ret;
+}
+
 static void print_info(int audio_format_num, struct afh_info *afhi)
 {
        char *msg;
@@ -102,16 +193,21 @@ int main(int argc, char **argv)
                }
                ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
                        fd, &afhi);
-               if (ret < 0)
-                       goto out;
-
-               audio_format_num = ret;
-               printf("File %d: %s\n", i + 1, conf.inputs[i]);
-               print_info(audio_format_num, &afhi);
-               if (conf.chunk_table_given)
-                       print_chunk_table(&afhi);
-               printf("\n");
-               clear_afhi(&afhi);
+               if (ret >= 0) {
+                       audio_format_num = ret;
+                       if (conf.modify_given) {
+                               ret = rewrite_tags(conf.inputs[i], fd, audio_file_data,
+                                       audio_file_size, audio_format_num, &afhi);
+                       } else {
+                               printf("File %d: %s\n", i + 1, conf.inputs[i]);
+                               print_info(audio_format_num, &afhi);
+                               if (conf.chunk_table_given)
+                                       print_chunk_table(&afhi);
+                               printf("\n");
+                       }
+                       clear_afhi(&afhi);
+               }
+               close(fd);
                ret2 = para_munmap(audio_file_data, audio_file_size);
                if (ret2 < 0 && ret >= 0)
                        ret = ret2;
diff --git a/afh.h b/afh.h
index b224b61a4bbf5f210311fe2c7573c4725e866e7a..7a30947a637e43fbeccc9788b30c21f917a427df 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -28,9 +28,9 @@ struct taginfo {
 /** Audio format dependent information. */
 struct afh_info {
        /** The number of chunks this audio file contains. */
-       long unsigned chunks_total;
+       uint32_t chunks_total;
        /** The length of the audio file in seconds. */
-       long unsigned seconds_total;
+       uint32_t seconds_total;
        /** Audio handler specific info about the file. */
        char *techinfo;
        /** Id3 tags, vorbis comments, aac tags. */
@@ -58,13 +58,25 @@ struct afh_info {
        uint16_t bitrate;
 };
 
+/** Data about the current audio file, passed from afs to server. */
+struct audio_file_data {
+       /** The open file descriptor to the current audio file. */
+       int fd;
+       /** Vss needs this for streaming. */
+       struct afh_info afhi;
+       /** Size of the largest chunk. */
+       uint32_t max_chunk_size;
+       /** Needed to get the audio file header. */
+       uint8_t audio_format_id;
+};
+
 /**
- *  Structure for audio format handling.
+ * Structure for audio format handling.
  *
- *  There's one such struct for each supported audio format. Initially, only \a
- *  name and \a init are defined. During the startup process, para_server calls
- *  the \a init function of each audio format handler which is expected to fill
- *  in the other part of this struct.
+ * There's one such struct for each supported audio format. Initially, only \a
+ * name and \a init are defined. During the startup process, para_server calls
+ * the \a init function of each audio format handler which is expected to fill
+ * in the other part of this struct.
  */
 struct audio_format_handler {
        /** Name of the audio format. */
@@ -92,6 +104,14 @@ struct audio_format_handler {
                struct afh_info *afi);
        /** Optional, used for header-rewriting. See \ref afh_get_header(). */
        void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
+       /**
+        * Write audio file with altered tags, optional.
+        *
+        * The output file descriptor has been opened by the caller and must not
+        * be closed in this function.
+        */
+       int (*rewrite_tags)(const char *map, size_t mapsize, struct taginfo *tags,
+               int output_fd, const char *filename);
 };
 
 void afh_init(void);
@@ -101,8 +121,12 @@ int compute_afhi(const char *path, char *data, size_t size,
 const char *audio_format_name(int);
 void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                void *map, const char **buf, size_t *len);
+int32_t afh_get_start_chunk(int32_t approx_chunk_num,
+               const struct afh_info *afhi);
 void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
                void *map, size_t mapsize, char **buf, size_t *len);
 void afh_free_header(char *header_buf, uint8_t audio_format_id);
 void clear_afhi(struct afh_info *afhi);
 unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **result);
+int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
+               struct taginfo *tags, int output_fd, const char *filename);
index 5be43550c202b516aaedf332dbbfba341da20dcb..063ae8f0b104ccab15e9f10f637ec34e7783e785 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #include "string.h"
 #include "afh.h"
 
-/* The mp3 audio format handler does not need any libs. */
-void mp3_init(struct audio_format_handler *);
-
-#ifdef HAVE_OGGVORBIS
-       void ogg_init(struct audio_format_handler *);
-#endif
-#ifdef HAVE_FAAD
-       void aac_afh_init(struct audio_format_handler *);
-#endif
-#ifdef HAVE_SPEEX
-       void spx_afh_init(struct audio_format_handler *);
-#endif
-#ifdef HAVE_FLAC
-       void flac_afh_init(struct audio_format_handler *);
-#endif
-
-#ifdef HAVE_OPUS
-       void opus_afh_init(struct audio_format_handler *);
-#endif
-
-void wma_afh_init(struct audio_format_handler *);
+typedef void afh_init_func(struct audio_format_handler *);
+/* It does not hurt to declare init functions which are not available. */
+extern afh_init_func mp3_init, ogg_init, aac_afh_init, wma_afh_init,
+       spx_afh_init, flac_afh_init, opus_afh_init;
 
 /** The list of all status items */
 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
@@ -59,13 +42,13 @@ static struct audio_format_handler afl[] = {
        },
        {
                .name = "ogg",
-#ifdef HAVE_OGGVORBIS
+#if defined(HAVE_OGG) && defined(HAVE_VORBIS)
                .init = ogg_init,
 #endif
        },
        {
                .name = "aac",
-#ifdef HAVE_FAAD
+#if defined(HAVE_MP4V2)
                .init = aac_afh_init,
 #endif
        },
@@ -75,19 +58,19 @@ static struct audio_format_handler afl[] = {
        },
        {
                .name = "spx",
-#ifdef HAVE_SPEEX
+#if defined(HAVE_OGG) && defined(HAVE_SPEEX)
                .init = spx_afh_init,
 #endif
        },
        {
                .name = "flac",
-#ifdef HAVE_FLAC
+#if defined(HAVE_OGG) && defined(HAVE_FLAC)
                .init = flac_afh_init,
 #endif
        },
        {
                .name = "opus",
-#ifdef HAVE_OPUS
+#if defined(HAVE_OGG) && defined(HAVE_OPUS)
                .init = opus_afh_init,
 #endif
        },
@@ -118,9 +101,9 @@ void afh_init(void)
 {
        int i;
 
-       PARA_INFO_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+       PARA_NOTICE_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
        FOR_EACH_AUDIO_FORMAT(i) {
-               PARA_NOTICE_LOG("initializing %s handler\n",
+               PARA_INFO_LOG("initializing %s handler\n",
                        audio_format_name(i));
                afl[i].init(&afl[i]);
        }
@@ -230,11 +213,12 @@ success:
 }
 
 /**
- * Deallocate contents of a filled-in ahi structure
+ * Deallocate the contents of an afh_info structure.
  *
  * \param afhi The structure to clear.
  *
- * The given pointer is kept, everything else is freed.
+ * This only frees the memory the various pointer fields of \a afhi point to.
+ * It does *not* free \a afhi itself.
  */
 void clear_afhi(struct afh_info *afhi)
 {
@@ -264,6 +248,12 @@ const char *audio_format_name(int i)
        return afl[i].name;
 }
 
+static inline size_t get_chunk_len(long unsigned chunk_num,
+               const struct afh_info *afhi)
+{
+       return afhi->chunk_table[chunk_num + 1] - afhi->chunk_table[chunk_num];
+}
+
 /**
  * Get one chunk of audio data.
  *
@@ -281,7 +271,28 @@ void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
 {
        size_t pos = afhi->chunk_table[chunk_num];
        *buf = map + pos;
-       *len = afhi->chunk_table[chunk_num + 1] - pos;
+       *len = get_chunk_len(chunk_num, afhi);
+}
+
+/**
+ * Find a suitable start chunk.
+ *
+ * \param approx_chunk_num Upper bound for the chunk number to return.
+ * \param afhi Needed for the chunk table.
+ *
+ * \return The first non-empty chunk <= \a approx_chunk_num.
+ *
+ * \sa \ref afh_get_chunk().
+ */
+int32_t afh_get_start_chunk(int32_t approx_chunk_num,
+               const struct afh_info *afhi)
+{
+       int32_t k;
+
+       for (k = PARA_MAX(0, approx_chunk_num); k >= 0; k--)
+               if (get_chunk_len(k, afhi) > 0)
+                       break;
+       return k;
 }
 
 /**
@@ -356,9 +367,9 @@ unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **re
                "%s: %s\n" /* format */
                "%s: %dHz\n" /* frequency */
                "%s: %d\n" /* channels */
-               "%s: %lu\n" /* seconds total */
+               "%s: %" PRIu32 "\n" /* seconds total */
                "%s: %lu: %lu\n" /* chunk time */
-               "%s: %lu\n" /* num chunks */
+               "%s: %" PRIu32 "\n" /* num chunks */
                "%s: %s\n" /* techinfo */
                "%s: %s\n" /* artist */
                "%s: %s\n" /* title */
@@ -381,3 +392,31 @@ unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **re
                status_item_list[SI_COMMENT], afhi->tags.comment? afhi->tags.comment : ""
        );
 }
+
+/**
+ * Create a copy of the given file with altered meta tags.
+ *
+ * \param audio_format_id Specifies the audio format.
+ * \param map The (read-only) memory map of the input file.
+ * \param mapsize The size of the input file in bytes.
+ * \param tags The new tags.
+ * \param output_fd Altered file is created using this file descriptor.
+ * \param filename The name of the temporary output file.
+ *
+ * This calls the ->rewrite_tags method of the audio format handler associated
+ * with \a audio_format_id to create a copy of the memory-mapped file given
+ * by \a map and \a mapsize, but with altered tags according to \a tags. If
+ * the audio format handler for \a audio_format_id lacks this optional method,
+ * the function returns (the paraslash error code of) \p ENOTSUP.
+ *
+ * \return Standard.
+ */
+int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
+               struct taginfo *tags, int output_fd, const char *filename)
+{
+       struct audio_format_handler *afh = afl + audio_format_id;
+
+       if (!afh->rewrite_tags)
+               return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+       return afh->rewrite_tags(map, mapsize, tags, output_fd, filename);
+}
index 5c17dab7de21132cf48d50bfaf37eabba506ce6f..164634634fedbaaf5441ebdec79dcff51e24d641 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -8,7 +8,6 @@
 
 #include <regex.h>
 #include <sys/types.h>
-#include <stdbool.h>
 
 #include "para.h"
 #include "error.h"
@@ -41,11 +40,11 @@ static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
 
        *result = NULL;
        if (!strcmp(cmd, "seconds_total")) {
-               *result = make_message("%lu", pard->afhi.seconds_total);
+               *result = make_message("%" PRIu32, pard->afhi.seconds_total);
                return 1;
        }
        if (!strcmp(cmd, "chunks_total")) {
-               *result = make_message("%lu", pard->afhi.chunks_total);
+               *result = make_message("%" PRIu32, pard->afhi.chunks_total);
                return 1;
        }
        if (!strcmp(cmd, "afhi")) {
@@ -59,8 +58,8 @@ static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
                        return ret;
                if (x >= pard->afhi.chunks_total)
                        return -ERRNO_TO_PARA_ERROR(EINVAL);
-               pard->first_chunk = pard->current_chunk = x;
-               rn->task.error = 0;
+               pard->first_chunk = afh_get_start_chunk(x, &pard->afhi);
+               pard->current_chunk = pard->first_chunk;
                return 1;
        }
        return -E_BTR_NAVAIL;
@@ -110,9 +109,12 @@ static int afh_recv_open(struct receiver_node *rn)
        if (PARA_ABS(conf->begin_chunk_arg) >= afhi->chunks_total)
                goto out_clear_afhi;
        if (conf->begin_chunk_arg >= 0)
-               pard->first_chunk = conf->begin_chunk_arg;
+               pard->first_chunk = afh_get_start_chunk(
+                       conf->begin_chunk_arg, &pard->afhi);
        else
-               pard->first_chunk = afhi->chunks_total + conf->begin_chunk_arg;
+               pard->first_chunk = afh_get_start_chunk(
+                       afhi->chunks_total + conf->begin_chunk_arg,
+                       &pard->afhi);
        if (conf->end_chunk_given) {
                ret = -ERRNO_TO_PARA_ERROR(EINVAL);
                if (PARA_ABS(conf->end_chunk_arg) > afhi->chunks_total)
@@ -151,14 +153,14 @@ static void afh_recv_close(struct receiver_node *rn)
        freep(&rn->private_data);
 }
 
-static void afh_recv_pre_select(struct sched *s, struct task *t)
+static void afh_recv_pre_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct private_afh_recv_data *pard = rn->private_data;
        struct afh_info *afhi = &pard->afhi;
        struct afh_recv_args_info *conf = rn->conf;
        struct timeval chunk_time;
-       int state = generic_recv_pre_select(s, t);
+       int state = generic_recv_pre_select(s, rn);
 
        if (state <= 0)
                return;
@@ -171,9 +173,9 @@ static void afh_recv_pre_select(struct sched *s, struct task *t)
        sched_request_barrier_or_min_delay(&chunk_time, s);
 }
 
-static int afh_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int afh_recv_post_select(__a_unused struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct afh_recv_args_info *conf = rn->conf;
        struct private_afh_recv_data *pard = rn->private_data;
        struct btr_node *btrn = rn->btrn;
diff --git a/afs.c b/afs.c
index 1a5e602dfbe1db769fef82c8f202ed0c9f5eca74..ee1c6b5d0edad1472551274d0de9a4288504c3af 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -1,15 +1,20 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file afs.c Paraslash's audio file selector. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <signal.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "server.cmdline.h"
 #include "para.h"
@@ -23,8 +28,8 @@
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
-#include "signal.h"
 #include "fd.h"
+#include "signal.h"
 #include "mood.h"
 #include "sideband.h"
 #include "command.h"
@@ -76,7 +81,7 @@ struct command_task {
         */
        uint32_t cookie;
        /** The associated task structure. */
-       struct task task;
+       struct task *task;
 };
 
 extern int mmd_mutex;
@@ -84,7 +89,7 @@ extern struct misc_meta_data *mmd;
 
 static int server_socket;
 static struct command_task command_task_struct;
-static struct signal_task signal_task_struct;
+static struct signal_task *signal_task;
 
 static enum play_mode current_play_mode;
 static char *current_mop; /* mode or playlist specifier. NULL means dummy mood */
@@ -92,10 +97,11 @@ static char *current_mop; /* mode or playlist specifier. NULL means dummy mood *
 /**
  * A random number used to "authenticate" the connection.
  *
- * para_server picks this number by random before forking the afs process.  The
- * command handlers write this number together with the id of the shared memory
- * area containing the query. This way, a malicious local user has to know this
- * number to be able to cause the afs process to crash by sending fake queries.
+ * para_server picks this number by random before it forks the afs process. The
+ * command handlers know this number as well and write it to the afs socket,
+ * together with the id of the shared memory area which contains the payload of
+ * the afs command. A local process has to know this number to abuse the afs
+ * service provided by the local socket.
  */
 extern uint32_t afs_socket_cookie;
 
@@ -125,7 +131,7 @@ extern uint32_t afs_socket_cookie;
  */
 struct callback_query {
        /** The function to be called. */
-       callback_function *handler;
+       afs_callback *handler;
        /** The number of bytes of the query */
        size_t query_size;
 };
@@ -161,13 +167,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
        }
        result.size = cr->result_size;
        result.data = result_shm + sizeof(*cr);
-       if (result.size) {
-               assert(handler);
-               ret = handler(&result, cr->band, private_result_data);
-               if (ret < 0)
-                       PARA_NOTICE_LOG("result handler error: %s\n",
-                               para_strerror(-ret));
-       }
+       assert(handler);
+       ret = handler(&result, cr->band, private_result_data);
        ret2 = shm_detach(result_shm);
        if (ret2 < 0) {
                PARA_ERROR_LOG("detach failed: %s\n", para_strerror(-ret2));
@@ -190,7 +191,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
  * copied.  It then notifies the afs process that the callback function \a f
  * should be executed by sending the shared memory identifier (shmid) to the
  * socket.
-
+ *
  * If the callback produces a result, it sends any number of shared memory
  * identifiers back via the socket. For each such identifier received, \a
  * result_handler is called. The contents of the sma identified by the received
@@ -201,7 +202,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
  *
  * \sa send_option_arg_callback_request(), send_standard_callback_request().
  */
-int send_callback_request(callback_function *f, struct osl_object *query,
+int send_callback_request(afs_callback *f, struct osl_object *query,
                callback_result_handler *result_handler,
                void *private_result_data)
 {
@@ -231,8 +232,8 @@ int send_callback_request(callback_function *f, struct osl_object *query,
        if (ret < 0)
                goto out;
 
-       *(uint32_t *) buf = afs_socket_cookie;
-       *(int *) (buf + sizeof(afs_socket_cookie)) = query_shmid;
+       *(uint32_t *)buf = afs_socket_cookie;
+       *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid;
 
        ret = connect_local_socket(conf.afs_socket_arg);
        if (ret < 0)
@@ -256,12 +257,10 @@ int send_callback_request(callback_function *f, struct osl_object *query,
                ret = *(int *) buf;
                assert(ret > 0);
                result_shmid = ret;
-               if (!dispatch_error) {
-                       ret = dispatch_result(result_shmid, result_handler,
-                               private_result_data);
-                       if (ret < 0)
-                               dispatch_error = 1;
-               }
+               ret = dispatch_result(result_shmid, result_handler,
+                       private_result_data);
+               if (ret < 0 && dispatch_error >= 0)
+                       dispatch_error = ret;
                ret = shm_destroy(result_shmid);
                if (ret < 0)
                        PARA_CRIT_LOG("destroy result failed: %s\n",
@@ -273,8 +272,11 @@ out:
                PARA_CRIT_LOG("shm destroy error\n");
        if (fd >= 0)
                close(fd);
-//     PARA_DEBUG_LOG("callback_ret: %d\n", ret);
-       return ret < 0? ret : num_dispatched;
+       if (dispatch_error < 0)
+               return dispatch_error;
+       if (ret < 0)
+               return ret;
+       return num_dispatched;
 }
 
 /**
@@ -287,10 +289,10 @@ out:
  * \param result_handler See \ref send_callback_request.
  * \param private_result_data See \ref send_callback_request.
  *
- * Some commands have a couple of options that are parsed in child context for
- * syntactic correctness and are stored in a special options structure for that
- * command. This function allows to pass such a structure together with a list
- * of further arguments (often a list of audio files) to the parent process.
+ * Some command handlers pass command-specific options to a callback, together
+ * with a list of further arguments (often a list of audio files). This
+ * function allows to pass an arbitrary structure (given as an osl object) and
+ * a usual argument vector to the specified callback.
  *
  * \return The return value of the underlying call to \ref
  * send_callback_request().
@@ -298,7 +300,7 @@ out:
  * \sa send_standard_callback_request(), send_callback_request().
  */
 int send_option_arg_callback_request(struct osl_object *options,
-               int argc,  char * const * const argv, callback_function *f,
+               int argc,  char * const * const argv, afs_callback *f,
                callback_result_handler *result_handler,
                void *private_result_data)
 {
@@ -340,7 +342,7 @@ int send_option_arg_callback_request(struct osl_object *options,
  * send_option_arg_callback_request().
  */
 int send_standard_callback_request(int argc,  char * const * const argv,
-               callback_function *f, callback_result_handler *result_handler,
+               afs_callback *f, callback_result_handler *result_handler,
                void *private_result_data)
 {
        return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
@@ -360,8 +362,11 @@ static int action_if_pattern_matches(struct osl_row *row, void *data)
        name = (char *)name_obj.data;
        if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
                return 1;
-       if (!pmd->patterns.size && (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING))
+       if (pmd->patterns.size == 0 &&
+                       (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) {
+               pmd->num_matches++;
                return pmd->action(pmd->table, row, name, pmd->data);
+       }
        for (p = pattern_txt; p < pattern_txt + pmd->patterns.size;
                        p += strlen(p) + 1) {
                ret = fnmatch(p, name, pmd->fnmatch_flags);
@@ -450,7 +455,7 @@ static int pass_afd(int fd, char *buf, size_t size)
 }
 
 /**
- * Open the audio file with highest score.
+ * Pass the fd of the next audio file to the server process.
  *
  * This stores all information for streaming the "best" audio file in a shared
  * memory area. The id of that area and an open file descriptor for the next
@@ -462,27 +467,15 @@ static int pass_afd(int fd, char *buf, size_t size)
  */
 static int open_next_audio_file(void)
 {
-       struct osl_row *aft_row;
        struct audio_file_data afd;
        int ret, shmid;
        char buf[8];
-       long score;
-again:
-       PARA_NOTICE_LOG("getting next audio file\n");
-       ret = score_get_best(&aft_row, &score);
+
+       ret = open_and_update_audio_file(&afd);
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                goto no_admissible_files;
        }
-       ret = open_and_update_audio_file(aft_row, score, &afd);
-       if (ret < 0) {
-               ret = score_delete(aft_row);
-               if (ret < 0) {
-                       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-                       goto no_admissible_files;
-               }
-               goto again;
-       }
        shmid = ret;
        if (!write_ok(server_socket)) {
                ret = -E_AFS_SOCKET;
@@ -509,6 +502,7 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible)
        enum play_mode mode;
        int ret;
 
+       PARA_INFO_LOG("new playlist: %s\n", arg);
        if (!arg) {
                ret = change_current_mood(NULL); /* always successful */
                mode = PLAY_MODE_MOOD;
@@ -563,54 +557,70 @@ int afs_cb_result_handler(struct osl_object *result, uint8_t band,
        struct command_context *cc = private;
 
        assert(cc);
-       if (!result->size)
-               return 1;
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, result->data, result->size, band,
-                       true);
-       return sc_send_bin_buffer(&cc->scc, result->data, result->size);
+       switch (band) {
+       case SBD_OUTPUT:
+       case SBD_DEBUG_LOG:
+       case SBD_INFO_LOG:
+       case SBD_NOTICE_LOG:
+       case SBD_WARNING_LOG:
+       case SBD_ERROR_LOG:
+       case SBD_CRIT_LOG:
+       case SBD_EMERG_LOG:
+               assert(result->size > 0);
+               return send_sb(&cc->scc, result->data, result->size, band, true);
+       case SBD_AFS_CB_FAILURE:
+               return *(int *)(result->data);
+       default:
+               return -E_BAD_BAND;
+       }
 }
 
-static void com_select_callback(int fd, const struct osl_object *query)
+static void flush_and_free_pb(struct para_buffer *pb)
 {
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler,
-       };
-       char *arg = query->data;
-       int num_admissible, ret, ret2;
+       int ret;
+       struct afs_max_size_handler_data *amshd = pb->private_data;
+
+       if (pb->buf && pb->size > 0) {
+               ret = pass_buffer_as_shm(amshd->fd, amshd->band, pb->buf,
+                       pb->offset);
+               if (ret < 0)
+                       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       }
+       free(pb->buf);
+}
+
+static int com_select_callback(struct afs_callback_arg *aca)
+{
+       char *arg = aca->query.data;
+       int num_admissible, ret;
 
        ret = clear_score_table();
        if (ret < 0) {
-               ret2 = para_printf(&pb, "%s\n", para_strerror(-ret));
-               goto out;
+               para_printf(&aca->pbout, "could not clear score table: %s\n",
+                       para_strerror(-ret));
+               return ret;
        }
        if (current_play_mode == PLAY_MODE_MOOD)
                close_current_mood();
        else
                playlist_close();
        ret = activate_mood_or_playlist(arg, &num_admissible);
-       if (ret < 0) {
-               ret2 = para_printf(&pb, "%s\nswitching back to %s\n",
-                       para_strerror(-ret), current_mop?
-                       current_mop : "dummy");
-               ret = activate_mood_or_playlist(current_mop, &num_admissible);
-               if (ret < 0) {
-                       if (ret2 >= 0)
-                               ret2 = para_printf(&pb, "failed, switching to dummy\n");
-                       activate_mood_or_playlist(NULL, &num_admissible);
-               }
-       } else
-               ret2 = para_printf(&pb, "activated %s (%d admissible files)\n", current_mop?
-                       current_mop : "dummy mood", num_admissible);
+       if (ret >= 0)
+               goto out;
+       /* ignore subsequent errors (but log them) */
+       para_printf(&aca->pbout, "could not activate %s: %s\n"
+               "switching back to %s\n",
+               arg, para_strerror(-ret), current_mop? current_mop : "dummy");
+       ret = activate_mood_or_playlist(current_mop, &num_admissible);
+       if (ret >= 0)
+               goto out;
+       para_printf(&aca->pbout, "could not activate %s: %s\nswitching to dummy\n",
+               current_mop, para_strerror(-ret));
+       activate_mood_or_playlist(NULL, &num_admissible);
 out:
-       if (ret2 >= 0 && pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+       para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
+               current_mop? current_mop : "dummy mood", num_admissible);
+       return ret;
 }
 
 int com_select(struct command_context *cc)
@@ -635,25 +645,19 @@ static int setup_command_socket_or_die(void)
 {
        int ret, socket_fd;
        char *socket_name = conf.afs_socket_arg;
-       struct sockaddr_un unix_addr;
 
        unlink(socket_name);
-       ret = create_local_socket(socket_name, &unix_addr,
-               S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
+       ret = create_local_socket(socket_name, 0);
        if (ret < 0) {
-               PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name);
-               exit(EXIT_FAILURE);
+               ret = create_local_socket(socket_name,
+                       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
+               if (ret < 0) {
+                       PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret),
+                               socket_name);
+                       exit(EXIT_FAILURE);
+               }
        }
        socket_fd = ret;
-       if (listen(socket_fd , 5) < 0) {
-               PARA_EMERG_LOG("can not listen on socket\n");
-               exit(EXIT_FAILURE);
-       }
-       ret = mark_fd_nonblocking(socket_fd);
-       if (ret < 0) {
-               close(socket_fd);
-               return ret;
-       }
        PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name,
                socket_fd);
        return socket_fd;
@@ -690,7 +694,7 @@ static int make_database_dir(void)
 
        get_database_dir();
        ret = para_mkdir(database_dir, 0777);
-       if (ret >= 0 || is_errno(-ret, EEXIST))
+       if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
                return 1;
        return ret;
 }
@@ -717,13 +721,7 @@ static int open_afs_tables(void)
        return ret;
 }
 
-static void signal_pre_select(struct sched *s, struct task *t)
-{
-       struct signal_task *st = container_of(t, struct signal_task, task);
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-}
-
-static int afs_signal_post_select(struct sched *s, __a_unused struct task *t)
+static int afs_signal_post_select(struct sched *s, __a_unused void *context)
 {
        int signum, ret;
 
@@ -751,24 +749,24 @@ shutdown:
 
 static void register_signal_task(struct sched *s)
 {
-       struct signal_task *st = &signal_task_struct;
-
        para_sigaction(SIGPIPE, SIG_IGN);
-       st->fd = para_signal_init();
-       PARA_INFO_LOG("signal pipe: fd %d\n", st->fd);
+       signal_task = signal_init_or_die();
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGHUP);
 
-       st->task.pre_select = signal_pre_select;
-       st->task.post_select = afs_signal_post_select;
-       sprintf(st->task.status, "signal task");
-       register_task(s, &st->task);
+       signal_task->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = afs_signal_post_select,
+               .context = signal_task,
+
+       }, s);
 }
 
 static struct list_head afs_client_list;
 
-/** Describes on connected afs client. */
+/** Describes one connected afs client. */
 struct afs_client {
        /** Position in the afs client list. */
        struct list_head node;
@@ -778,9 +776,9 @@ struct afs_client {
        struct timeval connect_time;
 };
 
-static void command_pre_select(struct sched *s, struct task *t)
+static void command_pre_select(struct sched *s, void *context)
 {
-       struct command_task *ct = container_of(t, struct command_task, task);
+       struct command_task *ct = context;
        struct afs_client *client;
 
        para_fd_set(server_socket, &s->rfds, &s->max_fileno);
@@ -807,14 +805,14 @@ static void command_pre_select(struct sched *s, struct task *t)
  * \return Zero if \a buf is \p NULL or \a size is zero. Negative on errors,
  * and positive on success.
  */
-int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size)
+int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size)
 {
        int ret, shmid;
        void *shm;
        struct callback_result *cr;
 
-       if (!buf || !size)
-               return 0;
+       if (size == 0)
+               assert(band != SBD_OUTPUT);
        ret = shm_new(size + sizeof(*cr));
        if (ret < 0)
                return ret;
@@ -825,7 +823,8 @@ int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size)
        cr = shm;
        cr->result_size = size;
        cr->band = band;
-       memcpy(shm + sizeof(*cr), buf, size);
+       if (size > 0)
+               memcpy(shm + sizeof(*cr), buf, size);
        ret = shm_detach(shm);
        if (ret < 0)
                goto err;
@@ -838,26 +837,43 @@ err:
        return ret;
 }
 
-/*
- * On errors, negative value is written to fd.
- * On success: If query produced a result, the result_shmid is written to fd.
- * Otherwise, zero is written.
- */
 static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
        struct callback_query *cq;
-       struct osl_object query;
-       int ret;
+       int ret, ret2;
+       struct afs_callback_arg aca = {.fd = fd};
 
        ret = shm_attach(query_shmid, ATTACH_RW, &query_shm);
        if (ret < 0)
                return ret;
        cq = query_shm;
-       query.data = (char *)query_shm + sizeof(*cq);
-       query.size = cq->query_size;
-       cq->handler(fd, &query);
-       return shm_detach(query_shm);
+       aca.query.data = (char *)query_shm + sizeof(*cq);
+       aca.query.size = cq->query_size;
+       aca.pbout.max_size = shm_get_shmmax();
+       aca.pbout.max_size_handler = afs_max_size_handler;
+       aca.pbout.private_data = &(struct afs_max_size_handler_data) {
+               .fd = fd,
+               .band = SBD_OUTPUT
+       };
+       ret = cq->handler(&aca);
+       ret2 = shm_detach(query_shm);
+       if (ret2 < 0) {
+               if (ret < 0) /* ignore (but log) detach error */
+                       PARA_ERROR_LOG("could not detach sma: %s\n",
+                               para_strerror(-ret2));
+               else
+                       ret = ret2;
+       }
+       flush_and_free_pb(&aca.pbout);
+       if (ret < 0) {
+               ret2 = pass_buffer_as_shm(fd, SBD_AFS_CB_FAILURE,
+                       (const char *)&ret, sizeof(ret));
+               if (ret2 < 0)
+                       PARA_ERROR_LOG("could not pass cb failure packet: %s\n",
+                               para_strerror(-ret));
+       }
+       return ret;
 }
 
 static int execute_server_command(fd_set *rfds)
@@ -915,14 +931,14 @@ err:
 /** Shutdown connection if query has not arrived until this many seconds. */
 #define AFS_CLIENT_TIMEOUT 3
 
-static int command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, void *context)
 {
-       struct command_task *ct = container_of(t, struct command_task, task);
+       struct command_task *ct = context;
        struct sockaddr_un unix_addr;
        struct afs_client *client, *tmp;
        int fd, ret;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
        ret = execute_server_command(&s->rfds);
@@ -970,10 +986,12 @@ static void register_command_task(uint32_t cookie, struct sched *s)
        ct->fd = setup_command_socket_or_die();
        ct->cookie = cookie;
 
-       ct->task.pre_select = command_pre_select;
-       ct->task.post_select = command_post_select;
-       sprintf(ct->task.status, "afs command task");
-       register_task(s, &ct->task);
+       ct->task = task_register(&(struct task_info) {
+               .name = "afs command",
+               .pre_select = command_pre_select,
+               .post_select = command_post_select,
+               .context = ct,
+       }, s);
 }
 
 /**
@@ -1005,6 +1023,7 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
        s.default_timeout.tv_sec = 0;
        s.default_timeout.tv_usec = 999 * 1000;
        ret = schedule(&s);
+       sched_shutdown(&s);
 out_close:
        close_afs_tables();
 out:
@@ -1013,11 +1032,10 @@ out:
        exit(EXIT_FAILURE);
 }
 
-static void create_tables_callback(int fd, const struct osl_object *query)
+static int com_init_callback(struct afs_callback_arg *aca)
 {
-       uint32_t table_mask = *(uint32_t *)query->data;
+       uint32_t table_mask = *(uint32_t *)aca->query.data;
        int i, ret;
-       struct para_buffer pb = {.buf = NULL};
 
        close_afs_tables();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
@@ -1028,17 +1046,19 @@ static void create_tables_callback(int fd, const struct osl_object *query)
                if (!t->create)
                        continue;
                ret = t->create(database_dir);
-               if (ret < 0)
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "cannot create table %s\n",
+                               t->name);
                        goto out;
-               para_printf(&pb, "successfully created %s table\n", t->name);
+               }
+               para_printf(&aca->pbout, "successfully created %s table\n",
+                       t->name);
        }
        ret = open_afs_tables();
-out:
        if (ret < 0)
-               para_printf(&pb, "%s\n", para_strerror(-ret));
-       if (pb.buf)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+               para_printf(&aca->pbout, "cannot open afs tables\n");
+out:
+       return ret;
 }
 
 int com_init(struct command_context *cc)
@@ -1066,12 +1086,8 @@ int com_init(struct command_context *cc)
                                return -E_BAD_TABLE_NAME;
                }
        }
-       ret = send_callback_request(create_tables_callback, &query,
+       return send_callback_request(com_init_callback, &query,
                afs_cb_result_handler, cc);
-       if (ret < 0 && !cc->use_sideband)
-               /* ignore return value */
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
-       return ret;
 }
 
 /**
@@ -1085,7 +1101,9 @@ enum com_check_flags {
        /** Check the mood table. */
        CHECK_MOODS = 2,
        /** Check the playlist table. */
-       CHECK_PLAYLISTS = 4
+       CHECK_PLAYLISTS = 4,
+       /** Check the attribute table against the audio file table. */
+       CHECK_ATTS = 8
 };
 
 int com_check(struct command_context *cc)
@@ -1105,6 +1123,10 @@ int com_check(struct command_context *cc)
                        flags |= CHECK_AFT;
                        continue;
                }
+               if (!strcmp(arg, "-A")) {
+                       flags |= CHECK_ATTS;
+                       continue;
+               }
                if (!strcmp(arg, "-p")) {
                        flags |= CHECK_PLAYLISTS;
                        continue;
@@ -1125,6 +1147,12 @@ int com_check(struct command_context *cc)
                if (ret < 0)
                        return ret;
        }
+       if (flags & CHECK_ATTS) {
+               ret = send_callback_request(attribute_check_callback, NULL,
+                       afs_cb_result_handler, cc);
+               if (ret < 0)
+                       return ret;
+       }
        if (flags & CHECK_PLAYLISTS) {
                ret = send_callback_request(playlist_check_callback,
                        NULL, afs_cb_result_handler, cc);
@@ -1147,10 +1175,14 @@ int com_check(struct command_context *cc)
  * \param pb May be \p NULL.
  * \param data Type depends on \a event.
  *
- * This function calls the table handlers of all tables and passes \a pb and \a
- * data verbatim. It's up to the handlers to interpret the \a data pointer.
+ * This function calls each table event handler, passing \a pb and \a data
+ * verbatim. It's up to the handlers to interpret the \a data pointer. If a
+ * handler returns negative, the loop is aborted.
+ *
+ * \return The (negative) error code of the first handler that failed, or non-negative
+ * if all handlers succeeded.
  */
-void afs_event(enum afs_events event, struct para_buffer *pb,
+__must_check int afs_event(enum afs_events event, struct para_buffer *pb,
                void *data)
 {
        int i, ret;
@@ -1160,10 +1192,13 @@ void afs_event(enum afs_events event, struct para_buffer *pb,
                if (!t->event_handler)
                        continue;
                ret = t->event_handler(event, pb, data);
-               if (ret < 0)
+               if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %d: %s\n", t->name,
                                event, para_strerror(-ret));
+                       return ret;
+               }
        }
+       return 1;
 }
 
 /**
diff --git a/afs.cmd b/afs.cmd
index 15459194cb7a4f0289890f3b6919eca1f33136c0..584ba809235c825cb9e1a3afdadd0644eea4d2c5 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -5,12 +5,11 @@ TM: mood lyr img pl
 ---
 N: add
 P: AFS_READ | AFS_WRITE
-D: Add new audio files to the database.
-U: add [-l] [-f] [-v] path...
-H: Each given path may be either the full path to an audio
-H: file, or the full path of a directory. In case of a
-H: directory, all audio files in that directory are added
-H: recursively. Only absolute paths are accepted.
+D: Add or update audio files.
+U: add [-a] [-l] [-f] [-v] path...
+H: Each path must be absolute and refer to either an audio file, or a
+H: directory. In case of a directory, all audio files in that directory
+H: are added recursively. Only absolute paths are accepted.
 H:
 H: Options:
 H:
@@ -18,11 +17,11 @@ H: -a       Add all files. The default is to add only files ending in a
 H:     known suffix for a supported audio format.
 H:
 H: -l  Add files lazily. If the path already exists in the
-H:     database, skip this file.  This operation is really cheap.
-H:     Use it when adding large directories if only a few files
-H:     where added.
+H:     database, skip this file.  This operation is really cheap. Useful
+H:     to update large directories after some files have been added or
+H:     deleted.
 H:
-H:-f   Force adding/updating. Recompute the audio format handler data
+H: -f  Force adding/updating. Recompute the audio format handler data
 H:     even if a file with the same path and the same hash value exists.
 H:
 H: -v  Verbose mode. Print what is being done.
@@ -37,26 +36,22 @@ H: only the tables given by table_name... are created.
 N: ls
 P: AFS_READ
 D: List audio files.
-U: ls [-l[s|l|v|m]] [-p] [-a] [-r] [-d] [-s{p|s|l|n|f|c|i|y|b|d|a}] [pattern...]
+U: ls [-l=mode] [-p] [-a] [-r] [-d] [-s=order] [pattern...]
 H: Print a list of all audio files matching pattern.
 H:
 H: Options:
 H:
-H: -l  Change listing mode. Defaults to short listing if not given.
+H: -l=mode     Change listing mode. Defaults to short listing if not given.
 H:
-H:             -ls:   short listing mode
+H:    Available modes:
+H:    s: short listing mode
+H:    l: long listing mode (equivalent to -l)
+H:    v: verbose listing mode
+H:    p: parser-friendly mode
+H:    m: mbox listing mode
+H:    c: chunk-table listing mode
 H:
-H:             -ll:   long listing mode (equivalent to -l)
-H:
-H:             -lv:   verbose listing mode
-H:
-H:             -lp:   parser-friendly mode
-H:
-H:             -lm:   mbox listing mode
-H:
-H:             -lc:   chunk-table listing mode
-H:
-H: -p  List full path of audio file. If not specified, only the basename
+H: -p  List full paths. If this option is not specified, only the basename
 H:     of each file is printed.
 H:
 H: -a  List only files that are admissible with respect to the current mood or
@@ -66,37 +61,27 @@ H: -r       Reverse sort order.
 H:
 H: -d  Print dates as seconds after the epoch.
 H:
-H: -s  Change sort order. Defaults to alphabetical path sort if not given.
-H:
-H:             -sp:  sort by path.
-H:
-H:             -sl:  sort by last played time.
-H:
-H:             -ss:  sort by score (implies -a).
-H:
-H:             -sn:  sort by num played count.
-H:
-H:             -sf:  sort by frequency.
-H:
-H:             -sc:  sort by number of channels.
-H:
-H:             -si:  sort by image id.
-H:
-H:             -sy:  sort by lyrics id.
-H:
-H:             -sb:  sort by bit rate.
-H:
-H:             -sd:  sort by duration.
-H:
-H:             -sa:  sort by audio format.
+H: -s=order    Change sort order. Defaults to alphabetical path sort if not given.
+H:
+H:   Possible values for order:
+H:   p: by path
+H:   l: by last played time
+H:   s: by score (implies -a)
+H:   n: by num played count
+H:   f: by frequency
+H:   c: by number of channels
+H:   i: by image id
+H:   y: by lyrics id
+H:   b: by bit rate
+H:   d: by duration
+H:   a: by audio format
 ---
 N: lsatt
 P: AFS_READ
 D: List attributes.
 U: lsatt [-i] [-l] [-r] [pattern]
-H: Print the list of all defined attributes which match the
-H: given pattern. If no pattern is given, the full list is
-H: printed.
+H: Print the list of all defined attributes which match the given
+H: pattern. If no pattern is given, the full list is printed.
 H:
 H: Options:
 H:
@@ -114,9 +99,9 @@ U: setatt attribute{+|-}... pattern
 H: Set ('+') or unset ('-') the given attributes for all audio files matching
 H: pattern.  Example:
 H:
-H:         setatt rock+ punk+ classic- '*foo.mp3'
+H:         setatt rock+ punk+ pop- '*foo.mp3'
 H:
-H: sets the 'rock' and the 'punk' attribute but unsets the 'classic'
+H: sets the 'rock' and the 'punk' attribute and unsets the 'pop'
 H: attribute of all files ending with 'foo.mp3'.
 ---
 N: addatt
@@ -135,9 +120,9 @@ H: Rename attribute old to new.
 N: check
 P: AFS_READ
 D: Run integrity checks against osl tables.
-U: check [-a] [-m] [-p]
-H: Check the audio file table, the mood definitions and all
-H: defined playlists and report any inconsistencies found.
+U: check [-a] [-A] [-m] [-p]
+H: Check the audio file table, the attribute table, the mood definitions
+H: and all defined playlists. Report any inconsistencies.
 H:
 H: Options:
 H:
@@ -146,12 +131,17 @@ H:        table which are not present in the file system. Moreover, it checks
 H:     whether the lyrics id and all entries in the audio file table are
 H:     valid.
 H:
+H: -A  Check the attribute table against the afs attribute bitmask of
+H:     each audio file in the audio file table. Reports audio files
+H:     whose attribute bitmask is invalid, i.e., has a bit set which
+H:     does not correspond to any attribute of the attribute table.
+H:
 H: -m  Run syntax checks on all defined moods in the mood table.
 H:
 H: -p  Check all playlists for lines that correspond to files not contained
 H:     in the audio file table.
 H:
-H: If called without arguments, all three checks are run.
+H: If called without arguments, all checks are run.
 ---
 N: rmatt
 P: AFS_READ | AFS_WRITE
@@ -164,9 +154,10 @@ N: rm
 P: AFS_READ | AFS_WRITE
 D: Remove entries from the audio file table.
 U: rm [-v] [-f] [-p] pattern...
-H: Delete all entries in the audio file table that match any given pattern.
-H: Note that affects the table entries only; paraslash won't touch your
-H: audio files in any way.
+H: Delete all entries in the audio file table that match any given pattern.  Note
+H: that this affects the table entries only; the command won't touch your audio
+H: files on disk.
+H:
 H: Options:
 H:
 H: -v  Verbose mode. Explain what is being done.
@@ -181,28 +172,29 @@ H:        a slash (see fnmatch(3)).
 ---
 N: touch
 P: AFS_READ | AFS_WRITE
-D: Manipulate the afs data for all audio files matching a pattern.
+D: Manipulate the afs entry of audio files.
 U: touch [-n=numplayed] [-l=lastplayed] [-y=lyrics_id] [-i=image_id] [-a=amp] [-v] [-p] pattern
-H: If no option is given, lastplayed is set to the current time
-H: and numplayed is increased by one. Otherwise, only the given
-H: options are taken into account.
+H: If no option is given, the lastplayed field is set to the current time
+H: and the value of the numplayed field is increased by one. Otherwise,
+H: only the given options are taken into account.
 H:
 H: Options:
 H:
-H: -n  Set numplayed count. The number of times afs has selected this
-H:     audio file for streaming.
+H: -n  Set the numplayed count, i.e. the number of times this audio
+H:     file was selected for streaming so far.
 H:
-H: -l  Set lastplayed time. The last time this audio file was selected.
-H:     Must be given as the number of seconds since the epoch. Example:
+H: -l  Set the lastplayed time, i.e. the last time this audio file was
+H:     selected for streaming. The argument must be a number of seconds
+H:     since the epoch. Example:
 H:
 H:             touch -l=$(date +%s) file
 H:
 H:     sets the lastplayed time of 'file' to the current time.
 H:
-H: -y  Set the lyrics id. Specify the lyrics data file associated with
-H:     this audio file.
+H: -y  Set the lyrics ID which specifies the lyrics data file associated
+H:     with the audio file.
 H:
-H: -i  Set the image id. Same as -y, but sets the image.
+H: -i  Like -y, but sets the image ID.
 H:
 H: -a  Set the amplification value (0-255). This determines a scaling
 H:     factor by which the amplitude should be multiplied in order to
@@ -223,10 +215,9 @@ N: cpsi
 P: AFS_READ | AFS_WRITE
 D: Copy audio file selector info.
 U: cpsi [-a] [-y] [-i] [-l] [-n] [-v] source pattern...
-H: If no option, or only the -v option is given, all fields of
-H: the audio file selector info are copied to all files
-H: matching pattern.  Otherwise, only the given options are
-H: taken into account.
+H: If no option, or only the -v option is given, all fields of the
+H: audio file selector info are copied to all files matching pattern.
+H: Otherwise, only the given options are taken into account.
 H:
 H: Options:
 H:
@@ -257,14 +248,11 @@ T: add
 N: add@member@
 O: int com_add@member@(struct command_context *cc);
 P: AFS_READ | AFS_WRITE
-D: Read data from stdin and add it as a blob to the @member@ table.
+D: Add stdin as a blob to the @member@ table.
 U: add@member@ @member@_name
-H: Read arbitrary binary data from stdin and send that data to
-H: the audio file selector process which creates a new blob for
-H: the data in the corresponding osl table.
-H:
-H: The names of the blobs of a table are unique. If an entry with the
-H: given name already exists, its contents are replaced by the new data.
+H: Read from stdin and ask the audio file selector to create a blob in the
+H: corresponding osl table. If the named blob already exists, it gets replaced
+H: with the new data.
 ---
 T: cat
 N: cat@member@
@@ -272,9 +260,7 @@ O: int com_cat@member@(struct command_context *cc);
 P: AFS_READ
 D: Dump the contents of a blob of type @member@ to stdout.
 U: cat@member@ @member@_name
-H: This command may be used to retrieve the blob identified by
-H: the given name from the corresponding osl table to which
-H: they were previously added.
+H: Retrieve the named blob and write it to stdout.
 ---
 T: ls
 N: ls@member@
@@ -282,9 +268,8 @@ O: int com_ls@member@(struct command_context *cc);
 P: AFS_READ
 D: List blobs of type @member@ which match a pattern.
 U: ls@member@ [-i] [-l] [-r] [pattern]
-H: Print a list of the names of all blobs in the corresponding
-H: osl table which match the given pattern. If no pattern is
-H: given, the full list is printed.
+H: Print the list of all blobs which match the given pattern. If no
+H: pattern is given, the full list is printed.
 H:
 H: Options:
 H:
@@ -300,13 +285,14 @@ O: int com_rm@member@(struct command_context *cc);
 P: AFS_READ | AFS_WRITE
 D: Remove blob(s) of type @member@ from the @member@ table.
 U: rm@member@ pattern...
-H: Remove all blobs from the corresponding table which match
-H: any given pattern.
+H: Remove all blobs whose name matches any of the given patterns.
 ---
 T: mv
 N: mv@member@
 O: int com_mv@member@(struct command_context *cc);
 P: AFS_READ | AFS_WRITE
 D: Rename a blob of type @member@.
-U: mv@member@ old_@member@_name new_@member@_name
-H: Rename the blob identified by the first name as the second name.
+U: mv@member@ source_@member@_name dest_@member@_name
+H: Rename the blob identified by the source blob name to the destination blob
+H: name. The command fails if the source does not exist, or if the destination
+H: already exists.
diff --git a/afs.h b/afs.h
index b415bfec6c2c7aa6d8fcb0f12308c7e8d30b7025..25ff421d8b86d0f5708d4b4f3749f310f9c4a877 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -1,13 +1,11 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file afs.h Exported symbols of the audio file selector. */
 
-#include <regex.h>
-
 /** Audio file selector data stored in the audio file table. */
 struct afs_info {
        /** Seconds since the epoch. */
@@ -20,7 +18,7 @@ struct afs_info {
        uint32_t image_id;
        /** Lyrics blob associated with this file (foreign key). */
        uint32_t lyrics_id;
-       /** Mp3, ogg, aac, wma, spx. */
+       /** Mp3, ogg, ... */
        uint8_t audio_format_id;
        /** Amplification value. */
        uint8_t amp;
@@ -104,42 +102,12 @@ enum play_mode {
        PLAY_MODE_PLAYLIST,
 };
 
-/**
- * Data about one audio file.
- *
- * Needed to produce ls and stat output.
- */
-struct ls_data {
-       /** Usual audio format handler information. */
-       struct afh_info afhi;
-       /** Audio file selector information. */
-       struct afs_info afsi;
-       /** The full path of the audio file. */
-       char *path;
-       /** The score value (if -a was given). */
-       long score;
-       /** The hash value of audio file data. */
-       unsigned char *hash;
-};
-
-/** Data about the current audio file, passed from afs to server. */
-struct audio_file_data {
-       /** The open file descriptor to the current audio file. */
-       int fd;
-       /** Vss needs this for streaming. */
-       struct afh_info afhi;
-       /** Size of the largest chunk. */
-       uint32_t max_chunk_size;
-       /** Needed to get the audio file header. */
-       uint8_t audio_format_id;
-};
-
 /**
  * Codes used for communication between the server and the afs process.
  *
  * Before forking the afs child, para_server creates a bidirectional pipe
  * through which both processes communicate. Usually para_server requests a new
- * audio in order to start streaming or when the end of the current audio file
+ * audio file in order to start streaming or when the end of the current audio file
  * has been reached.  The afs process responds to such a request by sending
  * back an eight byte buffer. The first four bytes is the uint32_t
  * representation of the code, usually \p NEXT_AUDIO_FILE if an admissible
@@ -187,6 +155,15 @@ struct pattern_match_data {
        int (*action)(struct osl_table *table, struct osl_row *row, const char *name, void *data);
 };
 
+/** Arguments passed to each afs callback. */
+struct afs_callback_arg {
+       /** The local socket connecting afs and the command handler. */
+       int fd;
+       /** Callback-specific data. */
+       struct osl_object query;
+       /** Will be written on band SBD_OUTPUT, fully buffered. */
+       struct para_buffer pbout;
+};
 
 /**
  * Afs command handlers run as a process which is not related to the afs
@@ -196,7 +173,7 @@ struct pattern_match_data {
  *
  * \sa send_callback_request().
  */
-typedef void callback_function(int fd, const struct osl_object *);
+typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
  * Callbacks send chunks to data back to the command handler. Pointers to
@@ -207,7 +184,7 @@ typedef void callback_function(int fd, const struct osl_object *);
  */
 typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
 int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
-int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size);
+int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size);
 
 /** Structure passed to the AFS max_size handler. */
 struct afs_max_size_handler_data {
@@ -239,17 +216,17 @@ _static_inline_ int afs_max_size_handler(char *buf, size_t size, void *private)
 }
 
 __noreturn void afs_init(uint32_t cookie, int socket_fd);
-void afs_event(enum afs_events event, struct para_buffer *pb,
+__must_check int afs_event(enum afs_events event, struct para_buffer *pb,
        void *data);
-int send_callback_request(callback_function *f, struct osl_object *query,
+int send_callback_request(afs_callback *f, struct osl_object *query,
                callback_result_handler *result_handler,
                void *private_result_data);
 int send_option_arg_callback_request(struct osl_object *options,
-               int argc,  char * const * const argv, callback_function *f,
+               int argc,  char * const * const argv, afs_callback *f,
                callback_result_handler *result_handler,
                void *private_result_data);
 int send_standard_callback_request(int argc,  char * const * const argv,
-               callback_function *f, callback_result_handler *result_handler,
+               afs_callback *f, callback_result_handler *result_handler,
                void *private_result_data);
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
 int for_each_matching_row(struct pattern_match_data *pmd);
@@ -257,7 +234,6 @@ int for_each_matching_row(struct pattern_match_data *pmd);
 /* score */
 void score_init(struct afs_table *t);
 int admissible_file_loop(void *data, osl_rbtree_loop_func *func);
-int admissible_file_loop_reverse(void *data, osl_rbtree_loop_func *func);
 int score_get_best(struct osl_row **aft_row, long *score);
 int get_score_and_aft_row(struct osl_row *score_row, long *score, struct osl_row **aft_row);
 int score_add(const struct osl_row *row, long score);
@@ -272,27 +248,24 @@ void attribute_init(struct afs_table *t);
 void get_attribute_bitmap(const uint64_t *atts, char *buf); /* needed by com_ls() */
 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum);
 int get_attribute_text(uint64_t *atts, const char *delim, char **text);
+int attribute_check_callback(struct afs_callback_arg *aca);
 
 /* aft */
 void aft_init(struct afs_table *t);
 int aft_get_row_of_path(const char *path, struct osl_row **row);
-int open_and_update_audio_file(struct osl_row *aft_row, long score,
-       struct audio_file_data *afd);
+int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
+int open_and_update_audio_file(struct audio_file_data *afd);
 int load_afd(int shmid, struct audio_file_data *afd);
-int load_afsi(struct afs_info *afsi, struct osl_object *obj);
-void save_afsi(struct afs_info *afsi, struct osl_object *obj);
 int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi);
 int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi);
-int get_afsi_of_path(const char *path, struct afs_info *afsi);
 int get_audio_file_path_of_row(const struct osl_row *row, char **path);
-int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj);
 int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
-void aft_check_callback(int fd, __a_unused const struct osl_object *query);
+int aft_check_callback(struct afs_callback_arg *aca);
 
 /* playlist */
 int playlist_open(char *name);
 void playlist_close(void);
-void playlist_check_callback(int fd, __a_unused const struct osl_object *query);
+int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
 #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
diff --git a/aft.c b/aft.c
index 301da7c7687ab1a461ec6e157ba44717f21135f0..261054df8fc4012236b42246158b87b77ce1623c 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -72,6 +72,20 @@ enum ls_listing_mode {
        LS_MODE_PARSER,
 };
 
+/* Data about one audio file. Needed for ls and stat output. */
+struct ls_data {
+       /* Usual audio format handler information. */
+       struct afh_info afhi;
+       /* Audio file selector information. */
+       struct afs_info afsi;
+       /* The full path of the audio file. */
+       char *path;
+       /* The score value (if -a was given). */
+       long score;
+       /* The hash value of the audio file data. */
+       unsigned char *hash;
+};
+
 /** The flags accepted by the ls command. */
 enum ls_flags {
        /** -p */
@@ -87,10 +101,9 @@ enum ls_flags {
 /**
  * The size of the individual output fields of the ls command.
  *
- * These depend on the actual content being listed. If, for instance only files
- * with duration less than an hour are being listed, then the duration with is
- * made smaller because then the duration is listed as mm:ss rather than
- * hh:mm:ss.
+ * These depend on the content being listed. For example, if each listed file
+ * is shorter than an hour, the duration format is set to mm:ss. Otherwise it
+ * is set to hh:mm:ss.
  */
 struct ls_widths {
        /** size of the score field. */
@@ -163,7 +176,7 @@ enum afsi_offsets {
        AFSI_SIZE = 32
 };
 
-/**
+/*
  * Convert a struct afs_info to an osl object.
  *
  * \param afsi Pointer to the audio file info to be converted.
@@ -171,7 +184,7 @@ enum afsi_offsets {
  *
  * \sa load_afsi().
  */
-void save_afsi(struct afs_info *afsi, struct osl_object *obj)
+static void save_afsi(struct afs_info *afsi, struct osl_object *obj)
 {
        char *buf = obj->data;
 
@@ -186,17 +199,17 @@ void save_afsi(struct afs_info *afsi, struct osl_object *obj)
        memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 2);
 }
 
-/**
- *  Get the audio file selector info struct stored in an osl object.
+/*
+ * Get the audio file selector info struct stored in an osl object.
  *
  * \param afsi Points to the audio_file info structure to be filled in.
  * \param obj The osl object holding the data.
  *
- * \return Positive on success, negative on errors. Possible errors: \p E_BAD_AFS.
+ * \return Standard.
  *
  * \sa save_afsi().
  */
-int load_afsi(struct afs_info *afsi, struct osl_object *obj)
+static int load_afsi(struct afs_info *afsi, struct osl_object *obj)
 {
        char *buf = obj->data;
        if (obj->size < AFSI_SIZE)
@@ -228,19 +241,12 @@ enum audio_file_table_columns {
        NUM_AFT_COLUMNS
 };
 
-/**
- * Compare two osl objects pointing to hash values.
- *
- * \param obj1 Pointer to the first hash object.
- * \param obj2 Pointer to the second hash object.
- *
- * \return The values required for an osl compare function.
- *
- * \sa osl_compare_func, uint32_compare().
- */
-static int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+/* compare function for the hash column */
+static int aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
 {
-       return hash_compare((unsigned char *)obj1->data, (unsigned char *)obj2->data);
+       return hash_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
 }
 
 static struct osl_column_description aft_cols[] = {
@@ -280,60 +286,36 @@ static struct osl_table_description audio_file_table_desc = {
        .column_descriptions = aft_cols
 };
 
-/* We don't want dot or dot-dot anywhere. */
-static int verify_dotfile(const char *rest)
-{
-       /*
-        * The first character was '.', but that has already been discarded, we
-        * now test the rest.
-        */
-       switch (*rest) {
-       case '\0': case '/': /* /foo/. and /foo/./bar are not ok */
-               return -1;
-       case '.': /* path start with /foo/.. */
-               if (rest[1] == '\0' || rest[1] == '/')
-                       return -1; /* /foo/.. or /foo/../bar are not ok */
-               /* /foo/..bar is ok */
-       }
-       return 1;
-}
-
 /*
- * We fundamentally don't like some paths: We don't want double slashes or
- * slashes at the end that can make pathnames ambiguous.
+ * Produce a canonicalized absolute pathname.
+ *
+ * Returns one if the resolved path a directory, zero if it is a regular file,
+ * negative on errors.
  */
 static int verify_path(const char *orig_path, char **resolved_path)
 {
-       char c;
-       size_t len;
-       char *path;
+       int ret;
+       char *path = NULL;
+       struct stat statbuf;
 
        if (*orig_path != '/') /* we only accept absolute paths */
-               return -E_BAD_PATH;
-       len = strlen(orig_path);
-       *resolved_path = para_strdup(orig_path);
-       path = *resolved_path;
-       while (len > 1 && path[--len] == '/')
-               path[len] = '\0'; /* remove slash at the end */
-       c = *path++;
-       while (c) {
-               if (c == '/') {
-                       c = *path++;
-                       switch (c) {
-                       case '/': /* double slash */
-                               goto bad_path;
-                       case '.':
-                               if (verify_dotfile(path) < 0)
-                                       goto bad_path;
-                       default:
-                               continue;
-                       }
-               }
-               c = *path++;
-       }
-       return 1;
-bad_path:
-       free(*resolved_path);
+               goto fail;
+       path = realpath(orig_path, NULL);
+       if (!path)
+               goto fail;
+       if (stat(path, &statbuf) < 0)
+               goto fail;
+       if (S_ISREG(statbuf.st_mode))
+               ret = 0;
+       else if (S_ISDIR(statbuf.st_mode))
+               ret = 1;
+       else
+               goto fail;
+       *resolved_path = path;
+       return ret;
+fail:
+       *resolved_path = NULL;
+       free(path);
        return -E_BAD_PATH;
 }
 
@@ -493,15 +475,16 @@ static int aft_get_row_of_hash(unsigned char *hash, struct osl_row **row)
        return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row));
 }
 
-/**
- * Get the osl object holding the audio file selector info of a row.
+/*
+ * Get the audio file selector info object of a row.
  *
  * \param row Pointer to a row in the audio file table.
  * \param obj Result pointer.
  *
  * \return Standard.
  */
-int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj)
+static int get_afsi_object_of_row(const struct osl_row *row,
+               struct osl_object *obj)
 {
        return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj));
 }
@@ -541,15 +524,15 @@ int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi)
        return load_afsi(afsi, &obj);
 }
 
-/**
+/*
  * Get the audio file selector info, given the path of an audio table.
  *
  * \param path The full path of the audio file.
  * \param afsi Result pointer.
  *
- * \return Positive on success, negative on errors.
+ * \return Standard.
  */
-int get_afsi_of_path(const char *path, struct afs_info *afsi)
+static int get_afsi_of_path(const char *path, struct afs_info *afsi)
 {
        struct osl_object obj;
        int ret = get_afsi_object_of_path(path, &obj);
@@ -679,16 +662,13 @@ err:
 int load_afd(int shmid, struct audio_file_data *afd)
 {
        void *shm_afd;
-       char *buf;
        int ret;
 
        ret = shm_attach(shmid, ATTACH_RO, &shm_afd);
        if (ret < 0)
                return ret;
        *afd = *(struct audio_file_data *)shm_afd;
-       buf = shm_afd;
-       buf += sizeof(*afd);
-       load_chunk_table(&afd->afhi, buf);
+       load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd));
        shm_detach(shm_afd);
        return 1;
 }
@@ -696,21 +676,27 @@ int load_afd(int shmid, struct audio_file_data *afd)
 static int get_local_time(uint64_t *seconds, char *buf, size_t size,
        time_t current_time, enum ls_listing_mode lm)
 {
-       struct tm t;
+       struct tm *tm;
+       /*
+        * Omit year but show time if the given value is closer to the current
+        * time than this many seconds.
+        */
+       const time_t m = 6 * 30 * 24 * 3600; /* six months */
 
-       if (!localtime_r((time_t *)seconds, &t))
+       tm = localtime((time_t *)seconds);
+       if (!tm)
                return -E_LOCALTIME;
        if (lm == LS_MODE_MBOX) {
-               if (!strftime(buf, size, "%c", &t))
+               if (!strftime(buf, size, "%c", tm))
                        return -E_STRFTIME;
                return 1;
        }
-       if (*seconds + 6 * 30 * 24 * 3600 > current_time) {
-               if (!strftime(buf, size, "%b %e %k:%M", &t))
+       if (*seconds > current_time - m && *seconds < current_time + m) {
+               if (!strftime(buf, size, "%b %e %k:%M", tm))
                        return -E_STRFTIME;
                return 1;
        }
-       if (!strftime(buf, size, "%b %e  %Y", &t))
+       if (!strftime(buf, size, "%b %e %Y", tm))
                return -E_STRFTIME;
        return 1;
 }
@@ -755,69 +741,56 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts)
        }
 }
 
-
 static int write_attribute_items(struct para_buffer *b,
                const char *att_bitmap, struct afs_info *afsi)
 {
        char *att_text;
        int ret;
 
-       ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap);
-       if (ret < 0)
-               return ret;
+       WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap);
        ret = get_attribute_text(&afsi->attributes, " ", &att_text);
        if (ret < 0)
                return ret;
-       ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text);
+       WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text);
        free(att_text);
        return ret;
 }
 
-static int write_lyrics_items(struct para_buffer *b, struct afs_info *afsi)
+static void write_lyrics_items(struct para_buffer *b, struct afs_info *afsi)
 {
        char *lyrics_name;
-       int ret;
 
-       ret = WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id);
-       if (ret < 0)
-               return ret;
+       WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id);
        lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name);
-       return WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name?
+       WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name?
                lyrics_name : "(none)");
 }
 
-static int write_image_items(struct para_buffer *b, struct afs_info *afsi)
+static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
 {
        char *image_name;
-       int ret;
 
-       ret = WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id);
-       if (ret < 0)
-               return ret;
+       WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id);
        img_get_name_by_id(afsi->image_id, &image_name);
-       return WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name?
+       WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name?
                image_name : "(none)");
 }
 
-static int write_filename_items(struct para_buffer *b, const char *path,
+static void write_filename_items(struct para_buffer *b, const char *path,
                unsigned flags)
 {
        char *val;
-       int ret;
 
-       if (!(flags & LS_FLAG_FULL_PATH))
-               return WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
-       ret = WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path);
-       if (ret < 0)
-               return ret;
+       if (!(flags & LS_FLAG_FULL_PATH)) {
+               WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
+               return;
+       }
+       WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path);
        val = para_basename(path);
-       ret = WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : "");
-       if (ret < 0)
-               return ret;
+       WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : "");
        val = para_dirname(path);
-       ret = WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : "");
+       WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : "");
        free(val);
-       return ret;
 }
 
 static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
@@ -834,32 +807,27 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0)
                return ret;
-       ret = para_printf(b, "%s\n"
+       para_printf(b, "%s\n"
                "chunk_time: %lu:%lu\nchunk_offsets: ",
                d->path,
                (long unsigned) d->afhi.chunk_tv.tv_sec,
                (long unsigned) d->afhi.chunk_tv.tv_usec
        );
-       if (ret < 0)
-               goto out;
        buf = chunk_table_obj.data;
-       for (i = 0; i <= d->afhi.chunks_total; i++) {
-               ret = para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
-               if (ret < 0)
-                       goto out;
-       }
-       ret = para_printf(b, "\n");
-out:
+       for (i = 0; i <= d->afhi.chunks_total; i++)
+               para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
+       para_printf(b, "\n");
+       ret = 1;
        osl_close_disk_object(&chunk_table_obj);
        return ret;
 }
 
-static int write_score(struct para_buffer *b, struct ls_data *d,
+static void write_score(struct para_buffer *b, struct ls_data *d,
                struct ls_options *opts)
 {
        if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/
-               return 0;
-       return WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
+               return;
+       WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
 }
 
 static int print_list_item(struct ls_data *d, struct ls_options *opts,
@@ -874,7 +842,8 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        char asc_hash[2 * HASH_SIZE + 1];
 
        if (opts->mode == LS_MODE_SHORT) {
-               ret = para_printf(b, "%s\n", d->path);
+               para_printf(b, "%s\n", d->path);
+               ret = 1;
                goto out;
        }
        if (opts->mode == LS_MODE_CHUNKS) {
@@ -895,12 +864,10 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        if (opts->mode == LS_MODE_LONG) {
                struct ls_widths *w = &opts->widths;
                if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) {
-                       ret = para_printf(b, "%*li ",
-                               opts->widths.score_width, d->score);
-                       if (ret < 0)
-                               goto out;
+                       para_printf(b, "%*li ", opts->widths.score_width,
+                               d->score);
                }
-               ret = para_printf(b,
+               para_printf(b,
                        "%s "   /* attributes */
                        "%*u "  /* amp */
                        "%*d "  /* image_id  */
@@ -927,98 +894,52 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        last_played_time,
                        d->path
                );
+               ret = 1;
                goto out;
        }
        if (opts->mode == LS_MODE_MBOX) {
                const char *bn = para_basename(d->path);
-               ret = para_printf(b,
+               para_printf(b,
                        "From foo@localhost %s\n"
                        "Received: from\nTo: bar\nFrom: a\n"
                        "Subject: %s\n\n",
                        last_played_time,
                        bn? bn : "?");
-               if (ret < 0)
-                       goto out;
        }
-       ret = write_filename_items(b, d->path, opts->flags);
-       if (ret < 0)
-               goto out;
-       ret = write_score(b, d, opts);
-       if (ret < 0)
-               goto out;
+       write_filename_items(b, d->path, opts->flags);
+       write_score(b, d, opts);
        ret = write_attribute_items(b, att_buf, afsi);
        if (ret < 0)
                goto out;
-       ret = write_image_items(b, afsi);
-       if (ret < 0)
-               goto out;
-       ret = write_lyrics_items(b, afsi);
-       if (ret < 0)
-               goto out;
+       write_image_items(b, afsi);
+       write_lyrics_items(b, afsi);
        hash_to_asc(d->hash, asc_hash);
-       ret = WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n",
+       WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash);
+       WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate);
+       WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n",
                audio_format_name(afsi->audio_format_id));
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n",
+       WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
+       WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
+       WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
+       WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n",
                afhi->seconds_total);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n",
-               tv2ms(&afhi->chunk_tv));
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n",
+       WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
+       WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
+       WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
+       WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
+       WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
                afhi->chunks_total);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album);
-       if (ret < 0)
-               goto out;
-       ret = WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment);
-       if (ret < 0)
-               goto out;
+       WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
+       WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
+       WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
+       WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year);
+       WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album);
+       WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment);
        if (opts->mode == LS_MODE_MBOX) {
                struct osl_object lyrics_def;
                lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def);
                if (lyrics_def.data) {
-                       ret = para_printf(b, "Lyrics:\n~~~~~~~\n%s",
+                       para_printf(b, "Lyrics:\n~~~~~~~\n%s",
                                (char *)lyrics_def.data);
                        osl_close_disk_object(&lyrics_def);
                }
@@ -1027,17 +948,32 @@ out:
        return ret;
 }
 
-static int make_status_items(struct audio_file_data *afd,
-               struct afs_info *afsi, char *path, long score,
-               unsigned char *hash)
+static struct ls_data status_item_ls_data;
+static struct osl_row *current_aft_row;
+
+static void make_inode_status_items(struct para_buffer *pb)
+{
+       struct stat statbuf = {.st_size = 0};
+       char *path, mtime_str[30] = "\0";
+       struct tm mtime_tm;
+       int ret;
+
+       ret = get_audio_file_path_of_row(current_aft_row, &path);
+       if (ret < 0)
+               goto out;
+       ret = stat(path, &statbuf);
+       if (ret < 0)
+               goto out;
+       localtime_r(&statbuf.st_mtime, &mtime_tm);
+       ret = strftime(mtime_str, 29, "%b %d %Y", &mtime_tm);
+       assert(ret > 0); /* number of bytes placed in mtime_str */
+out:
+       WRITE_STATUS_ITEM(pb, SI_MTIME, "%s\n", mtime_str);
+       WRITE_STATUS_ITEM(pb, SI_FILE_SIZE, "%ld\n", statbuf.st_size / 1024);
+}
+
+static int make_status_items(void)
 {
-       struct ls_data d = {
-               .afhi = afd->afhi,
-               .afsi = *afsi,
-               .path = path,
-               .score = score,
-               .hash = hash
-       };
        struct ls_options opts = {
                .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
                .mode = LS_MODE_VERBOSE,
@@ -1047,30 +983,30 @@ static int make_status_items(struct audio_file_data *afd,
        int ret;
 
        time(&current_time);
-       ret = print_list_item(&d, &opts, &pb, current_time);
+       ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
        if (ret < 0)
                return ret;
+       make_inode_status_items(&pb);
        free(status_items);
        status_items = pb.buf;
        memset(&pb, 0, sizeof(pb));
        pb.max_size = shm_get_shmmax() - 1;
        pb.flags = PBF_SIZE_PREFIX;
-       ret = print_list_item(&d, &opts, &pb, current_time);
+       ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
        if (ret < 0) {
                free(status_items);
                status_items = NULL;
                return ret;
        }
+       make_inode_status_items(&pb);
        free(parser_friendly_status_items);
        parser_friendly_status_items = pb.buf;
        return 1;
 }
 
 /**
- * Mmap the given audio file and update statistics.
+ * Open the audio file with highest score and set up an afd structure.
  *
- * \param aft_row Determines the audio file to be opened and updated.
- * \param score The score of the audio file.
  * \param afd Result pointer.
  *
  * On success, the numplayed field of the audio file selector info is increased
@@ -1079,67 +1015,77 @@ static int make_status_items(struct audio_file_data *afd,
  *
  * \return Positive shmid on success, negative on errors.
  */
-int open_and_update_audio_file(struct osl_row *aft_row, long score,
-       struct audio_file_data *afd)
+int open_and_update_audio_file(struct audio_file_data *afd)
 {
-       unsigned char *aft_hash, file_hash[HASH_SIZE];
+       unsigned char file_hash[HASH_SIZE];
        struct osl_object afsi_obj;
-       struct afs_info old_afsi, new_afsi;
-       int ret = get_hash_of_row(aft_row, &aft_hash);
+       struct afs_info new_afsi;
+       int ret;
        struct afsi_change_event_data aced;
        struct osl_object map, chunk_table_obj;
-       char *path;
-
+       struct ls_data *d = &status_item_ls_data;
+again:
+       ret = score_get_best(&current_aft_row, &d->score);
        if (ret < 0)
                return ret;
-       ret = get_audio_file_path_of_row(aft_row, &path);
+       ret = get_hash_of_row(current_aft_row, &d->hash);
        if (ret < 0)
                return ret;
-       PARA_NOTICE_LOG("%s\n", path);
-       ret = get_afsi_object_of_row(aft_row, &afsi_obj);
+       ret = get_audio_file_path_of_row(current_aft_row, &d->path);
        if (ret < 0)
                return ret;
-       ret = load_afsi(&old_afsi, &afsi_obj);
+       PARA_NOTICE_LOG("%s\n", d->path);
+       ret = get_afsi_object_of_row(current_aft_row, &afsi_obj);
        if (ret < 0)
                return ret;
-       ret = get_afhi_of_row(aft_row, &afd->afhi);
+       ret = load_afsi(&d->afsi, &afsi_obj);
        if (ret < 0)
                return ret;
-       afd->afhi.chunk_table = NULL;
-       ret = osl(osl_open_disk_object(audio_file_table, aft_row,
+       ret = get_afhi_of_row(current_aft_row, &afd->afhi);
+       if (ret < 0)
+               return ret;
+       d->afhi = afd->afhi;
+       d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
+       ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0)
                return ret;
-       ret = mmap_full_file(path, O_RDONLY, &map.data,
-               &map.size, &afd->fd);
+       ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
        if (ret < 0)
-               goto err;
+               goto out;
        hash_function(map.data, map.size, file_hash);
-       ret = hash_compare(file_hash, aft_hash);
+       ret = hash_compare(file_hash, d->hash);
        para_munmap(map.data, map.size);
        if (ret) {
                ret = -E_HASH_MISMATCH;
-               goto err;
+               goto out;
        }
-       new_afsi = old_afsi;
+       new_afsi = d->afsi;
        new_afsi.num_played++;
        new_afsi.last_played = time(NULL);
        save_afsi(&new_afsi, &afsi_obj); /* in-place update */
 
-       afd->audio_format_id = old_afsi.audio_format_id;
+       afd->audio_format_id = d->afsi.audio_format_id;
        load_chunk_table(&afd->afhi, chunk_table_obj.data);
-       ret = make_status_items(afd, &old_afsi, path, score, file_hash);
+       aced.aft_row = current_aft_row;
+       aced.old_afsi = &d->afsi;
+       /*
+        * No need to update the status items as the AFSI_CHANGE event will
+        * recreate them.
+        */
+       ret = afs_event(AFSI_CHANGE, NULL, &aced);
        if (ret < 0)
-               goto err;
-       aced.aft_row = aft_row;
-       aced.old_afsi = &old_afsi;
-       afs_event(AFSI_CHANGE, NULL, &aced);
+               goto out;
        ret = save_afd(afd);
-err:
+out:
        free(afd->afhi.chunk_table);
        osl_close_disk_object(&chunk_table_obj);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s: %s\n", path, para_strerror(-ret));
+       if (ret < 0) {
+               PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
+               ret = score_delete(current_aft_row);
+               if (ret >= 0)
+                       goto again;
+       }
        return ret;
 }
 
@@ -1348,22 +1294,14 @@ err:
        return ret;
 }
 
-static void com_ls_callback(int fd, const struct osl_object *query)
+static int com_ls_callback(struct afs_callback_arg *aca)
 {
-       struct ls_options *opts = query->data;
-       char *p, *pattern_start = (char *)query->data + sizeof(*opts);
-       struct para_buffer b = {
-               .max_size = shm_get_shmmax(),
-               .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
-               .max_size_handler = afs_max_size_handler,
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               }
-       };
+       struct ls_options *opts = aca->query.data;
+       char *p, *pattern_start = (char *)aca->query.data + sizeof(*opts);
        int i = 0, ret;
        time_t current_time;
 
+       aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
        if (opts->num_patterns) {
                opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
                for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
@@ -1380,8 +1318,7 @@ static void com_ls_callback(int fd, const struct osl_object *query)
        if (ret < 0)
                goto out;
        if (opts->num_matching_paths == 0) {
-               if (opts->num_patterns > 0)
-                       para_printf(&b, "no matches\n");
+               ret = opts->num_patterns > 0? -E_NO_MATCH : 0;
                goto out;
        }
        ret = sort_matching_paths(opts);
@@ -1390,23 +1327,23 @@ static void com_ls_callback(int fd, const struct osl_object *query)
        time(&current_time);
        if (opts->flags & LS_FLAG_REVERSE)
                for (i = opts->num_matching_paths - 1; i >= 0; i--) {
-                       ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
+                       ret = print_list_item(opts->data_ptr[i], opts,
+                               &aca->pbout, current_time);
                        if (ret < 0)
                                goto out;
                }
        else
                for (i = 0; i < opts->num_matching_paths; i++) {
-                       ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
+                       ret = print_list_item(opts->data_ptr[i], opts,
+                               &aca->pbout, current_time);
                        if (ret < 0)
                                goto out;
                }
 out:
-       if (b.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, b.buf, b.offset);
-       free(b.buf);
        free(opts->data);
        free(opts->data_ptr);
        free(opts->patterns);
+       return ret;
 }
 
 /*
@@ -1414,7 +1351,7 @@ out:
  */
 int com_ls(struct command_context *cc)
 {
-       int i, ret;
+       int i;
        unsigned flags = 0;
        enum ls_sorting_method sort = LS_SORT_BY_PATH;
        enum ls_listing_mode mode = LS_MODE_SHORT;
@@ -1429,18 +1366,26 @@ int com_ls(struct command_context *cc)
                        i++;
                        break;
                }
+               /*
+                * Compatibility: Prior to 0.5.5 it was necessary to specify
+                * the listing mode without the '=' character as in -lv, for
+                * example. Now the variant with '=' is preferred and
+                * documented but we still accept the old way to specify the
+                * listing mode.
+                *
+                * Support for the legacy syntax can be dropped at 0.6.0
+                * or later.
+                */
                if (!strncmp(arg, "-l", 2)) {
-                       if (!*(arg + 2)) {
-                               mode = LS_MODE_LONG;
-                               continue;
-                       }
-                       if (*(arg + 3))
-                               return -E_AFT_SYNTAX;
-                       switch(*(arg + 2)) {
+                       arg += 2;
+                       if (*arg == '=')
+                               arg++;
+                       switch (*arg) {
                        case 's':
                                mode = LS_MODE_SHORT;
                                continue;
                        case 'l':
+                       case '\0':
                                mode = LS_MODE_LONG;
                                continue;
                        case 'v':
@@ -1475,10 +1420,12 @@ int com_ls(struct command_context *cc)
                        flags |= LS_FLAG_UNIXDATE;
                        continue;
                }
+               /* The compatibility remark above applies also to -s. */
                if (!strncmp(arg, "-s", 2)) {
-                       if (!*(arg + 2) || *(arg + 3))
-                               return -E_AFT_SYNTAX;
-                       switch(*(arg + 2)) {
+                       arg += 2;
+                       if (*arg == '=')
+                               arg++;
+                       switch (*arg) {
                        case 'p':
                                sort = LS_SORT_BY_PATH;
                                continue;
@@ -1523,9 +1470,8 @@ int com_ls(struct command_context *cc)
        opts.sorting = sort;
        opts.mode = mode;
        opts.num_patterns = cc->argc - i;
-       ret = send_option_arg_callback_request(&query, opts.num_patterns,
+       return send_option_arg_callback_request(&query, opts.num_patterns,
                cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
-       return ret;
 }
 
 /**
@@ -1542,27 +1488,36 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func)
                func));
 }
 
-static struct osl_row *find_hash_sister(unsigned char *hash)
+static int find_hash_sister(unsigned char *hash, struct osl_row **result)
 {
-       const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
-       struct osl_row *row;
+       int ret = aft_get_row_of_hash(hash, result);
 
-       osl_get_row(audio_file_table, AFTCOL_HASH, &obj, &row);
-       return row;
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+               return 0;
+       return ret;
+}
+
+static int find_path_brother(const char *path, struct osl_row **result)
+{
+       int ret = aft_get_row_of_path(path, result);
+
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+               return 0;
+       return ret;
 }
 
 /** The format of the data stored by save_audio_file_data(). */
 enum com_add_buffer_offsets {
-       /** afhi (if present) starts here. */
+       /* afhi (if present) starts at this offset. */
        CAB_AFHI_OFFSET_POS = 0,
        /** Start of the chunk table (if present). */
-       CAB_CHUNKS_OFFSET_POS = 2,
-       /** Audio format id. */
-       CAB_AUDIO_FORMAT_OFFSET = 4,
+       CAB_CHUNKS_OFFSET_POS = 4,
        /** Flags given to the add command. */
-       CAB_FLAGS_OFFSET = 5,
+       CAB_FLAGS_OFFSET = 8,
+       /** Audio format id. */
+       CAB_AUDIO_FORMAT_ID_OFFSET = 12,
        /** The hash of the audio file being added. */
-       CAB_HASH_OFFSET = 9,
+       CAB_HASH_OFFSET = 13,
        /** Start of the path of the audio file. */
        CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH_SIZE),
 };
@@ -1583,27 +1538,23 @@ static void save_add_callback_buffer(unsigned char *hash, const char *path,
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
                + sizeof_chunk_table(afhi);
        char *buf = para_malloc(size);
-       uint16_t pos;
-
-       write_u8(buf + CAB_AUDIO_FORMAT_OFFSET, audio_format_num);
-       write_u32(buf + CAB_FLAGS_OFFSET, flags);
-
-       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
-       strcpy(buf + CAB_PATH_OFFSET, path);
+       uint32_t pos;
 
        pos = CAB_PATH_OFFSET + path_len;
-       PARA_DEBUG_LOG("size: %zu, afhi starts at %d\n", size, pos);
-       PARA_DEBUG_LOG("last afhi byte: %p, pos %zu\n", buf + pos + afhi_size - 1,
-               pos + afhi_size - 1);
-       write_u16(buf + CAB_AFHI_OFFSET_POS, pos);
+       write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
        save_afhi(afhi, buf + pos);
-
        pos += afhi_size;
-       PARA_DEBUG_LOG("size: %zu, chunks start at %d\n", size, pos);
-       write_u16(buf + CAB_CHUNKS_OFFSET_POS, pos);
+
+       write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
        if (afhi)
                save_chunk_table(afhi, buf + pos);
-       PARA_DEBUG_LOG("last byte in buf: %p\n", buf + size - 1);
+
+       write_u32(buf + CAB_FLAGS_OFFSET, flags);
+       write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
+
+       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+       strcpy(buf + CAB_PATH_OFFSET, path);
+
        obj->data = buf;
        obj->size = size;
 }
@@ -1671,9 +1622,9 @@ enum com_add_flags {
        ADD_FLAG_ALL = 8,
 };
 
-static void com_add_callback(int fd, const struct osl_object *query)
+static int com_add_callback(struct afs_callback_arg *aca)
 {
-       char *buf = query->data, *path;
+       char *buf = aca->query.data, *path;
        struct osl_row *pb, *aft_row;
        struct osl_row *hs;
        struct osl_object objs[NUM_AFT_COLUMNS];
@@ -1683,14 +1634,6 @@ static void com_add_callback(int fd, const struct osl_object *query)
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
-       struct para_buffer msg = {
-               .max_size = shm_get_shmmax(),
-               .max_size_handler = afs_max_size_handler,
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               }
-       };
        uint16_t afhi_offset, chunks_offset;
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
@@ -1703,26 +1646,26 @@ static void com_add_callback(int fd, const struct osl_object *query)
        objs[AFTCOL_PATH].size = strlen(path) + 1;
 
        PARA_INFO_LOG("request to add %s\n", path);
-       hs = find_hash_sister(hash);
-       ret = aft_get_row_of_path(path, &pb);
-       if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+       ret = find_hash_sister(hash, &hs);
+       if (ret < 0)
+               goto out;
+       ret = find_path_brother(path, &pb);
+       if (ret < 0)
                goto out;
        if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
                if (flags & ADD_FLAG_VERBOSE)
-                       ret = para_printf(&msg, "ignoring duplicate\n");
-               else
-                       ret = 1;
+                       para_printf(&aca->pbout, "ignoring duplicate\n");
+               ret = 1;
                goto out;
        }
        if (hs && hs != pb) {
                struct osl_object obj;
                if (pb) { /* hs trumps pb, remove pb */
-                       if (flags & ADD_FLAG_VERBOSE) {
-                               ret = para_printf(&msg, "removing %s\n", path);
-                               if (ret < 0)
-                                       goto out;
-                       }
-                       afs_event(AUDIO_FILE_REMOVE, &msg, pb);
+                       if (flags & ADD_FLAG_VERBOSE)
+                               para_printf(&aca->pbout, "removing %s\n", path);
+                       ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, pb);
+                       if (ret < 0)
+                               goto out;
                        ret = osl(osl_del_row(audio_file_table, pb));
                        if (ret < 0)
                                goto out;
@@ -1734,21 +1677,22 @@ static void com_add_callback(int fd, const struct osl_object *query)
                                AFTCOL_PATH, &obj));
                        if (ret < 0)
                                goto out;
-                       ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data);
-                       if (ret < 0)
-                               goto out;
+                       para_printf(&aca->pbout, "renamed from %s\n",
+                               (char *)obj.data);
                }
                ret = osl(osl_update_object(audio_file_table, hs, AFTCOL_PATH,
                        &objs[AFTCOL_PATH]));
                if (ret < 0)
                        goto out;
-               afs_event(AUDIO_FILE_RENAME, &msg, hs);
+               ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
+               if (ret < 0)
+                       goto out;
                if (!(flags & ADD_FLAG_FORCE))
                        goto out;
        }
        /* no hs or force mode, child must have sent afhi */
-       afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
-       chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
+       afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
+       chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
@@ -1756,7 +1700,7 @@ static void com_add_callback(int fd, const struct osl_object *query)
        if (!objs[AFTCOL_AFHI].size) /* "impossible" */
                goto out;
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
-       objs[AFTCOL_CHUNKS].size = query->size - chunks_offset;
+       objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
        if (pb && !hs) { /* update pb's hash */
                char old_asc[2 * HASH_SIZE + 1];
                unsigned char *old_hash;
@@ -1764,12 +1708,9 @@ static void com_add_callback(int fd, const struct osl_object *query)
                if (ret < 0)
                        goto out;
                hash_to_asc(old_hash, old_asc);
-               if (flags & ADD_FLAG_VERBOSE) {
-                       ret = para_printf(&msg, "file change: %s -> %s\n",
+               if (flags & ADD_FLAG_VERBOSE)
+                       para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
-                       if (ret < 0)
-                               goto out;
-               }
                ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
                        &objs[AFTCOL_HASH]);
                if (ret < 0)
@@ -1778,11 +1719,9 @@ static void com_add_callback(int fd, const struct osl_object *query)
        if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
                struct osl_row *row = pb? pb : hs;
                /* update afhi and chunk_table */
-               if (flags & ADD_FLAG_VERBOSE) {
-                       ret = para_printf(&msg, "updating afhi and chunk table\n");
-                       if (ret < 0)
-                               goto out;
-               }
+               if (flags & ADD_FLAG_VERBOSE)
+                       para_printf(&aca->pbout,
+                               "updating afhi and chunk table\n");
                ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI,
                        &objs[AFTCOL_AFHI]));
                if (ret < 0)
@@ -1791,29 +1730,28 @@ static void com_add_callback(int fd, const struct osl_object *query)
                        &objs[AFTCOL_CHUNKS]));
                if (ret < 0)
                        goto out;
-               afs_event(AFHI_CHANGE, &msg, row);
-               goto out;
-       }
-       /* new entry, use default afsi */
-       if (flags & ADD_FLAG_VERBOSE) {
-               ret = para_printf(&msg, "new file\n");
+               ret = afs_event(AFHI_CHANGE, &aca->pbout, row);
                if (ret < 0)
                        goto out;
+               goto out;
        }
+       /* new entry, use default afsi */
+       if (flags & ADD_FLAG_VERBOSE)
+               para_printf(&aca->pbout, "new file\n");
        default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
-       default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_OFFSET);
+       default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET);
 
        objs[AFTCOL_AFSI].data = &afsi_buf;
        objs[AFTCOL_AFSI].size = AFSI_SIZE;
        save_afsi(&default_afsi, &objs[AFTCOL_AFSI]);
        ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row));
-       afs_event(AUDIO_FILE_ADD, &msg, aft_row);
+       if (ret < 0)
+               goto out;
+       ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
 out:
        if (ret < 0)
-               para_printf(&msg, "%s\n", para_strerror(-ret));
-       if (msg.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, msg.buf, msg.offset);
-       free(msg.buf);
+               para_printf(&aca->pbout, "could not add %s\n", path);
+       return ret;
 }
 
 /** Used by com_add(). */
@@ -1824,26 +1762,26 @@ struct private_add_data {
        uint32_t flags;
 };
 
-static void path_brother_callback(int fd, const struct osl_object *query)
+static int path_brother_callback(struct afs_callback_arg *aca)
 {
-       char *path = query->data;
+       char *path = aca->query.data;
        struct osl_row *path_brother;
-       int ret = aft_get_row_of_path(path, &path_brother);
-       if (ret < 0)
-               return;
-       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&path_brother,
+       int ret = find_path_brother(path, &path_brother);
+       if (ret <= 0)
+               return ret;
+       return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&path_brother,
                sizeof(path_brother));
 }
 
-static void hash_sister_callback(int fd, const struct osl_object *query)
+static int hash_sister_callback(struct afs_callback_arg *aca)
 {
-       unsigned char *hash = query->data;
+       unsigned char *hash = aca->query.data;
        struct osl_row *hash_sister;
+       int ret = find_hash_sister(hash, &hash_sister);
 
-       hash_sister = find_hash_sister(hash);
-       if (!hash_sister)
-               return;
-       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&hash_sister,
+       if (ret <= 0)
+               return ret;
+       return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&hash_sister,
                sizeof(hash_sister));
 }
 
@@ -1851,7 +1789,9 @@ static int get_row_pointer_from_result(struct osl_object *result,
                __a_unused uint8_t band, void *private)
 {
        struct osl_row **row = private;
-       *row = *(struct osl_row **)(result->data);
+
+       if (band == SBD_OUTPUT)
+               *row = *(struct osl_row **)(result->data);
        return 1;
 }
 
@@ -1866,8 +1806,10 @@ static int add_one_audio_file(const char *path, void *private_data)
        unsigned char hash[HASH_SIZE];
 
        ret = guess_audio_format(path);
-       if (ret < 0 && !(pad->flags & ADD_FLAG_ALL))
+       if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) {
+               ret = 0;
                goto out_free;
+       }
        query.data = (char *)path;
        query.size = strlen(path) + 1;
        ret = send_callback_request(path_brother_callback, &query,
@@ -1877,12 +1819,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = pad->cc->use_sideband?
-                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                                       "lazy-ignore: %s\n", path)
-                       :
-                               sc_send_va_buffer(&pad->cc->scc,
-                                       "lazy-ignore: %s\n", path);
+                       send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                               "lazy-ignore: %s\n", path);
                goto out_free;
        }
        /* We still want to add this file. Compute its hash. */
@@ -1902,12 +1840,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = pad->cc->use_sideband?
-                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                                       "%s exists, not forcing update\n", path)
-                       :
-                               sc_send_va_buffer(&pad->cc->scc,
-                                       "%s exists, not forcing update\n", path);
+                       send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                               "%s exists, not forcing update\n", path);
                goto out_unmap;
        }
        /*
@@ -1924,12 +1858,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        munmap(map.data, map.size);
        close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
-               send_ret = pad->cc->use_sideband?
-                       send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                               "adding %s\n", path)
-               :
-                       sc_send_va_buffer(&pad->cc->scc,
-                               "adding %s\n", path);
+               send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                       "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
@@ -1944,14 +1874,8 @@ out_unmap:
        munmap(map.data, map.size);
 out_free:
        if (ret < 0 && send_ret >= 0)
-               send_ret = pad->cc->use_sideband?
-                       send_sb_va(&pad->cc->scc, SBD_ERROR_LOG,
-                               "failed to add %s (%s)\n", path,
-                               para_strerror(-ret))
-               :
-                       sc_send_va_buffer(&pad->cc->scc,
-                               "failed to add %s (%s)\n", path,
-                               para_strerror(-ret));
+               send_ret = send_sb_va(&pad->cc->scc, SBD_ERROR_LOG,
+                       "failed to add %s (%s)\n", path, para_strerror(-ret));
        free(obj.data);
        clear_afhi(afhi_ptr);
        /* Stop adding files only on send errors. */
@@ -1962,7 +1886,6 @@ int com_add(struct command_context *cc)
 {
        int i, ret;
        struct private_add_data pad = {.cc = cc, .flags = 0};
-       struct stat statbuf;
 
        for (i = 1; i < cc->argc; i++) {
                const char *arg = cc->argv[i];
@@ -1995,50 +1918,26 @@ int com_add(struct command_context *cc)
                char *path;
                ret = verify_path(cc->argv[i], &path);
                if (ret < 0) {
-                       ret = cc->use_sideband?
-                               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
-                               cc->argv[i], para_strerror(-ret))
-                       :
-                               sc_send_va_buffer(&cc->scc, "%s: %s\n",
+                       ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
                                cc->argv[i], para_strerror(-ret));
                        if (ret < 0)
                                return ret;
                        continue;
                }
-               ret = stat(path, &statbuf);
-               if (ret < 0) {
-                       ret = cc->use_sideband?
-                               send_sb_va(&cc->scc, SBD_ERROR_LOG,
-                                       "failed to stat %s (%s)\n", path,
-                                       strerror(errno))
-                               :
-                               sc_send_va_buffer(&cc->scc,
-                                       "failed to stat %s (%s)\n", path,
-                                       strerror(errno));
-                       free(path);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (S_ISDIR(statbuf.st_mode))
+               if (ret == 1) /* directory */
                        ret = for_each_file_in_dir(path, add_one_audio_file,
                                &pad);
-               else
+               else /* regular file */
                        ret = add_one_audio_file(path, &pad);
                if (ret < 0) {
-                       if (cc->use_sideband)
-                               send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path,
-                                       para_strerror(-ret));
-                       else
-                               sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
-                                       para_strerror(-ret));
+                       send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path,
+                               para_strerror(-ret));
                        free(path);
                        return ret;
                }
                free(path);
        }
        return 1;
-
 }
 
 /**
@@ -2069,94 +1968,78 @@ struct com_touch_options {
        unsigned flags;
 };
 
-/** Data passed to the action handler of com_touch(). */
-struct touch_action_data {
-       /** Command line options (see \ref com_touch_options). */
-       struct com_touch_options *cto;
-       /** Message buffer. */
-       struct para_buffer pb;
-};
-
 static int touch_audio_file(__a_unused struct osl_table *table,
                struct osl_row *row, const char *name, void *data)
 {
-       struct touch_action_data *tad = data;
+       struct afs_callback_arg *aca = data;
+       struct com_touch_options *cto = aca->query.data;
        struct osl_object obj;
        struct afs_info old_afsi, new_afsi;
-       int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 &&
-               tad->cto->lyrics_id < 0 && tad->cto->image_id < 0 && tad->cto->amp < 0;
+       int ret, no_options = cto->num_played < 0 && cto->last_played < 0 &&
+               cto->lyrics_id < 0 && cto->image_id < 0 && cto->amp < 0;
        struct afsi_change_event_data aced;
 
        ret = get_afsi_object_of_row(row, &obj);
-       if (ret < 0)
-               return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               return ret;
+       }
        ret = load_afsi(&old_afsi, &obj);
-       if (ret < 0)
-               return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               return ret;
+       }
        new_afsi = old_afsi;
        if (no_options) {
                new_afsi.num_played++;
                new_afsi.last_played = time(NULL);
-               if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
-                       ret = para_printf(&tad->pb, "%s: num_played = %u, "
+               if (cto->flags & TOUCH_FLAG_VERBOSE)
+                       para_printf(&aca->pbout, "%s: num_played = %u, "
                                "last_played = now()\n", name,
                                new_afsi.num_played);
-                       if (ret < 0)
-                               return ret;
-               }
        } else {
-               if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
-                       ret = para_printf(&tad->pb, "touching %s\n", name);
-                       if (ret < 0)
-                               return ret;
-               }
-               if (tad->cto->lyrics_id >= 0)
-                       new_afsi.lyrics_id = tad->cto->lyrics_id;
-               if (tad->cto->image_id >= 0)
-                       new_afsi.image_id = tad->cto->image_id;
-               if (tad->cto->num_played >= 0)
-                       new_afsi.num_played = tad->cto->num_played;
-               if (tad->cto->last_played >= 0)
-                       new_afsi.last_played = tad->cto->last_played;
-               if (tad->cto->amp >= 0)
-                       new_afsi.amp = tad->cto->amp;
+               if (cto->flags & TOUCH_FLAG_VERBOSE)
+                       para_printf(&aca->pbout, "touching %s\n", name);
+               if (cto->lyrics_id >= 0)
+                       new_afsi.lyrics_id = cto->lyrics_id;
+               if (cto->image_id >= 0)
+                       new_afsi.image_id = cto->image_id;
+               if (cto->num_played >= 0)
+                       new_afsi.num_played = cto->num_played;
+               if (cto->last_played >= 0)
+                       new_afsi.last_played = cto->last_played;
+               if (cto->amp >= 0)
+                       new_afsi.amp = cto->amp;
        }
        save_afsi(&new_afsi, &obj); /* in-place update */
        aced.aft_row = row;
        aced.old_afsi = &old_afsi;
-       afs_event(AFSI_CHANGE, &tad->pb, &aced);
-       return 1;
+       return afs_event(AFSI_CHANGE, &aca->pbout, &aced);
 }
 
-static void com_touch_callback(int fd, const struct osl_object *query)
+static int com_touch_callback(struct afs_callback_arg *aca)
 {
-       struct touch_action_data tad = {.cto = query->data,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = afs_max_size_handler
-               }
-       };
-       int ret, ret2 = 0;
+       int ret;
+       struct com_touch_options *cto = aca->query.data;
        struct pattern_match_data pmd = {
                .table = audio_file_table,
                .loop_col_num = AFTCOL_HASH,
                .match_col_num = AFTCOL_PATH,
-               .patterns = {.data = (char *)query->data + sizeof(*tad.cto),
-                       .size = query->size - sizeof(*tad.cto)},
-               .data = &tad,
+               .patterns = {
+                       .data = (char *)aca->query.data
+                               + sizeof(struct com_touch_options),
+                       .size = aca->query.size
+                               - sizeof(struct com_touch_options)
+               },
+               .data = aca,
                .action = touch_audio_file
        };
-       if (tad.cto->flags & TOUCH_FLAG_FNM_PATHNAME)
+       if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               ret2 = para_printf(&tad.pb, "%s\n", para_strerror(-ret));
-       else if (pmd.num_matches == 0)
-               ret2 = para_printf(&tad.pb, "no matches\n");
-       if (ret2 >= 0 && tad.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, tad.pb.buf, tad.pb.offset);
-       free(tad.pb.buf);
+       if (ret >= 0 && pmd.num_matches == 0)
+               ret = -E_NO_MATCH;
+       return ret;
 }
 
 int com_touch(struct command_context *cc)
@@ -2226,11 +2109,8 @@ int com_touch(struct command_context *cc)
        }
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
-       ret = send_option_arg_callback_request(&query, cc->argc - i,
+       return send_option_arg_callback_request(&query, cc->argc - i,
                cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       return ret;
 }
 
 /** Flags for com_rm(). */
@@ -2243,67 +2123,50 @@ enum rm_flags {
        RM_FLAG_FNM_PATHNAME = 4
 };
 
-/** Data passed to the action handler of com_rm(). */
-struct com_rm_action_data {
-       /** Command line flags ((see \ref rm_flags). */
-       uint32_t flags;
-       /** Message buffer. */
-       struct para_buffer pb;
-};
-
 static int remove_audio_file(__a_unused struct osl_table *table,
                struct osl_row *row, const char *name, void *data)
 {
-       struct com_rm_action_data *crd = data;
+       struct afs_callback_arg *aca = data;
+       uint32_t flags =*(uint32_t *)aca->query.data;
        int ret;
 
-       if (crd->flags & RM_FLAG_VERBOSE) {
-               ret = para_printf(&crd->pb, "removing %s\n", name);
-               if (ret < 0)
-                       return ret;
-       }
-       afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
+       if (flags & RM_FLAG_VERBOSE)
+               para_printf(&aca->pbout, "removing %s\n", name);
+       ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, row);
+       if (ret < 0)
+               return ret;
        ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
-               para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret));
+               para_printf(&aca->pbout, "cannot remove %s\n", name);
        return ret;
 }
 
-static void com_rm_callback(int fd, const struct osl_object *query)
+static int com_rm_callback(struct afs_callback_arg *aca)
 {
-       struct com_rm_action_data crd = {.flags = *(uint32_t *)query->data,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = afs_max_size_handler
-               }
-       };
        int ret;
+       uint32_t flags = *(uint32_t *)aca->query.data;
        struct pattern_match_data pmd = {
                .table = audio_file_table,
                .loop_col_num = AFTCOL_HASH,
                .match_col_num = AFTCOL_PATH,
-               .patterns = {.data = (char *)query->data + sizeof(uint32_t),
-                       .size = query->size - sizeof(uint32_t)},
-               .data = &crd,
+               .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
+                       .size = aca->query.size - sizeof(uint32_t)},
+               .data = aca,
                .action = remove_audio_file
        };
-       if (crd.flags & RM_FLAG_FNM_PATHNAME)
+       if (flags & RM_FLAG_FNM_PATHNAME)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0) {
-               para_printf(&crd.pb, "%s\n", para_strerror(-ret));
-               return;
-       }
-       if ((pmd.num_matches == 0) && !(crd.flags & RM_FLAG_FORCE))
-               ret = para_printf(&crd.pb, "no matches -- nothing removed\n");
-       else if (crd.flags & RM_FLAG_VERBOSE) {
-               ret = para_printf(&crd.pb, "removed %u files\n",
+       if (ret < 0)
+               goto out;
+       if (pmd.num_matches == 0) {
+               if (!(flags & RM_FLAG_FORCE))
+                       ret = -E_NO_MATCH;
+       } else if (flags & RM_FLAG_VERBOSE)
+               para_printf(&aca->pbout, "removed %u file(s)\n",
                        pmd.num_matches);
-       }
-       if (ret >= 0 && crd.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, crd.pb.buf, crd.pb.offset);
-       free(crd.pb.buf);
+out:
+       return ret;
 }
 
 /* TODO options: -r (recursive) */
@@ -2311,7 +2174,7 @@ int com_rm(struct command_context *cc)
 {
        uint32_t flags = 0;
        struct osl_object query = {.data = &flags, .size = sizeof(flags)};
-       int i, ret;
+       int i;
 
        for (i = 1; i < cc->argc; i++) {
                const char *arg = cc->argv[i];
@@ -2337,11 +2200,8 @@ int com_rm(struct command_context *cc)
        }
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
-       ret = send_option_arg_callback_request(&query, cc->argc - i,
+       return send_option_arg_callback_request(&query, cc->argc - i,
                cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       return ret;
 }
 
 /**
@@ -2368,10 +2228,10 @@ enum cpsi_flags {
 struct cpsi_action_data {
        /** command line flags (see \ref cpsi_flags). */
        unsigned flags;
-       /** Message buffer. */
-       struct para_buffer pb;
        /** Values are copied from here. */
        struct afs_info source_afsi;
+       /** What was passed to com_cpsi_callback(). */
+       struct afs_callback_arg *aca;
 };
 
 static int copy_selector_info(__a_unused struct osl_table *table,
@@ -2399,35 +2259,27 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        if (cad->flags & CPSI_FLAG_COPY_ATTRIBUTES)
                target_afsi.attributes = cad->source_afsi.attributes;
        save_afsi(&target_afsi, &target_afsi_obj); /* in-place update */
-       if (cad->flags & CPSI_FLAG_VERBOSE) {
-               ret = para_printf(&cad->pb, "copied afsi to %s\n", name);
-               if (ret < 0)
-                       return ret;
-       }
+       if (cad->flags & CPSI_FLAG_VERBOSE)
+               para_printf(&cad->aca->pbout, "copied afsi to %s\n", name);
        aced.aft_row = row;
        aced.old_afsi = &old_afsi;
-       afs_event(AFSI_CHANGE, &cad->pb, &aced);
-       return 1;
+       return afs_event(AFSI_CHANGE, &cad->aca->pbout, &aced);
 }
 
-static void com_cpsi_callback(int fd, const struct osl_object *query)
+static int com_cpsi_callback(struct afs_callback_arg *aca)
 {
        struct cpsi_action_data cad = {
-               .flags = *(unsigned *)query->data,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = afs_max_size_handler
-               }
+               .flags = *(unsigned *)aca->query.data,
+               .aca = aca
        };
        int ret;
-       char *source_path = (char *)query->data + sizeof(cad.flags);
+       char *source_path = (char *)aca->query.data + sizeof(cad.flags);
        struct pattern_match_data pmd = {
                .table = audio_file_table,
                .loop_col_num = AFTCOL_HASH,
                .match_col_num = AFTCOL_PATH,
                .patterns = {.data = source_path + strlen(source_path) + 1,
-                       .size = query->size - sizeof(cad.flags)
+                       .size = aca->query.size - sizeof(cad.flags)
                                - strlen(source_path) - 1},
                .data = &cad,
                .action = copy_selector_info
@@ -2437,25 +2289,22 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
        if (ret < 0)
                goto out;
        ret = for_each_matching_row(&pmd);
-out:
        if (ret < 0)
-               para_printf(&cad.pb, "%s\n", para_strerror(-ret));
-       else if (cad.flags & CPSI_FLAG_VERBOSE) {
-               if (pmd.num_matches > 0)
-                       para_printf(&cad.pb, "copied requested afsi from %s "
-                               "to %u files\n", source_path, pmd.num_matches);
-               else
-                       para_printf(&cad.pb, "nothing copied\n");
-       }
-       if (cad.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
-       free(cad.pb.buf);
+               goto out;
+       if (pmd.num_matches > 0) {
+               if (cad.flags & CPSI_FLAG_VERBOSE)
+                       para_printf(&aca->pbout, "updated afsi of %u file(s)\n",
+                               pmd.num_matches);
+       } else
+               ret = -E_NO_MATCH;
+out:
+       return ret;
 }
 
 int com_cpsi(struct command_context *cc)
 {
        unsigned flags = 0;
-       int i, ret;
+       int i;
        struct osl_object options = {.data = &flags, .size = sizeof(flags)};
 
        for (i = 1; i < cc->argc; i++) {
@@ -2496,22 +2345,115 @@ int com_cpsi(struct command_context *cc)
                return -E_AFT_SYNTAX;
        if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
                flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
-       ret = send_option_arg_callback_request(&options, cc->argc - i,
+       return send_option_arg_callback_request(&options, cc->argc - i,
                cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
+}
+
+struct change_atts_data {
+       uint64_t add_mask, del_mask;
+       struct afs_callback_arg *aca;
+};
+
+static int change_atts(__a_unused struct osl_table *table,
+               struct osl_row *row, __a_unused const char *name, void *data)
+{
+       int ret;
+       struct osl_object obj;
+       struct afs_info old_afsi, new_afsi;
+       struct afsi_change_event_data aced = {
+               .aft_row = row,
+               .old_afsi = &old_afsi
+       };
+       struct change_atts_data *cad = data;
+
+       ret = get_afsi_object_of_row(row, &obj);
+       if (ret < 0)
+               return ret;
+       ret = load_afsi(&old_afsi, &obj);
+       if (ret < 0)
+               return ret;
+       new_afsi = old_afsi;
+       new_afsi.attributes |= cad->add_mask;
+       new_afsi.attributes &= ~cad->del_mask;
+       save_afsi(&new_afsi, &obj); /* in-place update */
+       return afs_event(AFSI_CHANGE, &cad->aca->pbout, &aced);
+}
+
+static int com_setatt_callback(struct afs_callback_arg *aca)
+{
+       char *p;
+       int ret;
+       size_t len;
+       struct change_atts_data cad = {.aca = aca};
+       struct pattern_match_data pmd = {
+               .table = audio_file_table,
+               .loop_col_num = AFTCOL_HASH,
+               .match_col_num = AFTCOL_PATH,
+               .pm_flags = PM_SKIP_EMPTY_NAME,
+               .data = &cad,
+               .action = change_atts
+       };
+
+       for (
+               p = aca->query.data;
+               p < (char *)aca->query.data + aca->query.size;
+               p += len + 1
+       ) {
+               char c;
+               unsigned char bitnum;
+               uint64_t one = 1;
+
+               len = strlen(p);
+               ret = -E_ATTR_SYNTAX;
+               if (len == 0)
+                       goto out;
+               c = p[len - 1];
+               if (c != '+' && c != '-')
+                       break;
+               p[len - 1] = '\0';
+               ret = get_attribute_bitnum_by_name(p, &bitnum);
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "attribute not found: %s\n", p);
+                       goto out;
+               }
+               if (c == '+')
+                       cad.add_mask |= (one << bitnum);
+               else
+                       cad.del_mask |= (one << bitnum);
+       }
+       ret = -E_ATTR_SYNTAX;
+       if (!cad.add_mask && !cad.del_mask)
+               goto out;
+       pmd.patterns.data = p;
+       if (p >= (char *)aca->query.data + aca->query.size)
+               goto out;
+       pmd.patterns.size = (char *)aca->query.data + aca->query.size - p;
+       ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               send_strerror(cc, -ret);
+               goto out;
+       if (pmd.num_matches == 0)
+               ret = -E_NO_MATCH;
+out:
        return ret;
 }
 
-static void afs_stat_callback(int fd, const struct osl_object *query)
+int com_setatt(struct command_context *cc)
+{
+       if (cc->argc < 3)
+               return -E_ATTR_SYNTAX;
+       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+               com_setatt_callback, afs_cb_result_handler, cc);
+}
+
+static int afs_stat_callback(struct afs_callback_arg *aca)
 {
-       int *parser_friendly = query->data;
+       int *parser_friendly = aca->query.data;
        char *buf = *parser_friendly?
                parser_friendly_status_items : status_items;
 
        if (!buf)
-               return;
-       pass_buffer_as_shm(fd, SBD_OUTPUT, buf, strlen(buf));
+               return 0;
+       return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, buf, strlen(buf));
 }
 
 /**
@@ -2526,7 +2468,7 @@ static void afs_stat_callback(int fd, const struct osl_object *query)
  * is used to pass the status items from the afs process to the command handler
  * via a shared memory area and a pipe.
  *
- * \return The return value of the underyling call to \ref send_callback_request().
+ * \return The return value of the underlying call to \ref send_callback_request().
  */
 int send_afs_status(struct command_context *cc, int parser_friendly)
 {
@@ -2537,7 +2479,7 @@ int send_afs_status(struct command_context *cc, int parser_friendly)
                afs_cb_result_handler, cc);
 }
 
-/* TODO: optionally fix problems by removing offending rows */
+/* returns success on non-fatal errors to keep the loop going */
 static int check_audio_file(struct osl_row *row, void *data)
 {
        char *path;
@@ -2547,64 +2489,99 @@ static int check_audio_file(struct osl_row *row, void *data)
        struct afs_info afsi;
        char *blob_name;
 
-       if (ret < 0)
-               return para_printf(pb, "%s\n", para_strerror(-ret));
-       if (stat(path, &statbuf) < 0) {
-               ret = para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
-               if (ret < 0)
-                       return ret;
-       } else {
-               if (!S_ISREG(statbuf.st_mode)) {
-                       ret = para_printf(pb, "%s: not a regular file\n", path);
-                       if (ret < 0)
-                               return ret;
-               }
+       if (ret < 0) {
+               para_printf(pb, "%s\n", para_strerror(-ret));
+               return ret;
        }
+       if (stat(path, &statbuf) < 0)
+               para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
+       else if (!S_ISREG(statbuf.st_mode))
+               para_printf(pb, "%s: not a regular file\n", path);
        ret = get_afsi_of_row(row, &afsi);
-       if (ret < 0)
-               return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
-       ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
        if (ret < 0) {
-               ret = para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
-                       para_strerror(-ret));
-               if (ret < 0)
-                       return ret;
+               para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+               return 1;
        }
+       ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
+       if (ret < 0)
+               para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
+                       para_strerror(-ret));
        ret = img_get_name_by_id(afsi.image_id, &blob_name);
        if (ret < 0)
-               ret = para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
+               para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
                        para_strerror(-ret));
-       return ret;
+       return 0;
 }
 
 /**
  * Check the audio file table for inconsistencies.
  *
- * \param fd The afs socket.
- * \param query Unused.
+ * \param aca Only ->pbout is used for diagnostics.
  *
- * This function always succeeds.
+ * \return Standard. Inconsistencies are reported but not regarded as an error.
  *
  * \sa com_check().
  */
-void aft_check_callback(int fd, __a_unused const struct osl_object *query)
+int aft_check_callback(struct afs_callback_arg *aca)
 {
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler
-       };
-       int ret = para_printf(&pb, "checking audio file table...\n");
+       para_printf(&aca->pbout, "checking audio file table...\n");
+       return audio_file_loop(&aca->pbout, check_audio_file);
+}
 
-       if (ret < 0)
-               return;
-       audio_file_loop(&pb, check_audio_file);
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+struct aft_check_atts_data {
+       uint64_t att_mask;
+       struct para_buffer *pb;
+};
+
+static int check_atts_of_audio_file(struct osl_row *row, void *data)
+{
+       struct aft_check_atts_data *acad = data;
+       int ret;
+       struct afs_info afsi;
+       char *path;
+       uint64_t bad_bits;
+
+       ret = get_afsi_of_row(row, &afsi);
+       if (ret < 0) {
+               para_printf(acad->pb, "cannot get afsi\n");
+               return ret;
+       }
+       bad_bits = afsi.attributes & ~acad->att_mask;
+       if (bad_bits == 0) /* OK */
+               return 0;
+       ret = get_audio_file_path_of_row(row, &path);
+       if (ret < 0) {
+               para_printf(acad->pb, "cannot get path\n");
+               return ret;
+       }
+       para_printf(acad->pb, "invalid attribute bits (%" PRIu64 "): %s\n",
+               bad_bits, path);
+       /* return success to keep looping */
+       return 1;
+}
+
+/**
+ * Iterate over all audio files and check the attribute bit mask.
+ *
+ * \param att_mask The mask of all valid attributes.
+ * \param pb Used for reporting inconsistencies.
+ *
+ * This reads the attribute bit mask of each audio file from the afs info
+ * structure stored in the audio file table and verifies that all set bits are
+ * also turned on in \a att_mask, i.e., correspond to an attribute of the
+ * attribute table. Audio files for which this is not the case are reported via
+ * \a pb.
+ *
+ * \return Standard. Inconsistencies are not regarded as errors.
+ *
+ * \sa \ref attribute_check_callback().
+ */
+int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb)
+{
+       struct aft_check_atts_data acad = {.att_mask = att_mask, .pb = pb};
+
+       para_printf(pb, "checking attributes, mask: %" PRIx64 "\n", att_mask);
+       return audio_file_loop(&acad, check_atts_of_audio_file);
 }
 
 /**
@@ -2641,9 +2618,9 @@ static int aft_open(const char *dir)
                PARA_INFO_LOG("audio file table contains %d files\n", num);
                return ret;
        }
-       PARA_INFO_LOG("failed to open audio file table\n");
+       PARA_NOTICE_LOG("failed to open audio file table\n");
        audio_file_table = NULL;
-       if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
                return 1;
        return ret;
 }
@@ -2677,18 +2654,34 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
 {
        int ret;
 
-       switch(event) {
+       switch (event) {
        case ATTRIBUTE_REMOVE: {
                const struct rmatt_event_data *red = data;
-               ret = para_printf(pb, "clearing attribute %s (bit %u) from all "
+               para_printf(pb, "clearing attribute %s (bit %u) from all "
                        "entries in the audio file table\n", red->name,
                        red->bitnum);
+               return audio_file_loop(data, clear_attribute);
+       } case AFSI_CHANGE: {
+               struct afsi_change_event_data *aced = data;
+               uint64_t old_last_played = status_item_ls_data.afsi.last_played;
+               if (aced->aft_row != current_aft_row)
+                       return 0;
+               ret = get_afsi_of_row(aced->aft_row, &status_item_ls_data.afsi);
                if (ret < 0)
                        return ret;
-               return audio_file_loop(data, clear_attribute);
-               }
-       default:
+               status_item_ls_data.afsi.last_played = old_last_played;
+               make_status_items();
+               return 1;
+       } case AFHI_CHANGE: {
+               if (data != current_aft_row)
+                       return 0;
+               ret = get_afhi_of_row(data, &status_item_ls_data.afhi);
+               if (ret < 0)
+                       return ret;
+               make_status_items();
                return 1;
+       } default:
+               return 0;
        }
 }
 
index 01f1c51260a7b6753ba8b14aac0c255120b4b331..cad58af0a9481bd8cb7985cf87b431685f537c01 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -100,11 +100,13 @@ static bool channel_has_playback(snd_mixer_selem_channel_id_t chn,
 
 static char *alsa_mix_get_channels(struct mixer_handle *handle)
 {
+       int ret;
        char *list = NULL;
        snd_mixer_selem_id_t *sid;
        snd_mixer_elem_t *elem;
 
-       snd_mixer_selem_id_alloca(&sid);
+       ret = snd_mixer_selem_id_malloc(&sid);
+       assert(ret >= 0);
        elem = snd_mixer_first_elem(handle->mixer);
        for (; elem; elem = snd_mixer_elem_next(elem)) {
                char *tmp = list;
@@ -122,6 +124,7 @@ static char *alsa_mix_get_channels(struct mixer_handle *handle)
                        name);
                free(tmp);
        }
+       snd_mixer_selem_id_free(sid);
        return list;
 }
 
@@ -133,7 +136,8 @@ static int alsa_mix_set_channel(struct mixer_handle *h,
 
        if (!mixer_channel)
                mixer_channel = "Master";
-       snd_mixer_selem_id_alloca(&sid);
+       ret = snd_mixer_selem_id_malloc(&sid);
+       assert(ret >= 0);
        snd_mixer_selem_id_set_index(sid, selem_id);
        snd_mixer_selem_id_set_name(sid, mixer_channel);
        h->elem = snd_mixer_find_selem(h->mixer, sid);
@@ -141,21 +145,27 @@ static int alsa_mix_set_channel(struct mixer_handle *h,
                PARA_NOTICE_LOG("unable to find simple control '%s',%i\n",
                        snd_mixer_selem_id_get_name(sid),
                        snd_mixer_selem_id_get_index(sid));
-               return -E_ALSA_MIX_BAD_ELEM;
+               ret = -E_BAD_CHANNEL;
+               goto out;
        }
        ret = snd_mixer_selem_get_playback_volume_range(h->elem,
                &h->pmin, &h->pmax);
        if (ret < 0) {
                PARA_NOTICE_LOG("unable to get %s range (%s): %s\n",
                        mixer_channel, h->card, snd_strerror(ret));
-               return -E_ALSA_MIX_BAD_ELEM;
+               ret = -E_ALSA_MIX_RANGE;
+               goto out;
        }
-       if (h->pmin < 0 || h->pmax < 0 || h->pmin >= h->pmax) {
+       if (h->pmin >= h->pmax) {
                PARA_NOTICE_LOG("alsa reported %s range %ld-%ld (%s)\n",
                        mixer_channel, h->pmin, h->pmax, h->card);
-               return -E_ALSA_MIX_BAD_ELEM;
+               ret = -E_ALSA_MIX_RANGE;
+               goto out;
        }
-       return 1;
+       ret = 1;
+out:
+       snd_mixer_selem_id_free(sid);
+       return ret;
 }
 
 static int alsa_mix_get(struct mixer_handle *h)
index a3912c1165e6054b0be0bd622b17175fdad4fd8a..63a7055be35fab00847379be8ce3785a48312818 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -34,17 +34,24 @@ struct private_alsa_write_data {
        snd_pcm_t *handle;
        /** Determined and set by alsa_init(). */
        int bytes_per_frame;
-       /**
-        * The sample rate given by command line option or the decoder
-        * of the writer node group.
+       /*
+        * If the sample rate is not given at the command line and no wav
+        * header was detected, the btr exec mechanism is employed to query the
+        * ancestor buffer tree nodes for this information. In a typical setup
+        * the decoder passes the sample rate back to the alsa writer.
+        *
+        *  \sa \ref btr_exec_up().
         */
        unsigned sample_rate;
-       snd_pcm_format_t sample_format;
-       /**
-        * The number of channels, given by command line option or the
-        * decoder of the writer node group.
+       /*
+        * The sample format (8/16 bit, signed/unsigned, little/big endian) is
+        * determined in the same way as the \a sample_rate.
         */
+       snd_pcm_format_t sample_format;
+       /* The number of channels, again determined like \a sample_rate. */
        unsigned channels;
+       /* time until buffer underrun occurs, in milliseconds */
+       unsigned buffer_time;
        struct timeval drain_barrier;
        /* File descriptor for select(). */
        int poll_fd;
@@ -67,14 +74,14 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
 static int alsa_init(struct private_alsa_write_data *pad,
                struct alsa_write_args_info *conf)
 {
-       snd_pcm_hw_params_t *hwparams;
-       snd_pcm_sw_params_t *swparams;
+       snd_pcm_hw_params_t *hwparams = NULL;
+       snd_pcm_sw_params_t *swparams = NULL;
        snd_pcm_uframes_t start_threshold, stop_threshold;
        snd_pcm_uframes_t buffer_size, period_size;
        snd_output_t *output_log;
-       unsigned buffer_time;
        int ret;
        const char *msg;
+       unsigned period_time;
 
        PARA_INFO_LOG("opening %s\n", conf->device_arg);
        msg = "unable to open pcm";
@@ -82,7 +89,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
                SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
        if (ret < 0)
                goto fail;
-       snd_pcm_hw_params_alloca(&hwparams);
+       ret = snd_pcm_hw_params_malloc(&hwparams);
+       assert(ret >= 0);
        msg = "Broken alsa configuration";
        ret = snd_pcm_hw_params_any(pad->handle, hwparams);
        if (ret < 0)
@@ -107,18 +115,21 @@ static int alsa_init(struct private_alsa_write_data *pad,
                &pad->sample_rate, NULL);
        if (ret < 0)
                goto fail;
-       msg = "unable to get buffer time";
-       ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time,
-               NULL);
-       if (ret < 0 || buffer_time == 0)
-               goto fail;
-       /* buffer at most 500 milliseconds */
-       buffer_time = PARA_MIN(buffer_time, 500U * 1000U);
+       /* alsa wants microseconds */
+       pad->buffer_time = conf->buffer_time_arg * 1000;
        msg = "could not set buffer time";
        ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
-               &buffer_time, NULL);
+               &pad->buffer_time, NULL);
        if (ret < 0)
                goto fail;
+       pad->buffer_time /= 1000; /* we prefer milliseconds */
+       period_time = pad->buffer_time * 250; /* buffer time / 4 */
+       msg = "could not set period time";
+       ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams,
+               &period_time, NULL);
+       if (ret < 0)
+               goto fail;
+
        msg = "unable to install hw params";
        ret = snd_pcm_hw_params(pad->handle, hwparams);
        if (ret < 0)
@@ -130,7 +141,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
                goto fail;
 
        /* software parameter setup */
-       snd_pcm_sw_params_alloca(&swparams);
+       ret = snd_pcm_sw_params_malloc(&swparams);
+       assert(ret >= 0);
        snd_pcm_sw_params_current(pad->handle, swparams);
        snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size);
        if (buffer_size < 1)
@@ -166,32 +178,38 @@ static int alsa_init(struct private_alsa_write_data *pad,
        if (ret == 0) {
                char *buf, *p;
                size_t sz;
-               PARA_INFO_LOG("dumping alsa configuration\n");
+               PARA_DEBUG_LOG("dumping alsa configuration\n");
                snd_pcm_dump(pad->handle, output_log);
+               snd_pcm_hw_params_dump(hwparams, output_log);
                sz = snd_output_buffer_string(output_log, &buf);
                for (p = buf; p < buf + sz;) {
                        char *q = memchr(p, '\n', buf + sz - p);
                        if (!q)
                                break;
                        *q = '\0';
-                       PARA_INFO_LOG("%s\n", p);
+                       PARA_DEBUG_LOG("%s\n", p);
                        p = q + 1;
                }
                snd_output_close(output_log);
        }
-       return 1;
+       ret = 1;
+       goto out;
 fail:
        if (ret < 0)
                PARA_ERROR_LOG("%s: %s\n", msg, snd_strerror(-ret));
        else
                PARA_ERROR_LOG("%s\n", msg);
-       return -E_ALSA;
+       ret = -E_ALSA;
+out:
+       snd_pcm_hw_params_free(hwparams);
+       snd_pcm_sw_params_free(swparams);
+       return ret;
 }
 
-static void alsa_write_pre_select(struct sched *s, struct task *t)
+static void alsa_write_pre_select(struct sched *s, void *context)
 {
        struct pollfd pfd;
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
        int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
 
@@ -207,11 +225,12 @@ static void alsa_write_pre_select(struct sched *s, struct task *t)
                sched_request_barrier_or_min_delay(&pad->drain_barrier, s);
                return;
        }
+       /* wait at most 50% of the buffer time */
+       sched_request_timeout_ms(pad->buffer_time / 2, s);
        ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1);
        if (ret < 0) {
                PARA_ERROR_LOG("could not get alsa poll fd: %s\n",
                        snd_strerror(-ret));
-               t->error = -E_ALSA;
                return;
        }
        pad->poll_fd = pfd.fd;
@@ -236,10 +255,9 @@ static void alsa_close(struct writer_node *wn)
        free(pad);
 }
 
-static int alsa_write_post_select(__a_unused struct sched *s,
-               struct task *t)
+static int alsa_write_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        char *data;
@@ -247,7 +265,7 @@ static int alsa_write_post_select(__a_unused struct sched *s,
        snd_pcm_sframes_t frames;
        int ret;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(wn->task);
        if (ret < 0)
                goto err;
 again:
@@ -296,21 +314,13 @@ again:
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
-       if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
-               return 0;
        frames = bytes / pad->bytes_per_frame;
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
-               /*
-                * The alsa poll fd was ready for IO but we failed to write to
-                * the alsa handle. This means another event is pending. We
-                * don't care about that but we have to dispatch the event in
-                * order to avoid a busy loop. So we simply read from the poll
-                * fd.
-                */
                char buf[100];
-               if (read(pad->poll_fd, buf, 100))
-                       do_nothing;
+               if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+                       if (read(pad->poll_fd, buf, 100))
+                               do_nothing;
                return 0;
        }
        if (frames > 0) {
index f62ea8a2f50962182e158ce8da7f18fb4ec1df4f..5193d7c166d9345ced27899e38714ddd5d393fcf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -34,7 +34,7 @@ static void amp_close(struct filter_node *fn)
 static int amp_parse_config(int argc, char **argv, void **config)
 {
        struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
-       int ret = -E_AMP_SYNTAX;
+       int ret;
 
        amp_filter_cmdline_parser(argc, argv, conf);
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
@@ -58,13 +58,13 @@ static void amp_open(struct filter_node *fn)
                sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp);
        else
                pad->amp = conf->amp_arg;
-       PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n",
+       PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n",
                pad->amp, pad->amp / 64.0 + 1.0);
 }
 
-static int amp_post_select(__a_unused struct sched *s, struct task *t)
+static int amp_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_amp_data *pad = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret, factor = 64 + pad->amp;
@@ -73,7 +73,7 @@ static int amp_post_select(__a_unused struct sched *s, struct task *t)
        bool inplace = btr_inplace_ok(btrn);
 
        if (pad->amp == 0) { /* no amplification */
-               btr_splice_out_node(btrn);
+               btr_splice_out_node(&fn->btrn);
                return -E_AMP_ZERO_AMP;
        }
 next_buffer:
index fdae8eea542f97720f963f281bddd121cf5d0a38..82d98f3ee8cf6c0491e1820ee32b63c8216abac2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -28,6 +28,7 @@ struct private_aow_data {
 
        pthread_t thread;
        pthread_attr_t attr;
+       /* The mutex and the condition variable serialize access to ->btrn */
        pthread_mutex_t mutex;
        pthread_cond_t data_available;
        struct btr_node *thread_btrn;
@@ -39,19 +40,46 @@ static void aow_close(struct writer_node *wn)
 
        if (!pawd)
                return;
+       assert(!pawd->thread_btrn);
        ao_close(pawd->dev);
        free(pawd);
        wn->private_data = NULL;
-       ao_shutdown();
 }
 
-static void aow_pre_select(struct sched *s, struct task *t)
+static void aow_pre_select(struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
-       int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       struct writer_node *wn = context;
+       struct private_aow_data *pawd = wn->private_data;
+       int ret;
 
-       if (ret == 0)
-               return;
+       if (!pawd) { /* not yet started */
+               assert(wn->btrn);
+               ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret != 0)
+                       goto min_delay;
+               return; /* no data available */
+       }
+       if (!wn->btrn) { /* EOF */
+               if (!pawd->thread_btrn) /* ready to exit */
+                       goto min_delay;
+               /* wait for the play thread to terminate */
+               goto timeout;
+       }
+       pthread_mutex_lock(&pawd->mutex);
+       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       pthread_mutex_unlock(&pawd->mutex);
+       if (ret != 0)
+               goto min_delay;
+       /*
+        * Even though the node status is zero, we might have data available,
+        * but the output buffer is full. If we don't set a timeout here, we
+        * are woken up only if new data arrives, which might be too late and
+        * result in a buffer underrun in the playing thread. To avoid this we
+        * never sleep longer than the (default) buffer time.
+        */
+timeout:
+       return sched_request_timeout_ms(20, s);
+min_delay:
        sched_min_delay(s);
 }
 
@@ -129,7 +157,6 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
        struct ao_write_args_info *conf = wn->conf;
 
-       ao_initialize();
        if (conf->driver_given) {
                ret = -E_AO_BAD_DRIVER;
                id = ao_driver_id(conf->driver_arg);
@@ -192,16 +219,12 @@ __noreturn static void *aow_play(void *priv)
        char *data;
        int ret;
 
+       pthread_mutex_lock(&pawd->mutex);
        for (;;) {
-               /*
-                * Lock mutex and wait for signal. pthread_cond_wait() will
-                * automatically and atomically unlock mutex while it waits.
-                */
-               pthread_mutex_lock(&pawd->mutex);
                for (;;) {
                        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
                        if (ret < 0)
-                               goto unlock;
+                               goto fail;
                        if (ret > 0) {
                                btr_merge(btrn, wn->min_iqs);
                                bytes = btr_next_buffer(btrn, &data);
@@ -210,26 +233,35 @@ __noreturn static void *aow_play(void *priv)
                                        break;
                                /* eof and less than a single frame available */
                                ret = -E_WRITE_COMMON_EOF;
-                               goto unlock;
+                               goto fail;
                        }
-                       //PARA_CRIT_LOG("waiting for data\n");
-                       //usleep(1000);
-                       //pthread_mutex_unlock(&pawd->mutex);
-                       pthread_cond_wait(&pawd->data_available, &pawd->mutex);
+                       /*
+                        * No data available, go to sleep and wait for the main
+                        * thread to wake us up. pthread_cond_wait() unlocks
+                        * the mutex while it waits and locks it again upon
+                        * return.
+                        */
+                       ret = pthread_cond_wait(&pawd->data_available,
+                               &pawd->mutex);
+                       /* pthread_cond_wait() can never fail here */
+                       assert(ret == 0);
                }
-               pthread_mutex_unlock(&pawd->mutex);
                assert(frames > 0);
                bytes = frames * pawd->bytes_per_frame;
-               ret = -E_AO_PLAY;
-               if (ao_play(pawd->dev, data, bytes) == 0) /* failure */
-                       goto out;
+               pthread_mutex_unlock(&pawd->mutex);
+               ret = ao_play(pawd->dev, data, bytes);
+               pthread_mutex_lock(&pawd->mutex);
+               if (ret == 0) { /* failure */
+                       ret = -E_AO_PLAY;
+                       goto fail;
+               }
                btr_consume(btrn, bytes);
        }
-unlock:
-       pthread_mutex_unlock(&pawd->mutex);
-out:
+fail:
+       btr_remove_node(&pawd->thread_btrn);
        assert(ret < 0);
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       pthread_mutex_unlock(&pawd->mutex);
        pthread_exit(NULL);
 }
 
@@ -276,10 +308,9 @@ fail:
        return -E_AO_PTHREAD;
 }
 
-static int aow_post_select(__a_unused struct sched *s,
-               struct task *t)
+static int aow_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
        int ret;
 
@@ -314,21 +345,32 @@ static int aow_post_select(__a_unused struct sched *s,
                        goto remove_thread_btrn;
                return 0;
        }
+       if (!wn->btrn) {
+               if (!pawd->thread_btrn) {
+                       pthread_join(pawd->thread, NULL);
+                       return -E_AO_EOF;
+               }
+               PARA_INFO_LOG("waiting for play thread to terminate\n");
+               return 0;
+       }
        pthread_mutex_lock(&pawd->mutex);
        ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
        if (ret > 0) {
                btr_pushdown(wn->btrn);
-               pthread_cond_signal(&pawd->data_available);
+               if (pthread_cond_signal(&pawd->data_available) != 0) {
+                       ret = -E_AO_PTHREAD;
+                       PARA_ERROR_LOG("pthread_cond_signal() failed\n");
+                       goto remove_thread_btrn;
+               }
        }
-       pthread_mutex_unlock(&pawd->mutex);
-       if (ret >= 0)
+       if (ret >= 0) {
+               pthread_mutex_unlock(&pawd->mutex);
                goto out;
-       pthread_mutex_lock(&pawd->mutex);
+       }
        btr_remove_node(&wn->btrn);
-       PARA_INFO_LOG("waiting for thread to terminate\n");
        pthread_cond_signal(&pawd->data_available);
        pthread_mutex_unlock(&pawd->mutex);
-       pthread_join(pawd->thread, NULL);
+       return 0;
 remove_thread_btrn:
        btr_remove_node(&pawd->thread_btrn);
 remove_btrn:
@@ -410,6 +452,5 @@ void ao_write_init(struct writer *w)
        dh[num_lines] = NULL;
        w->help.detailed_help = (const char **)dh;
        ao_write_cmdline_parser_free(&dummy);
-       ao_shutdown();
 }
 
index 65f08aef4c062c79b6b204b628fe0bbf0c98e8bb..f3f8ea7a45d0fb1136827cc19c8a715fdd3773a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -121,70 +121,60 @@ enum lsatt_flags {
 };
 
 /** Data passed to the action function of lsatt */
-struct lsatt_action_data {
-       /** The result buffer. */
-       struct para_buffer pb;
-       /** The given flags for the lsatt command. */
-       unsigned flags;
-};
-
 static int print_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
-       struct lsatt_action_data *laad = data;
+       struct afs_callback_arg *aca = data;
+       unsigned flags = *(unsigned *)aca->query.data;
        struct osl_object bitnum_obj;
        int ret;
 
-       if (!(laad->flags & LSATT_FLAG_LONG))
-               return para_printf(&laad->pb, "%s\n", name);
+       if (!(flags & LSATT_FLAG_LONG)) {
+               para_printf(&aca->pbout, "%s\n", name);
+               return 1;
+       }
        ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
-               para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret));
+               para_printf(&aca->pbout, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
-       return para_printf(&laad->pb, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
+       para_printf(&aca->pbout, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
                name);
+       return 1;
 }
 
-static void com_lsatt_callback(int fd, const struct osl_object *query)
+static int com_lsatt_callback(struct afs_callback_arg *aca)
 {
-       struct lsatt_action_data laad = {
-               .flags = *(unsigned *) query->data,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &(struct afs_max_size_handler_data) {
-                               .fd = fd,
-                               .band = SBD_OUTPUT
-                       },
-                       .max_size_handler = afs_max_size_handler
-               }
-
-       };
+       unsigned flags = *(unsigned *)aca->query.data;
+       int ret;
        struct pattern_match_data pmd = {
                .table = attribute_table,
                .loop_col_num = ATTCOL_NAME,
                .match_col_num = ATTCOL_NAME,
-               .patterns = {.data = (char *)query->data + sizeof(laad.flags),
-                       .size = query->size - sizeof(laad.flags)},
+               .patterns = {.data = (char *)aca->query.data + sizeof(flags),
+                       .size = aca->query.size - sizeof(flags)},
                .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING,
-               .data = &laad,
+               .data = aca,
                .action = print_attribute
        };
-       if (laad.flags & LSATT_FLAG_SORT_BY_ID)
+       if (flags & LSATT_FLAG_SORT_BY_ID)
                pmd.loop_col_num = ATTCOL_BITNUM;
-       if (laad.flags & LSATT_FLAG_REVERSE)
+       if (flags & LSATT_FLAG_REVERSE)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       for_each_matching_row(&pmd);
-       if (laad.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, laad.pb.buf, laad.pb.offset);
-       free(laad.pb.buf);
+       ret = for_each_matching_row(&pmd);
+       if (ret < 0)
+               goto out;
+       if (pmd.num_matches == 0)
+               ret = -E_NO_MATCH;
+out:
+       return ret;
 }
 
 int com_lsatt(struct command_context *cc)
 {
        unsigned flags = 0;
        struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-       int ret, i;
+       int i;
 
        for (i = 1; i < cc->argc; i++) {
                const char *arg = cc->argv[i];
@@ -207,88 +197,8 @@ int com_lsatt(struct command_context *cc)
                        continue;
                }
        }
-       ret = send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
+       return send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
                com_lsatt_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       else if (ret == 0 && cc->argc > 1)
-               ret = cc->use_sideband?
-                       send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n")
-               :
-                       sc_send_va_buffer(&cc->scc, "no matches\n");
-       return ret;
-}
-
-static void com_setatt_callback(__a_unused int fd, const struct osl_object *query)
-{
-       char *p;
-       uint64_t add_mask = 0, del_mask = 0, one = 1;
-       int ret;
-       size_t len;
-       struct osl_object obj;
-       struct osl_row *row;
-
-       for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
-               char c;
-
-               len = strlen(p);
-               ret = -E_ATTR_SYNTAX;
-               if (!*p)
-                       goto out;
-               c = p[len - 1];
-               if (c != '+' && c != '-')
-                       break;
-               p[len - 1] = '\0';
-               obj.data = p;
-               obj.size = len + 1;
-               ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
-               if (ret < 0)
-                       goto out;
-               ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM,
-                       &obj));
-               if (ret < 0)
-                       goto out;
-               if (c == '+')
-                       add_mask |= (one << *(unsigned char *)obj.data);
-               else
-                       del_mask |= (one << *(unsigned char *)obj.data);
-       }
-       ret = -E_ATTR_SYNTAX;
-       if (!add_mask && !del_mask)
-               goto out;
-       PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask,
-               (long long unsigned)del_mask);
-       for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */
-               struct afs_info old_afsi, new_afsi;
-               struct afsi_change_event_data aced = {.old_afsi = &old_afsi};
-
-               len = strlen(p);
-               ret = aft_get_row_of_path(p, &aced.aft_row);
-               if (ret < 0)
-                       goto out;
-               ret = get_afsi_object_of_row(aced.aft_row, &obj);
-               if (ret < 0)
-                       goto out;
-               ret = load_afsi(&old_afsi, &obj);
-               if (ret < 0)
-                       goto out;
-               new_afsi = old_afsi;
-               new_afsi.attributes |= add_mask;
-               new_afsi.attributes &= ~del_mask;
-               save_afsi(&new_afsi, &obj); /* in-place update */
-               afs_event(AFSI_CHANGE, NULL, &aced);
-       }
-out:
-       if (ret < 0)
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-}
-
-int com_setatt(struct command_context *cc)
-{
-       if (cc->argc < 3)
-               return -E_ATTR_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_setatt_callback, NULL, NULL);
 }
 
 struct addatt_event_data {
@@ -297,21 +207,17 @@ struct addatt_event_data {
 };
 
 
-static void com_addatt_callback(int fd, const struct osl_object *query)
+static int com_addatt_callback(struct afs_callback_arg *aca)
 {
        char *p;
-       int ret = 1, ret2 = 0;
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler
-       };
+       int ret = 1;
        size_t len;
 
-       for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
+       for (
+               p = aca->query.data;
+               p < (char *)aca->query.data + aca->query.size;
+               p += len + 1
+       ) {
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
@@ -319,16 +225,12 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
 
                len = strlen(p);
                if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
-                       ret2 = para_printf(&pb, "invalid attribute name: %s\n", p);
-                       if (ret2 < 0)
-                               goto out;
+                       para_printf(&aca->pbout, "invalid attribute name: %s\n", p);
                        continue;
                }
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                if (ret >= 0) {
-                       ret2 = para_printf(&pb, "attribute \"%s\" already exists\n", p);
-                       if (ret2 < 0)
-                               goto out;
+                       para_printf(&aca->pbout, "attribute \"%s\" already exists\n", p);
                        continue;
                }
                if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
@@ -356,15 +258,15 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
                        goto out;
                aed.name = p;
                aed.bitnum = bitnum;
-               afs_event(ATTRIBUTE_ADD, &pb, &aed);
+               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
+               if (ret < 0)
+                       goto out;
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
-       if (ret < 0 && ret2 >= 0)
-               para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+       if (ret < 0)
+               para_printf(&aca->pbout, "%s: %s\n", p, para_strerror(-ret));
+       return ret;
 }
 
 int com_addatt(struct command_context *cc)
@@ -380,21 +282,13 @@ int com_addatt(struct command_context *cc)
        return ret;
 }
 
-static void com_mvatt_callback(int fd, const struct osl_object *query)
+static int com_mvatt_callback(struct afs_callback_arg *aca)
 {
-       char *old = query->data;
+       char *old = aca->query.data;
        size_t size = strlen(old) + 1;
        char *new = old + size;
        struct osl_object obj = {.data = old, .size = size};
        struct osl_row *row;
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler,
-       };
        int ret;
 
        ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
@@ -405,100 +299,67 @@ static void com_mvatt_callback(int fd, const struct osl_object *query)
        ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
 out:
        if (ret < 0)
-               para_printf(&pb, "%s\n", para_strerror(-ret));
+               para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
        else
-               afs_event(ATTRIBUTE_RENAME, &pb, NULL);
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+               ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
+       return ret;
 }
 
 int com_mvatt(struct command_context *cc)
 {
-       int ret;
-
        if (cc->argc != 3)
                return -E_ATTR_SYNTAX;
-       ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
                com_mvatt_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       return ret;
 }
 
-/** Data passed to the action handler of com_rmatt(). */
-struct remove_attribute_action_data {
-       /** Message buffer. */
-       struct para_buffer pb;
-       /** Numver of attributes removed. */
-       int num_removed;
-       /** Bitwise "or" of the removed attributes. */
-       uint64_t mask_of_removed_atts;
-};
-
 static int remove_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
-       struct remove_attribute_action_data *raad = data;
+       struct afs_callback_arg *aca = data;
        int ret;
        struct rmatt_event_data red = {.name = name};
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
-       if (ret < 0)
-               return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               return ret;
+       }
+       para_printf(&aca->pbout, "removing attribute %s\n", name);
        ret = osl(osl_del_row(table, row));
-       if (ret < 0)
-               return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
-       ret = para_printf(&raad->pb, "removed attribute %s\n", name);
-       raad->num_removed++;
-       raad->mask_of_removed_atts |= (1 << red.bitnum);
-       afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red);
-       return ret;
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               return ret;
+       }
+       return afs_event(ATTRIBUTE_REMOVE, &aca->pbout, &red);
 }
 
-static void com_rmatt_callback(int fd, const struct osl_object *query)
+static int com_rmatt_callback(struct afs_callback_arg *aca)
 {
-       struct remove_attribute_action_data raad = {
-               .num_removed = 0,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &(struct afs_max_size_handler_data) {
-                               .fd = fd,
-                               .band = SBD_OUTPUT
-                       },
-                       .max_size_handler = afs_max_size_handler,
-               }
-       };
-       int ret, ret2 = 0;
+       int ret;
        struct pattern_match_data pmd = {
                .table = attribute_table,
-               .patterns = *query,
+               .patterns = aca->query,
                .loop_col_num = ATTCOL_BITNUM,
                .match_col_num = ATTCOL_NAME,
-               .data = &raad,
+               .data = aca,
                .action = remove_attribute
        };
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               ret2 = para_printf(&raad.pb, "%s\n", para_strerror(-ret));
-       else if (!raad.num_removed)
-               ret2 = para_printf(&raad.pb, "no match -- nothing removed\n");
-       if (ret2 >= 0 && raad.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, raad.pb.buf, raad.pb.offset);
-       free(raad.pb.buf);
+               goto out;
+       if (pmd.num_matches == 0)
+               ret = -E_NO_MATCH;
+out:
+       return ret;
 }
 
 int com_rmatt(struct command_context *cc)
 {
-       int ret;
-
        if (cc->argc < 2)
                return -E_ATTR_SYNTAX;
-       ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
                com_rmatt_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       return ret;
 }
 
 /**
@@ -513,7 +374,7 @@ int com_rmatt(struct command_context *cc)
  * used for unset attributes.
  *
  * In practice, not all 64 attributes are defined. In this case, the function
- * only prints \a N + 1 charaters where \a N is the greatest id of a defined
+ * only prints \a N + 1 characters where \a N is the greatest id of a defined
  * attribute.
  */
 void get_attribute_bitmap(const uint64_t *atts, char *buf)
@@ -574,6 +435,47 @@ err:
        return ret;
 }
 
+static int att_logical_or(struct osl_row *row, void *data)
+{
+       uint64_t *att_mask = data;
+       struct osl_object bitnum_obj;
+       int ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &bitnum_obj);
+
+       if (ret < 0)
+               return ret;
+       *att_mask |= 1 << *(unsigned char *)bitnum_obj.data;
+       return 0;
+}
+
+/**
+ * Compute the attribute bit mask and check each afs info bitmap.
+ *
+ * \param aca The query field of \a aca is ignored.
+ *
+ * This iterates over all attributes in the attribute table and computes the
+ * logical or of 1 << b where b is the bit number of the attribute. The
+ * resulting bit mask is passed to aft_check_attributes() which performs the
+ * actual check.
+ *
+ * \return Standard.
+ *
+ * \sa \ref aft_check_attributes().
+ */
+int attribute_check_callback(struct afs_callback_arg *aca)
+{
+       int ret;
+       uint64_t att_mask = 0; /* bits corresponding to a attributes */
+
+       ret = osl_rbtree_loop(attribute_table, ATTCOL_BITNUM, &att_mask,
+               att_logical_or);
+       if (ret < 0) {
+               PARA_ERROR_LOG("attribute table loop failed: %s\n",
+                       para_strerror(-ret));
+               return ret;
+       }
+       return aft_check_attributes(att_mask, &aca->pbout);
+}
+
 /**
  * Close the attribute table.
  *
@@ -606,7 +508,7 @@ static int attribute_open(const char *dir)
                return ret;
        }
        attribute_table = NULL;
-       if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
                return 1;
        return ret;
 }
index 0edab366efe6ef6fa50f99c22b9ffa3492494640..337c8062e2b4257f47e7412d3d1ca631edd503a1 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -1,14 +1,18 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file audioc.c The client program used to connect to para_audiod. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
-#include <stdbool.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 #include <signal.h>
 
 #include "audioc.cmdline.h"
@@ -67,14 +71,14 @@ fail:
 #include "sched.h"
 #include "buffer_tree.h"
 #include "interactive.h"
-#include "audiod_completion.h"
+#include "audiod.completion.h"
 
 static struct sched sched;
 
 struct audioc_task {
        int fd;
        struct btr_node *btrn;
-       struct task task;
+       struct task *task;
 };
 
 static struct i9e_completer audiod_completers[];
@@ -92,6 +96,15 @@ static void help_completer(struct i9e_completion_info *ci,
        result->matches = i9e_complete_commands(ci->word, audiod_completers);
 }
 
+static void version_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       char *opts[] = {"-v", NULL};
+
+       if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
+               i9e_complete_option(opts, ci, cr);
+}
+
 static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -116,9 +129,9 @@ static struct i9e_completer audiod_completers[] = {
        {.name = NULL}
 };
 
-static void audioc_pre_select(struct sched *s, struct task *t)
+static void audioc_pre_select(struct sched *s, void *context)
 {
-       struct audioc_task *at = container_of(t, struct audioc_task, task);
+       struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
 
        if (ret < 0)
@@ -126,10 +139,10 @@ static void audioc_pre_select(struct sched *s, struct task *t)
        para_fd_set(at->fd, &s->rfds, &s->max_fileno);
 }
 
-static int audioc_post_select(struct sched *s, struct task *t)
+static int audioc_post_select(struct sched *s, void *context)
 {
        char *buf = NULL;
-       struct audioc_task *at = container_of(t, struct audioc_task, task);
+       struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
 
        if (ret < 0)
@@ -154,13 +167,7 @@ out:
        return ret;
 }
 
-static struct audioc_task audioc_task = {
-       .task = {
-               .pre_select = audioc_pre_select,
-               .post_select = audioc_post_select,
-               .status = "audioc task"
-       },
-}, *at = &audioc_task;
+static struct audioc_task audioc_task, *at = &audioc_task;
 
 static int audioc_i9e_line_handler(char *line)
 {
@@ -188,8 +195,12 @@ static int audioc_i9e_line_handler(char *line)
        args = NULL;
        at->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "audioc line handler"));
-       at->task.error = 0;
-       register_task(&sched, &at->task);
+       at->task = task_register(&(struct task_info) {
+               .name = "audioc",
+               .pre_select = audioc_pre_select,
+               .post_select = audioc_post_select,
+               .context = at,
+       }, &sched);
        i9e_attach_to_stdout(at->btrn);
        return 1;
 close:
@@ -234,6 +245,7 @@ __noreturn static void interactive_session(void)
                goto out;
        para_log = i9e_log;
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        i9e_close();
        para_log = stderr_log;
 out:
index 8f2a72ac8700ee0699c19d78724cbe1d6ec61296..798142f39a348ec6f0ac74e689332d18a1318444 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -1,13 +1,20 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file audiod.c The paraslash's audio daemon. */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 #include <signal.h>
+#include <pwd.h>
 
 #include "para.h"
 #include "error.h"
@@ -62,6 +69,30 @@ struct audio_format_info {
        struct timeval restart_barrier;
 };
 
+/* Describes one instance of a receiver-filter-writer chain. */
+struct slot_info {
+       /* Number of the audio format in this slot. */
+       int format;
+       /* The stream_start status item announced by para_server.  */
+       struct timeval server_stream_start;
+       /* The offset status item announced by para_server. */
+       unsigned offset_seconds;
+       /* The seconds_total status item announced by para_server. */
+       unsigned seconds_total;
+       /* The receiver info associated with this slot. */
+       struct receiver_node *receiver_node;
+       /* The array of filter nodes. */
+       struct filter_node *fns;
+       /* The array of writers attached to the last filter. */
+       struct writer_node *wns;
+};
+
+/** Maximal number of simultaneous instances. */
+#define MAX_STREAM_SLOTS 5
+
+/** Iterate over all slots. */
+#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++)
+
 /**
  * para_audiod uses \p MAX_STREAM_SLOTS different slots, each of which may
  * be associated with a receiver/filter/writer triple. This array holds all
@@ -87,14 +118,10 @@ enum vss_status_flags {
  */
 struct sched sched = {.max_fileno = 0};
 
-/**
- * The task for obtaining para_server's status (para_client stat).
- *
- * \sa struct task, struct sched.
- */
+/* The task for obtaining para_server's status (para_client stat). */
 struct status_task {
        /** The associated task structure of audiod. */
-       struct task task;
+       struct task *task;
        /** Client data associated with the stat task. */
        struct client_task *ct;
        /** Do not restart client command until this time. */
@@ -142,10 +169,12 @@ struct audiod_args_info conf;
 static char *socket_name;
 static struct audio_format_info afi[NUM_AUDIO_FORMATS];
 
-static struct signal_task signal_task_struct, *sig_task = &signal_task_struct;
+static struct signal_task *signal_task;
 
 static struct status_task status_task_struct;
 
+static uid_t *uid_whitelist;
+
 /**
  * the task that calls the status command of para_server
  *
@@ -153,16 +182,24 @@ static struct status_task status_task_struct;
  */
 static struct status_task *stat_task = &status_task_struct;
 
-/**
- * the task for handling audiod commands
+/*
+ * The task for handling audiod commands.
+ *
+ * We need two listening sockets for backward compability: on Linux systems
+ * fd[0] is an abstract socket (more precisely, a socket bound to an address in
+ * the abstract namespace), and fd[1] is the usual pathname socket. On other
+ * systems, fd[0] is negative, and only the pathname socket is used.
  *
- * \sa struct task, struct sched
+ * For 0.5.x we accept connections on both sockets to make sure that old
+ * para_audioc versions can still connect. New versions use only the abstract
+ * socket. Hence after v0.6.0 we can go back to a single socket, either an
+ * abstract one (Linux) or a pathname socket (all other systems).
  */
 struct command_task {
-       /** the local listening socket */
-       int fd;
+       /** The local listening sockets. */
+       int fd[2];
        /** the associated task structure */
-       struct task task;
+       struct task *task;
 };
 
 /** iterate over all supported audio formats */
@@ -188,6 +225,35 @@ static int get_audio_format_num(const char *name)
        return -E_UNSUPPORTED_AUDIO_FORMAT;
 }
 
+/**
+ * Return the flags for the \a decoder_flags status item.
+ *
+ * Allocates a string which contains one octal digit per slot.  Bit zero (value
+ * 1) is set if a receiver is active. Bit one (value 2) and bit three (value 4)
+ * have the analogous meaning for filter and writer, respectively.
+ *
+ * \return String that must be freed by the caller.
+ */
+__malloc char *audiod_get_decoder_flags(void)
+{
+       int i;
+       char flags[MAX_STREAM_SLOTS + 1];
+
+       FOR_EACH_SLOT(i) {
+               struct slot_info *s = &slot[i];
+               char flag = '0';
+               if (s->receiver_node)
+                       flag += 1;
+               if (s->fns)
+                       flag += 2;
+               if (s->wns)
+                       flag += 4;
+               flags[i] = flag;
+       }
+       flags[MAX_STREAM_SLOTS] = '\0';
+       return para_strdup(flags);
+}
+
 static int get_matching_audio_format_nums(const char *re)
 {
        int i, ret;
@@ -204,28 +270,44 @@ static int get_matching_audio_format_nums(const char *re)
        return ret;
 }
 
+static int get_play_time_slot_num(void)
+{
+       int i, oldest_slot = -1;
+       struct timeval oldest_wstime = {0, 0};
+
+       FOR_EACH_SLOT(i) {
+               struct slot_info *s = &slot[i];
+               struct timeval wstime;
+               if (!s->wns || !s->wns[0].btrn)
+                       continue;
+               btr_get_node_start(s->wns[0].btrn, &wstime);
+               if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
+                       continue;
+               oldest_wstime = wstime;
+               oldest_slot = i;
+       }
+       return oldest_slot;
+}
+
 /**
- * Compute the play time based on information of the given slot.
- *
- * \param slot_num The slot number (negative means: no slot).
+ * Compute the play time based on information of the current slot.
  *
  * This computes a string of the form "0:07 [3:33] (3%/3:40)" using information
  * from the status items received from para_server and the start time of the
- * (first) writer of the given slot.
+ * (first) writer of the current slot.
  *
- * It has to to take into account that probably the stream was not started at
+ * It has to take into account that the stream was probably not started at
  * the beginning of the file, that the clock between the server and the client
  * host may differ and that playback of the stream was delayed, e.g. because
- * the prebuffer filter is used in the filter configuration of the given slot.
+ * the prebuffer filter is used in the filter configuration.
  *
- * If no writer is active in the given slot, or \a slot_num is negative
- * (indicating that para_audiod runs in standby mode), an approximation based
- * only on the status items is computed and the returned string is prefixed
- * with "~".
+ * If no writer is active, for example because para_audiod runs in standby
+ * mode, an approximation based only on the status items is computed and the
+ * returned string is prefixed with "~".
  *
  * \return A string that must be freed by the caller.
  */
-char *get_time_string(int slot_num)
+char *get_time_string(void)
 {
        int ret, seconds = 0, length;
        struct timeval *tmp, sum, sss, /* server stream start */
@@ -233,6 +315,7 @@ char *get_time_string(int slot_num)
                wstime, /* writer start time */
                wtime, /* now - writer start */
                rskip; /* receiver start - sss */
+       int slot_num = get_play_time_slot_num();
        struct slot_info *s = slot_num < 0? NULL : &slot[slot_num];
        char *msg;
 
@@ -308,20 +391,9 @@ empty:
        return para_strdup(NULL);
 }
 
-static int want_colors(void)
-{
-       if (conf.color_arg == color_arg_no)
-               return 0;
-       if (conf.color_arg == color_arg_yes)
-               return 1;
-       if (conf.logfile_given)
-               return 0;
-       return isatty(STDERR_FILENO);
-}
-
 static void parse_config_or_die(void)
 {
-       int ret;
+       int ret, i;
        char *config_file;
        struct audiod_cmdline_parser_params params = {
                .override = 0,
@@ -341,6 +413,7 @@ static void parse_config_or_die(void)
        ret = file_exists(config_file);
        if (conf.config_file_given && !ret) {
                PARA_EMERG_LOG("can not read config file %s\n", config_file);
+               free(config_file);
                goto err;
        }
        if (ret) {
@@ -348,16 +421,35 @@ static void parse_config_or_die(void)
                daemon_set_loglevel(conf.loglevel_arg);
        }
        free(config_file);
+       if (conf.user_allow_given > 0) {
+               uid_whitelist = para_malloc(conf.user_allow_given
+                       * sizeof(uid_t));
+               for (i = 0; i < conf.user_allow_given; i++) {
+                       int32_t val;
+                       struct passwd *pw;
+                       ret = para_atoi32(conf.user_allow_arg[i], &val);
+                       if (ret >= 0) {
+                               uid_whitelist[i] = val;
+                               continue;
+                       }
+                       errno = 0; /* see getpwnam(3) */
+                       pw = getpwnam(conf.user_allow_arg[i]);
+                       if (!pw) {
+                               PARA_EMERG_LOG("invalid username: %s\n",
+                                       conf.user_allow_arg[i]);
+                               goto err;
+                       }
+                       uid_whitelist[i] = pw->pw_uid;
+               }
+       }
        return;
 err:
-       free(config_file);
        exit(EXIT_FAILURE);
 }
 
 static void setup_signal_handling(void)
 {
-       sig_task->fd = para_signal_init();
-       PARA_INFO_LOG("signal pipe: fd %d\n", sig_task->fd);
+       signal_task = signal_init_or_die();
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGHUP);
@@ -385,8 +477,10 @@ static void close_receiver(int slot_num)
                audio_formats[s->format], slot_num);
        a->receiver->close(s->receiver_node);
        btr_remove_node(&s->receiver_node->btrn);
+       task_reap(&s->receiver_node->task);
        free(s->receiver_node);
        s->receiver_node = NULL;
+       stat_task->current_audio_format_num = -1;
        tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
                &a->restart_barrier);
 }
@@ -401,6 +495,7 @@ static void writer_cleanup(struct writer_node *wn)
        PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]);
        w->close(wn);
        btr_remove_node(&wn->btrn);
+       task_reap(&wn->task);
 }
 
 static void close_writers(struct slot_info *s)
@@ -430,14 +525,15 @@ static void close_filters(struct slot_info *s)
                return;
        for (i = a->num_filters - 1; i >= 0; i--) {
                struct filter_node *fn = s->fns + i;
-               struct filter *f;
+               const struct filter *f;
 
                if (!fn)
                        continue;
-               f = filters + fn->filter_num;
+               f = filter_get(fn->filter_num);
                if (f->close)
                        f->close(fn);
                btr_remove_node(&fn->btrn);
+               task_reap(&fn->task);
        }
        free(s->fns);
        s->fns = NULL;
@@ -453,7 +549,7 @@ static void notify_receivers(int error)
                        continue;
                if (!s->receiver_node)
                        continue;
-               task_notify(&s->receiver_node->task, error);
+               task_notify(s->receiver_node->task, error);
        }
 }
 
@@ -491,22 +587,26 @@ static void open_filters(struct slot_info *s)
        s->fns = para_calloc(nf * sizeof(struct filter_node));
        parent = s->receiver_node->btrn;
        for (i = 0; i < nf; i++) {
-               struct filter *f = filters + a->filter_nums[i];
+               char buf[20];
+               const struct filter *f = filter_get(a->filter_nums[i]);
                fn = s->fns + i;
                fn->filter_num = a->filter_nums[i];
                fn->conf = a->filter_conf[i];
-               fn->task.pre_select = f->pre_select;
-               fn->task.post_select = f->post_select;
                fn->btrn = btr_new_node(&(struct btr_node_description)
                        EMBRACE(.name = f->name, .parent = parent,
                                .handler = f->execute, .context = fn));
 
                f->open(fn);
-               register_task(&sched, &fn->task);
+               sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot));
+               fn->task = task_register(&(struct task_info) {
+                       .name = buf,
+                       .pre_select = f->pre_select,
+                       .post_select = f->post_select,
+                       .context = fn,
+               }, &sched);
                parent = fn->btrn;
                PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n",
                        audio_formats[s->format], i,  nf, f->name, (int)(s - slot));
-               sprintf(fn->task.status, "%s (slot %d)", f->name, (int)(s - slot));
        }
 }
 
@@ -520,12 +620,13 @@ static void open_writers(struct slot_info *s)
        assert(s->wns == NULL);
        s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
                * sizeof(struct writer_node));
-       PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]);
        for (i = 0; i < a->num_writers; i++) {
                wn = s->wns + i;
                wn->conf = a->writer_conf[i];
                wn->writer_num = a->writer_nums[i];
                register_writer_node(wn, parent, &sched);
+               PARA_NOTICE_LOG("%s writer started in slot %d\n",
+                       writer_names[a->writer_nums[i]], (int)(s - slot));
        }
 }
 
@@ -559,10 +660,12 @@ static int open_receiver(int format)
        s->receiver_node = rn;
        PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n",
                audio_formats[format], r->name, slot_num);
-       rn->task.pre_select = r->pre_select;
-       rn->task.post_select = r->post_select;
-       sprintf(rn->task.status, "%s receiver node", r->name);
-       register_task(&sched, &rn->task);
+       rn->task = task_register(&(struct task_info) {
+               .name = r->name,
+               .pre_select = r->pre_select,
+               .post_select = r->post_select,
+               .context = rn,
+       }, &sched);
        return slot_num;
 }
 
@@ -577,7 +680,7 @@ static bool receiver_running(void)
 
                if (!s->receiver_node)
                        continue;
-               if (s->receiver_node->task.error >= 0)
+               if (task_status(s->receiver_node->task) >= 0)
                        return true;
                if (ss1 == ss2)
                        return true;
@@ -604,7 +707,7 @@ struct btr_node *audiod_get_btr_root(void)
                struct timeval rstime;
                if (!s->receiver_node)
                        continue;
-               if (s->receiver_node->task.error < 0)
+               if (task_status(s->receiver_node->task) < 0)
                        continue;
                btr_get_node_start(s->receiver_node->btrn, &rstime);
                if (newest_slot >= 0 && tv_diff(&rstime, &newest_rstime, NULL) < 0)
@@ -640,7 +743,7 @@ static bool must_start_decoder(void)
        return true;
 }
 
-static unsigned compute_time_diff(const struct timeval *status_time)
+static void compute_time_diff(const struct timeval *status_time)
 {
        struct timeval tmp, diff;
        static unsigned count;
@@ -648,8 +751,6 @@ static unsigned compute_time_diff(const struct timeval *status_time)
        const struct timeval max_deviation = {0, 500 * 1000};
        const int time_smooth = 5;
 
-       if (!status_time)
-               return count;
        sign = tv_diff(status_time, now, &diff);
 //             PARA_NOTICE_LOG("%s: sign = %i, sa_time_diff_sign = %i\n", __func__,
 //                     sign, sa_time_diff_sign);
@@ -679,7 +780,6 @@ static unsigned compute_time_diff(const struct timeval *status_time)
        );
 out:
        stat_task->sa_time_diff_sign = sa_time_diff_sign;
-       return count;
 }
 
 static int update_item(int itemnum, char *buf)
@@ -758,7 +858,7 @@ static int add_filter(int format, char *cmdline)
        a->filter_conf[nf] = cfg;
        a->num_filters++;
        PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf,
-               filters[filter_num].name);
+               filter_get(filter_num)->name);
        return filter_num;
 }
 
@@ -893,20 +993,20 @@ static int init_default_filters(void)
                }
                /* add "dec" to audio format name */
                tmp = make_message("%sdec", audio_formats[i]);
-               for (j = 0; filters[j].name; j++)
-                       if (!strcmp(tmp, filters[j].name))
+               for (j = 0; filter_get(j); j++)
+                       if (!strcmp(tmp, filter_get(j)->name))
                                break;
                free(tmp);
                ret = -E_UNSUPPORTED_FILTER;
-               if (!filters[j].name)
+               if (!filter_get(j))
                        goto out;
-               tmp = para_strdup(filters[j].name);
+               tmp = para_strdup(filter_get(j)->name);
                ret = add_filter(i, tmp);
                free(tmp);
                if (ret < 0)
                        goto out;
                PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i],
-                       filters[j].name);
+                       filter_get(j)->name);
        }
 out:
        return ret;
@@ -914,7 +1014,7 @@ out:
 
 static int parse_filter_args(void)
 {
-       int i, j, ret, af_mask;
+       int i, j, ret, af_mask, num_matches;
 
        for (i = 0; i < conf.filter_given; i++) {
                char *arg;
@@ -922,13 +1022,18 @@ static int parse_filter_args(void)
                if (ret < 0)
                        goto out;
                af_mask = ret;
+               num_matches = 0;
                FOR_EACH_AUDIO_FORMAT(j) {
                        if ((af_mask & (1 << j)) == 0) /* no match */
                                continue;
                        ret = add_filter(j, arg);
                        if (ret < 0)
                                goto out;
+                       num_matches++;
                }
+               if (num_matches == 0)
+                       PARA_WARNING_LOG("ignoring filter spec: %s\n",
+                               conf.filter_arg[i]);
        }
        ret = init_default_filters(); /* use default values for the rest */
 out:
@@ -952,11 +1057,8 @@ static int parse_stream_args(void)
 }
 
 /* does not unlink socket on errors */
-static int audiod_get_socket(void)
+static void init_local_sockets(struct command_task *ct)
 {
-       struct sockaddr_un unix_addr;
-       int ret, fd;
-
        if (conf.socket_given)
                socket_name = para_strdup(conf.socket_arg);
        else {
@@ -968,91 +1070,111 @@ static int audiod_get_socket(void)
        PARA_NOTICE_LOG("local socket: %s\n", socket_name);
        if (conf.force_given)
                unlink(socket_name);
-       ret = create_local_socket(socket_name, &unix_addr,
+       ct->fd[0] = create_local_socket(socket_name, 0);
+       ct->fd[1] = create_local_socket(socket_name,
                S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
-       if (ret < 0)
-               goto err;
-       fd = ret;
-       if (listen(fd , 5) < 0) {
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-               goto err;
-       }
-       ret = mark_fd_nonblocking(fd);
-       if (ret < 0)
-               goto err;
-       return fd;
-err:
-       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       if (ct->fd[0] >= 0 || ct->fd[1] >= 0)
+               return;
+       PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd[1]));
        exit(EXIT_FAILURE);
 }
 
-static void signal_pre_select(struct sched *s, struct task *t)
+static int signal_post_select(struct sched *s, void *context)
 {
-       struct signal_task *st = container_of(t, struct signal_task, task);
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-}
-
-static int signal_post_select(struct sched *s, __a_unused struct task *t)
-{
-       int signum;
+       struct signal_task *st = context;
+       int ret, signum;
 
+       ret = task_get_notification(st->task);
+       if (ret < 0)
+               return ret;
        signum = para_next_signal(&s->rfds);
        switch (signum) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
-               PARA_EMERG_LOG("terminating on signal %d\n", signum);
-               clean_exit(EXIT_FAILURE, "caught deadly signal");
+               PARA_NOTICE_LOG("received signal %d\n", signum);
+               task_notify_all(s, E_AUDIOD_SIGNAL);
+               return -E_AUDIOD_SIGNAL;
        }
        return 0;
 }
 
-static void signal_setup_default(struct signal_task *st)
+static void command_pre_select(struct sched *s, void *context)
 {
-       st->task.pre_select = signal_pre_select;
-       st->task.post_select = signal_post_select;
-       sprintf(st->task.status, "signal task");
-}
+       struct command_task *ct = context;
+       int i;
 
-static void command_pre_select(struct sched *s, struct task *t)
-{
-       struct command_task *ct = container_of(t, struct command_task, task);
-       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+       for (i = 0; i < 2; i++)
+               if (ct->fd[i] >= 0)
+                       para_fd_set(ct->fd[i], &s->rfds, &s->max_fileno);
 }
 
-static int command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, void *context)
 {
-       int ret;
-       struct command_task *ct = container_of(t, struct command_task, task);
+       int ret, i;
+       struct command_task *ct = context;
        static struct timeval last_status_dump;
-       struct timeval tmp, delay = {0, 500 * 1000};
+       struct timeval tmp, delay;
+       bool force = false;
 
-       tv_add(&last_status_dump, &delay, &tmp);
-       if (tv_diff(&tmp, now, NULL) < 0) {
-               audiod_status_dump();
-               last_status_dump = *now;
+       ret = task_get_notification(ct->task);
+       if (ret < 0)
+               return ret;
+       for (i = 0; i < 2; i++) {
+               if (ct->fd[i] < 0)
+                       continue;
+               ret = handle_connect(ct->fd[i], &s->rfds, uid_whitelist);
+               if (ret < 0) {
+                       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+                       if (ret == -E_AUDIOD_TERM) {
+                               task_notify_all(s, -ret);
+                               return ret;
+                       }
+               } else if (ret > 0)
+                       force = true;
        }
+       if (force == true)
+               goto dump;
 
-       ret = handle_connect(ct->fd, &s->rfds);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-       audiod_status_dump();
-       return 0;
+       /* if last status dump was less than 500ms ago, do nothing */
+       delay.tv_sec = 0;
+       delay.tv_usec = 500 * 1000;
+       tv_add(&last_status_dump, &delay, &tmp);
+       if (tv_diff(now, &tmp, NULL) < 0)
+               return 0;
+
+       /*
+        * If last status dump was more than 5s ago, force update. Otherwise,
+        * update only those items that have changed.
+        */
+       delay.tv_sec = 5;
+       delay.tv_usec = 0;
+       tv_add(&last_status_dump, &delay, &tmp);
+       if (tv_diff(now, &tmp, NULL) > 0)
+               force = true;
+dump:
+       audiod_status_dump(force);
+       last_status_dump = *now;
+       return 1;
 }
 
 static void init_command_task(struct command_task *ct)
 {
-       ct->task.pre_select = command_pre_select;
-       ct->task.post_select = command_post_select;
-       ct->task.error = 0;
-       ct->fd = audiod_get_socket(); /* doesn't return on errors */
-       sprintf(ct->task.status, "command task");
+       init_local_sockets(ct); /* doesn't return on errors */
+
+       ct->task = task_register(&(struct task_info) {
+               .name = "command",
+               .pre_select = command_pre_select,
+               .post_select = command_post_select,
+               .context = ct,
+       }, &sched);
 }
 
 static void close_stat_pipe(void)
 {
        if (!stat_task->ct)
                return;
+       task_reap(&stat_task->ct->task);
        client_close(stat_task->ct);
        stat_task->ct = NULL;
        clear_and_dump_items();
@@ -1060,28 +1182,7 @@ static void close_stat_pipe(void)
        stat_task->offset_seconds = 0;
        stat_task->vss_status = 0;
        stat_task->current_audio_format_num = -1;
-       audiod_status_dump();
-}
-
-/**
- * close the connection to para_server and exit
- *
- * \param status the exit status which is passed to exit(3)
- * \param msg the log message
- *
- * Log \a msg with loglevel \p EMERG, close the connection to para_server if
- * open, and call \p exit(status). \a status should be either EXIT_SUCCESS or
- * EXIT_FAILURE.
- *
- * \sa exit(3)
- */
-void __noreturn clean_exit(int status, const char *msg)
-{
-       PARA_EMERG_LOG("%s\n", msg);
-       if (socket_name)
-               unlink(socket_name);
-       close_stat_pipe();
-       exit(status);
+       audiod_status_dump(true);
 }
 
 /* avoid busy loop if server is down */
@@ -1099,36 +1200,57 @@ static bool must_close_slot(int slot_num)
 
        if (s->format < 0)
                return false;
-       if (s->receiver_node && s->receiver_node->task.error >= 0)
+       if (s->receiver_node && task_status(s->receiver_node->task) >= 0)
                return false;
        for (i = 0; i < a->num_filters; i++)
-               if (s->fns && s->fns[i].task.error >= 0)
+               if (s->fns && task_status(s->fns[i].task) >= 0)
                        return false;
        if (a->num_writers > 0) {
                for (i = 0; i < a->num_writers; i++)
-                       if (s->wns && s->wns[i].task.error >= 0)
+                       if (s->wns && task_status(s->wns[i].task) >= 0)
                                return false;
        } else {
-               if (s->wns && s->wns[0].task.error >= 0)
+               if (s->wns && task_status(s->wns[0].task) >= 0)
                        return false;
        }
        return true;
 }
 
+static void close_slot(int slot_num)
+{
+       struct slot_info *s = slot + slot_num;
+
+       PARA_INFO_LOG("closing slot %d\n", slot_num);
+       close_writers(s);
+       close_filters(s);
+       close_receiver(slot_num);
+       clear_slot(slot_num);
+}
+
 static void close_unused_slots(void)
 {
        int i;
 
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = slot + i;
-               if (!must_close_slot(i))
-                       continue;
-               PARA_INFO_LOG("closing slot %d\n", i);
-               close_writers(s);
-               close_filters(s);
-               close_receiver(i);
-               clear_slot(i);
-       }
+       FOR_EACH_SLOT(i)
+               if (must_close_slot(i))
+                       close_slot(i);
+}
+
+/*
+ * Cleanup all resources.
+ *
+ * This performs various cleanups, removes the audiod socket and closes the
+ * connection to para_server.
+ */
+static void audiod_cleanup(void)
+{
+       if (socket_name)
+               unlink(socket_name);
+       close_stat_pipe();
+       close_unused_slots();
+       audiod_cmdline_parser_free(&conf);
+       close_stat_clients();
+       free(uid_whitelist);
 }
 
 /*
@@ -1139,7 +1261,6 @@ static void start_stop_decoders(void)
 {
        int ret;
        struct slot_info *sl;
-       struct audio_format_info *a;
 
        close_unused_slots();
        if (audiod_status != AUDIOD_ON ||
@@ -1153,17 +1274,15 @@ static void start_stop_decoders(void)
                return;
        }
        sl = slot + ret;
-       a = afi + sl->format;
-       if (a->num_filters)
-               open_filters(sl);
+       open_filters(sl);
        open_writers(sl);
        activate_grab_clients(&sched);
        btr_log_tree(sl->receiver_node->btrn, LL_NOTICE);
 }
 
-static void status_pre_select(struct sched *s, struct task *t)
+static void status_pre_select(struct sched *s, void *context)
 {
-       struct status_task *st = container_of(t, struct status_task, task);
+       struct status_task *st = context;
        int i, ret, cafn = stat_task->current_audio_format_num;
 
        if (must_start_decoder())
@@ -1193,15 +1312,19 @@ min_delay:
 }
 
 /* restart the client task if necessary */
-static int status_post_select(struct sched *s, struct task *t)
+static int status_post_select(struct sched *s, void *context)
 {
-       struct status_task *st = container_of(t, struct status_task, task);
+       struct status_task *st = context;
+       int ret;
 
+       ret = task_get_notification(st->task);
+       if (ret < 0)
+               return ret;
        if (audiod_status == AUDIOD_OFF) {
                if (!st->ct)
                        goto out;
-               if (st->ct->task.error >= 0) {
-                       task_notify(&st->ct->task, E_AUDIOD_OFF);
+               if (task_status(st->ct->task) >= 0) {
+                       task_notify(st->ct->task, E_AUDIOD_OFF);
                        goto out;
                }
                close_stat_pipe();
@@ -1211,26 +1334,26 @@ static int status_post_select(struct sched *s, struct task *t)
        if (st->ct) {
                char *buf;
                size_t sz;
-               int ret;
-               if (st->ct->task.error < 0) {
+
+               ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
+               if (ret < 0) {
                        close_stat_pipe();
                        goto out;
                }
-               if (st->ct->status != CL_RECEIVING)
+               if (st->ct->status != CL_EXECUTING)
                        goto out;
-               ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
-               if (ret <= 0) {
+               if (ret == 0) {
                        struct timeval diff;
                        tv_diff(now, &st->last_status_read, &diff);
                        if (diff.tv_sec > 61)
-                               task_notify(&st->ct->task, E_AUDIOD_OFF);
+                               task_notify(st->ct->task, E_STATUS_TIMEOUT);
                        goto out;
                }
                btr_merge(st->btrn, st->min_iqs);
                sz = btr_next_buffer(st->btrn, &buf);
                ret = for_each_stat_item(buf, sz, update_item);
                if (ret < 0) {
-                       task_notify(&st->ct->task, E_AUDIOD_OFF);
+                       task_notify(st->ct->task, -ret);
                        goto out;
                }
                if (sz != ret) {
@@ -1272,14 +1395,18 @@ out:
 static void init_status_task(struct status_task *st)
 {
        memset(st, 0, sizeof(struct status_task));
-       st->task.pre_select = status_pre_select;
-       st->task.post_select = status_post_select;
        st->sa_time_diff_sign = 1;
        st->clock_diff_count = conf.clock_diff_count_arg;
        st->current_audio_format_num = -1;
-       sprintf(st->task.status, "stat");
        st->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stat"));
+
+       stat_task->task = task_register(&(struct task_info) {
+               .name = "stat",
+               .pre_select = status_pre_select,
+               .post_select = status_post_select,
+               .context = stat_task,
+       }, &sched);
 }
 
 static void set_initial_status(void)
@@ -1315,18 +1442,6 @@ __noreturn static void print_help_and_die(void)
        exit(0);
 }
 
-static void init_colors_or_die(void)
-{
-       int i;
-
-       if (!want_colors())
-               return;
-       daemon_set_default_log_colors();
-       daemon_set_flag(DF_COLOR_LOG);
-       for (i = 0; i < conf.log_color_given; i++)
-               daemon_set_log_color_or_die(conf.log_color_arg[i]);
-}
-
 /**
  * the main function of para_audiod
  *
@@ -1359,9 +1474,11 @@ int main(int argc, char *argv[])
        writer_init();
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
-       drop_privileges_or_die(conf.user_arg, conf.group_arg);
+       daemon_set_priority(conf.priority_arg);
+       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
        parse_config_or_die();
-       init_colors_or_die();
+       daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+               conf.logfile_given, conf.log_color_arg, conf.log_color_given);
        init_random_seed_or_die();
        daemon_set_flag(DF_LOG_TIME);
        daemon_set_flag(DF_LOG_HOSTNAME);
@@ -1377,13 +1494,12 @@ int main(int argc, char *argv[])
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
-       log_welcome("para_audiod");
-       set_server_start_time(NULL);
+       daemon_log_welcome("audiod");
+       daemon_set_start_time();
        set_initial_status();
        FOR_EACH_SLOT(i)
                clear_slot(i);
        setup_signal_handling();
-       signal_setup_default(sig_task);
 
        init_status_task(stat_task);
        init_command_task(cmd_task);
@@ -1391,13 +1507,21 @@ int main(int argc, char *argv[])
        if (conf.daemon_given)
                daemonize(false /* parent exits immediately */);
 
-       register_task(&sched, &sig_task->task);
-       register_task(&sched, &cmd_task->task);
-       register_task(&sched, &stat_task->task);
+       signal_task->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = signal_post_select,
+               .context = signal_task,
+       }, &sched);
+
        sched.default_timeout.tv_sec = 2;
        sched.default_timeout.tv_usec = 999 * 1000;
        ret = schedule(&sched);
+       audiod_cleanup();
+       sched_shutdown(&sched);
+       signal_shutdown(signal_task);
 
-       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       return EXIT_FAILURE;
+       if (ret < 0)
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 6c99d4c7d272e73e686bc8a9cd1d5832b3bdf9de..18c802de8857788449cb6dfbfa53823c7ef0da0e 100644 (file)
@@ -19,17 +19,13 @@ H: Options:
 H:
 H: -m  Change grab mode. Defaults to sloppy grab if not given.
 H:
-H:             -ms: sloppy grab
+H:    -ms: sloppy grab
+H:    -mp: pedantic grab
+H:    -ma: aggressive grab
 H:
-H:             -mp: pedantic grab
-H:
-H:             -ma: aggressive grab
-H:
-H:     The various grab modes only differ in what happens if the
-H:     file descriptor to write the grabbed audio data to is not
-H:     ready for writing (i.e. would block). Sloppy mode ignores
-H:     the write, pedantic mode aborts and aggressive mode tries
-H:     to write anyway.
+H: The various grab modes only differ in what happens if an attempt to
+H: write the grabbed audio data would block. Sloppy mode ignores the
+H: write, pedantic mode aborts and aggressive mode tries to write anyway.
 H:
 H: -p  Grab output of node PARENT of the buffer tree.
 H:
@@ -70,9 +66,14 @@ H: parser-friendly mode.
 N: tasks
 D: list current tasks
 U: tasks
-H: print the list of task ids together with the status of each task
+H: Print the list of task ids together with the status of each task.
 ---
 N: term
 D: terminate audiod
 U: term
 H: Stop all decoders, shut down connection to para_server and exit.
+---
+N: version
+D: print the version of para_audiod
+U: version [-v]
+H: If the -v option is given, a more detailed version text is printed.
index e54a8576dfe5da24d7496e4de39e9f0044573321..b9cfb25e8a34a931dab19e67303813f46ab6b738 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -13,9 +13,6 @@ enum {AUDIOD_AUDIO_FORMATS_ENUM};
 /** array of audio format names supported by para_audiod */
 extern const char *audio_formats[];
 
-/** maximal number of simultaneous instances */
-#define MAX_STREAM_SLOTS 5
-
 /**
  * the possible modes of operation
  *
@@ -25,55 +22,15 @@ extern const char *audio_formats[];
  */
 enum audiod_status_info {AUDIOD_OFF, AUDIOD_ON, AUDIOD_STANDBY};
 
-/** defines one command of para_audiod */
-struct audiod_command {
-       /** the name of the command */
-       const char *name;
-       /** pointer to the function that handles the command */
-       int (*handler)(int, int, char**);
-       /** one-line description of the command */
-       const char *description;
-       /** summary of the command line options */
-       const char *usage;
-       /** the long help text */
-       const char *help;
-};
-
-/**
- * Describes one instance of a receiver-filter-writer chain.
- *
- * \sa receiver_node, receiver, filter, filter_node, writer, writer_node,
- * writer_node_group.
- */
-struct slot_info {
-       /** Number of the audio format in this slot. */
-       int format;
-       /** The stream_start status item announced by para_server.  */
-       struct timeval server_stream_start;
-       /** The offset status item announced by para_server. */
-       unsigned offset_seconds;
-       /** The seconds_total status item announced by para_server. */
-       unsigned seconds_total;
-       /** The receiver info associated with this slot. */
-       struct receiver_node *receiver_node;
-       /** The array of filter nodes. */
-       struct filter_node *fns;
-       /** The array of writers attached to the last filter. */
-       struct writer_node *wns;
-};
-
-extern struct slot_info slot[MAX_STREAM_SLOTS];
 extern struct audiod_args_info conf;
 extern int audiod_status;
 
-void __noreturn clean_exit(int status, const char *msg);
-int handle_connect(int accept_fd, fd_set *rfds);
-void audiod_status_dump(void);
-char *get_time_string(int slot_num);
+int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist);
+void audiod_status_dump(bool force);
+__malloc char *audiod_get_decoder_flags(void);
+char *get_time_string(void);
 struct btr_node *audiod_get_btr_root(void);
 
 void stat_client_write_item(int item_num);
 void clear_and_dump_items(void);
-
-/** iterate over all slots */
-#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++)
+void close_stat_clients(void);
index b49d659e0ac0bd784b2002b941b3471540ee5ac9..278e6ef69e9f38a081aea8637405de8fcdc6e600 100644 (file)
@@ -1,16 +1,22 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file audiod_command.c Commands for para_audiod. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "audiod.cmdline.h"
+#include "audiod.command_list.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
 #include "string.h"
 #include "write.h"
 #include "fd.h"
-#include "audiod_command_list.h"
+#include "version.h"
 
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
+typedef int audiod_command_handler_t(int, int, char **);
+static audiod_command_handler_t AUDIOD_COMMAND_HANDLERS;
+
+/* Defines one command of para_audiod. */
+struct audiod_command {
+       const char *name;
+       /* Pointer to the function that handles the command. */
+       /*
+        * Command handlers must never never close their file descriptor. A
+        * positive return value tells audiod that the status items have
+        * changed. In this case audiod will send an updated version of all
+        * status items to to each connected stat client.
+        */
+       audiod_command_handler_t *handler;
+       /* One-line description. */
+       const char *description;
+       /* Summary of the command line options. */
+       const char *usage;
+       /* The long help text. */
+       const char *help;
+};
 
-struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
 
 /** Iterate over the array of all audiod commands. */
 #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
@@ -57,7 +84,7 @@ struct stat_client {
        int fd;
        /** Bitmask of those status items the client is interested in. */
        uint64_t item_mask;
-       /** See \ref stat_client flags. s*/
+       /** See \ref stat_client flags. */
        unsigned flags;
        /** Its entry in the list of stat clients. */
        struct list_head node;
@@ -92,15 +119,19 @@ static void dump_stat_client_list(void)
 static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
 {
        struct stat_client *new_client;
+       int ret;
 
        if (num_clients >= MAX_STAT_CLIENTS) {
                PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n",
                        MAX_STAT_CLIENTS);
                return -E_TOO_MANY_CLIENTS;
        }
-       PARA_INFO_LOG("adding client on fd %d\n", fd);
-       new_client = para_calloc(sizeof(struct stat_client));
-       new_client->fd = fd;
+       ret = dup(fd);
+       if (ret < 0)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       new_client = para_calloc(sizeof(*new_client));
+       new_client->fd = ret;
+       PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
        new_client->item_mask = mask;
        if (parser_friendly)
                new_client->flags = SCF_PARSER_FRIENDLY;
@@ -109,6 +140,31 @@ static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
        num_clients++;
        return 1;
 }
+
+static void close_stat_client(struct stat_client *sc)
+{
+       PARA_INFO_LOG("closing client fd %d\n", sc->fd);
+       close(sc->fd);
+       list_del(&sc->node);
+       free(sc);
+       num_clients--;
+}
+
+/**
+ * Empty the status clients list.
+ *
+ * This iterates over the list of connected status clients, closes each client
+ * file descriptor and frees the resources.
+ */
+void close_stat_clients(void)
+{
+       struct stat_client *sc, *tmp;
+
+       list_for_each_entry_safe(sc, tmp, &client_list, node)
+               close_stat_client(sc);
+       assert(num_clients == 0);
+}
+
 /**
  * Write a message to all connected status clients.
  *
@@ -127,23 +183,18 @@ void stat_client_write_item(int item_num)
        struct para_buffer *b;
 
        list_for_each_entry_safe(sc, tmp, &client_list, node) {
-               int fd = sc->fd, ret;
+               int ret;
 
                if (!((one << item_num) & sc->item_mask))
                        continue;
                b = (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb;
                if (!b->buf)
-                       (void)WRITE_STATUS_ITEM(b, item_num, "%s\n",
-                               msg? msg : "");
-               ret = write(fd, b->buf, b->offset);
+                       WRITE_STATUS_ITEM(b, item_num, "%s\n", msg? msg : "");
+               ret = write(sc->fd, b->buf, b->offset);
                if (ret == b->offset)
                        continue;
                /* write error or short write */
-               close(fd);
-               num_clients--;
-               PARA_INFO_LOG("deleting client on fd %d\n", fd);
-               list_del(&sc->node);
-               free(sc);
+               close_stat_client(sc);
                dump_stat_client_list();
        }
        free(pb.buf);
@@ -189,46 +240,6 @@ __malloc static char *audiod_status_string(void)
        return para_strdup(status);
 }
 
-static int get_play_time_slot_num(void)
-{
-       int i, oldest_slot = -1;
-       struct timeval oldest_wstime = {0, 0};
-
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               struct timeval wstime;
-               if (!s->wns || !s->wns[0].btrn)
-                       continue;
-               btr_get_node_start(s->wns[0].btrn, &wstime);
-               if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
-                       continue;
-               oldest_wstime = wstime;
-               oldest_slot = i;
-       }
-       //PARA_CRIT_LOG("oldest slot: %d\n", oldest_slot);
-       return oldest_slot;
-}
-
-__malloc static char *decoder_flags(void)
-{
-       int i;
-       char flags[MAX_STREAM_SLOTS + 1];
-
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               char flag = '0';
-               if (s->receiver_node)
-                       flag += 1;
-               if (s->fns)
-                       flag += 2;
-               if (s->wns)
-                       flag += 4;
-               flags[i] = flag;
-       }
-       flags[MAX_STREAM_SLOTS] = '\0';
-       return para_strdup(flags);
-}
-
 static int dump_commands(int fd)
 {
        char *buf = para_strdup(""), *tmp = NULL;
@@ -246,17 +257,10 @@ static int dump_commands(int fd)
        return ret;
 }
 
-/*
- * command handlers don't close their fd on errors (ret < 0) so that
- * its caller can send an error message. Otherwise (ret >= 0) it's up
- * to each individual command to close the fd if necessary.
- */
-
 static int com_help(int fd, int argc, char **argv)
 {
        int i, ret;
        char *buf;
-       const char *dflt = "No such command. Available commands:\n";
 
        if (argc < 2) {
                ret = dump_commands(fd);
@@ -278,25 +282,22 @@ static int com_help(int fd, int argc, char **argv)
                free(buf);
                goto out;
        }
-       ret = client_write(fd, dflt);
+       ret = client_write(fd, "No such command. Available commands:\n");
        if (ret > 0)
                ret = dump_commands(fd);
 out:
-       if (ret >= 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
 {
        char *tl = get_task_list(&sched);
        int ret = 1;
+
        if (tl)
                ret = client_write(fd, tl);
        free(tl);
-       if (ret > 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int com_stat(int fd, int argc, char **argv)
@@ -320,7 +321,6 @@ static int com_stat(int fd, int argc, char **argv)
                if (!strncmp(arg, "-p", 2)) {
                        parser_friendly = 1;
                        b.flags = PBF_SIZE_PREFIX;
-                       continue;
                }
        }
        if (i >= argc)
@@ -336,48 +336,45 @@ static int com_stat(int fd, int argc, char **argv)
                char *item = stat_item_values[i];
                if (!((one << i) & mask))
                        continue;
-               (void)WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
+               WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
        }
        ret = client_write(fd, b.buf);
        if (ret >= 0)
                ret = stat_client_add(fd, mask, parser_friendly);
        free(b.buf);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int com_grab(int fd, int argc, char **argv)
 {
-       return grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, argc, argv, &sched);
+       return ret < 0? ret : 0;
 }
 
-__noreturn static int com_term(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
-       close(fd);
-       clean_exit(EXIT_SUCCESS, "terminating on user request");
+       return -E_AUDIOD_TERM;
 }
 
-static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_ON;
-       close(fd);
        return 1;
 }
 
-static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_OFF;
-       close(fd);
        return 1;
 }
 
-static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_STANDBY;
-       close(fd);
        return 1;
 }
 
-static int com_cycle(int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, int argc, char **argv)
 {
        switch (audiod_status) {
                case  AUDIOD_ON:
@@ -390,18 +387,31 @@ static int com_cycle(int fd, int argc, char **argv)
                        return com_off(fd, argc, argv);
                        break;
        }
-       close(fd);
        return 1;
 }
 
-static int check_perms(uid_t uid)
+static int com_version(int fd, int argc, char **argv)
+{
+       int ret;
+       char *msg;
+
+       if (argc > 1 && strcmp(argv[1], "-v") == 0)
+               msg = make_message("%s", version_text("audiod"));
+       else
+               msg = make_message("%s\n", version_single_line("audiod"));
+       ret = client_write(fd, msg);
+       free(msg);
+       return ret < 0? ret : 0;
+}
+
+static int check_perms(uid_t uid, uid_t *whitelist)
 {
        int i;
 
        if (!conf.user_allow_given)
                return 1;
        for (i = 0; i < conf.user_allow_given; i++)
-               if (uid == conf.user_allow_arg[i])
+               if (uid == whitelist[i])
                        return 1;
        return -E_UCRED_PERM;
 }
@@ -411,6 +421,7 @@ static int check_perms(uid_t uid)
  *
  * \param accept_fd The fd to accept connections on.
  * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
+ * \param uid_whitelist Array of UIDs which are allowed to connect.
  *
  * This is called in each iteration of the select loop. If there is an incoming
  * connection on \a accept_fd, this function reads the command sent by the peer,
@@ -423,7 +434,7 @@ static int check_perms(uid_t uid)
  *
  * \sa para_accept(), recv_cred_buffer()
  * */
-int handle_connect(int accept_fd, fd_set *rfds)
+int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
 {
        int i, argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
@@ -437,8 +448,8 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret < 0)
                goto out;
        uid = ret;
-       PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
-       ret = check_perms(uid);
+       PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
+       ret = check_perms(uid, uid_whitelist);
        if (ret < 0)
                goto out;
        ret = create_argv(buf, "\n", &argv);
@@ -455,27 +466,28 @@ int handle_connect(int accept_fd, fd_set *rfds)
        ret = -E_INVALID_AUDIOD_CMD;
 out:
        free_argv(argv);
-       if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
+       if (ret < 0 && ret != -E_CLIENT_WRITE) {
                char *tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
-               close(clifd);
        }
+       close(clifd);
        return ret;
 }
 
 /**
  * Send the current audiod status to all connected stat clients.
+ *
+ * \param force Whether to write unchanged items.
  */
-void audiod_status_dump(void)
+void audiod_status_dump(bool force)
 {
-       int slot_num = get_play_time_slot_num();
        char *old, *new;
 
        old = stat_item_values[SI_PLAY_TIME];
-       new = get_time_string(slot_num);
+       new = get_time_string();
        if (new) {
-               if (!old || strcmp(old, new)) {
+               if (force || !old || strcmp(old, new)) {
                        free(old);
                        stat_item_values[SI_PLAY_TIME] = new;
                        stat_client_write_item(SI_PLAY_TIME);
@@ -483,9 +495,9 @@ void audiod_status_dump(void)
                        free(new);
        }
 
-       new = get_server_uptime_str(now);
+       new = daemon_get_uptime_str(now);
        old = stat_item_values[SI_AUDIOD_UPTIME];
-       if (!old || strcmp(old, new)) {
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_AUDIOD_UPTIME] = new;
                stat_client_write_item(SI_AUDIOD_UPTIME);
@@ -494,7 +506,7 @@ void audiod_status_dump(void)
 
        old = stat_item_values[SI_AUDIOD_STATUS];
        new = audiod_status_string();
-       if (!old || strcmp(old, new)) {
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_AUDIOD_STATUS] = new;
                stat_client_write_item(SI_AUDIOD_STATUS);
@@ -502,8 +514,8 @@ void audiod_status_dump(void)
                free(new);
 
        old = stat_item_values[SI_DECODER_FLAGS];
-       new = decoder_flags();
-       if (!old || strcmp(old, new)) {
+       new = audiod_get_decoder_flags();
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_DECODER_FLAGS] = new;
                stat_client_write_item(SI_DECODER_FLAGS);
index 2e138de2d5d98dc78796539a30d6f21bd94979c9..d432adbb64aa08a74e4dab7bd21360ebb2ef4227 100755 (executable)
@@ -14,7 +14,12 @@ echo preparing, parallel=$n...
 if test -f Makefile; then
        make maintainer-clean > /dev/null 2>&1
 fi
-autoconf
+autom4te \
+       --language=autoconf \
+       --output=configure \
+       --no-cache \
+       --warnings=all \
+       configure.ac
 autoheader
 echo configuring...
 ./configure $@ > /dev/null
index d35247eb7d47d4d739a61973949f8c564d67bb2f..0eb7074c26532b2f349485de651a2a11e385f5bd 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+# Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
 #
 # Licensed under the GPL v2. For licencing details see COPYING.
 
index ddad67bb177211b3909f88d3be9dcb3d5c381238..1593b99ee5bda9fe6f7e47b6ab63c445a10ad529 100644 (file)
  * For licencing details see COPYING.LIB.
  */
 
-/**
- * \file bitstream.c Bitstream API.
- */
+/** \file bitstream.c Bitstream API for the wma decoder. */
 
-#include <stdlib.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
 #include <regex.h>
 
 #include "para.h"
 #include "wma.h"
 #include "bitstream.h"
 
-/** Read an 8, 16, or 32 bit entity from a VLC table. */
-#define GET_DATA(v, table, i, size) \
-{\
-       const uint8_t *ptr = (const uint8_t *)table + i * size; \
-       switch (size) { \
-       case 1: \
-               v = *(const uint8_t *)ptr; \
-               break; \
-       case 2: \
-               v = *(const uint16_t *)ptr; \
-               break; \
-       default: \
-               v = *(const uint32_t *)ptr; \
-               break; \
-       } \
+static inline uint32_t get_data(const void *table, int i, int size)
+{
+       const uint8_t *ptr = (const uint8_t *)table + i * size;
+       uint32_t v;
+
+       switch (size) {
+       case 1:
+               v = *(const uint8_t *)ptr;
+               break;
+       case 2:
+               v = *(const uint16_t *)ptr;
+               break;
+       default:
+               v = *(const uint32_t *)ptr;
+               break;
+       }
+       return v;
 }
 
 static void alloc_table(struct vlc *vlc, int size)
@@ -58,8 +54,7 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes,
                const void *bits, const void *codes, int codes_size,
                uint32_t code_prefix, int n_prefix)
 {
-       int i, j, k, n, table_size, table_index, nb, n1, idx, code_prefix2,
-               symbol;
+       int i, j, k, n, table_size, table_index, nb, n1, idx;
        uint32_t code;
        VLC_TYPE(*table)[2];
 
@@ -69,62 +64,57 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes,
        table = &vlc->table[table_index];
 
        for (i = 0; i < table_size; i++) {
-               table[i][1] = 0;        //bits
-               table[i][0] = -1;       //codes
+               table[i][1] = 0; /* bits */
+               table[i][0] = -1; /* codes */
        }
 
-       /* map codes and compute auxillary table sizes */
+       /* map codes and compute auxiliary table sizes */
        for (i = 0; i < nb_codes; i++) {
-               GET_DATA(n, bits, i, 1);
-               GET_DATA(code, codes, i, codes_size);
+               n = get_data(bits, i, 1);
                /* we accept tables with holes */
+               n -= n_prefix;
                if (n <= 0)
                        continue;
-               symbol = i;
+               code = get_data(codes, i, codes_size);
                /* if code matches the prefix, it is in the table */
-               n -= n_prefix;
-               code_prefix2 = code >> n;
-               if (n <= 0 || code_prefix2 != code_prefix)
+               if ((code >> n) != code_prefix)
                        continue;
                if (n <= table_nb_bits) {
                        /* no need to add another table */
                        j = (code << (table_nb_bits - n)) & (table_size - 1);
                        nb = 1 << (table_nb_bits - n);
                        for (k = 0; k < nb; k++) {
-                               if (table[j][1] /* bits */ != 0) {
-                                       PARA_EMERG_LOG("incorrect code\n");
-                                       exit(EXIT_FAILURE);
-                               }
-                               table[j][1] = n;        //bits
-                               table[j][0] = symbol;
+                               assert(table[j][1] == 0); /* incorrect code */
+                               table[j][1] = n; /* bits */
+                               table[j][0] = i;
                                j++;
                        }
                } else {
                        n -= table_nb_bits;
                        j = (code >> n) & ((1 << table_nb_bits) - 1);
                        /* compute table size */
-                       n1 = -table[j][1];      //bits
+                       n1 = -table[j][1]; /* bits */
                        if (n > n1)
                                n1 = n;
-                       table[j][1] = -n1;      //bits
+                       table[j][1] = -n1; /* bits */
                }
        }
 
-       /* fill auxillary tables recursively */
+       /* fill auxiliary tables recursively */
        for (i = 0; i < table_size; i++) {
-               n = table[i][1];        //bits
+               n = table[i][1]; /* bits */
                if (n < 0) {
                        n = -n;
                        if (n > table_nb_bits) {
                                n = table_nb_bits;
-                               table[i][1] = -n;       //bits
+                               table[i][1] = -n; /* bits */
                        }
                        idx = build_table(vlc, n, nb_codes, bits, codes,
                                codes_size, (code_prefix << table_nb_bits) | i,
                                n_prefix + table_nb_bits);
                        /* vlc->table might have changed */
                        table = &vlc->table[table_index];
-                       table[i][0] = idx;      //code
+                       table[i][0] = idx; /* code */
                }
        }
        return table_index;
@@ -134,19 +124,15 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes,
  * Build VLC decoding tables suitable for use with get_vlc().
  *
  * \param vlc The structure to be initialized.
- *
- * \param nb_bits Set the decoding table size (2^nb_bits) entries. The bigger
- * it is, the faster is the decoding. But it should not be too big to save
- * memory and L1 cache. '9' is a good compromise.
- *
- * \param nb_codes Number of vlcs codes.
- *
+ * \param nb_bits Set the decoding table size (2^nb_bits) entries.
+ * \param nb_codes Number of vlc codes.
  * \param bits Table which gives the size (in bits) of each vlc code.
- *
  * \param codes Table which gives the bit pattern of of each vlc code.
- *
  * \param codes_size The number of bytes of each entry of the \a codes tables.
  *
+ * The bigger \a nb_bits is, the faster is the decoding. But it should not be
+ * too big to save memory and L1 cache. '9' is a good compromise.
+ *
  * The wrap and size parameters allow to use any memory configuration and
  * types (byte/word/long) to store the bits and codes tables.
  */
@@ -178,15 +164,14 @@ void free_vlc(struct vlc *vlc)
  * Parse a vlc code.
  *
  * \param gbc The getbit context structure.
- *
  * \param table The vlc tables to use.
- *
- * \param bits The number of bits which will be read at once, must be
- * identical to nb_bits in init_vlc().
- *
- * \param max_depth The number of times bits bits must be read to completely
+ * \param bits The number of bits which will be read at once.
+ * \param max_depth The number of times \a bits bits must be read to completely
  * read the longest vlc code = (max_vlc_length + bits - 1) / bits.
  *
+ * The \a bits parameter must be identical to the \a nb_bits value supplied to
+ * \ref init_vlc().
+ *
  * \return The vlc code.
  */
 int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits,
index a3393380e5e02801a4312c6f904c1ea25527aad9..5890d08c89d4033f7fefb9a19be525860a701d77 100644 (file)
@@ -69,11 +69,13 @@ static inline unsigned int get_bit(struct getbit_context *gbc)
 /**
  * Initialize a getbit_context structure.
  *
- * \param buffer The bitstream buffer. It must be 4 bytes larger then the
- * actual read bits because the bitstream reader might read 32 bits at once and
- * could read over the end.
+ * \param gbc The structure to initialize.
+ * \param buffer The bitstream buffer.
+ * \param size The size of the buffer in bytes.
  *
- * \param bit_size The size of the buffer in bytes.
+ * The bitstream buffer must be 4 bytes larger then the actual read bits
+ * because the bitstream reader might read 32 bits at once and could read over
+ * the end.
  */
 static inline void init_get_bits(struct getbit_context *gbc,
                const uint8_t *buffer, int size)
@@ -88,4 +90,3 @@ void init_vlc(struct vlc *vlc, int nb_bits, int nb_codes, const void *bits,
 void free_vlc(struct vlc *vlc);
 int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits,
                int max_depth);
-
diff --git a/blob.c b/blob.c
index 44e58a1062a3ce4bf27cb0f620cefb4fdca5b329..2e0a4762bfd1e34d0f12c7a9b2d4de582c8316d4 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -66,7 +66,7 @@ static struct osl_column_description blob_cols[] = {
 
 /** Define an osl table description for a blob table. */
 #define DEFINE_BLOB_TABLE_DESC(table_name) \
-       struct osl_table_description table_name ## _table_desc = { \
+       static struct osl_table_description table_name ## _table_desc = { \
                .name = #table_name, \
                .num_columns = NUM_BLOB_COLUMNS, \
                .flags = OSL_LARGE_TABLE, \
@@ -100,75 +100,60 @@ enum blob_ls_flags {
        BLOB_LS_FLAG_SORT_BY_ID = 4,
 };
 
-/** Structure passed to the \p print_blob function. */
-struct lsblob_action_data {
-       /** The flags given at the command line. */
-       uint32_t flags;
-       /** Message buffer. */
-       struct para_buffer pb;
-};
-
 static int print_blob(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
-       struct lsblob_action_data *lbad = data;
+       struct afs_callback_arg *aca = data;
+       uint32_t flags = *(uint32_t *)aca->query.data;
        struct osl_object obj;
        uint32_t id;
        int ret;
 
-       if (!(lbad->flags & BLOB_LS_FLAG_LONG))
-               return para_printf(&lbad->pb, "%s\n", name);
+       if (!(flags & BLOB_LS_FLAG_LONG)) {
+               para_printf(&aca->pbout, "%s\n", name);
+               return 0;
+       }
        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
-               para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret));
+               para_printf(&aca->pbout, "cannot list %s\n", name);
                return ret;
        }
        id = *(uint32_t *)obj.data;
-       return para_printf(&lbad->pb, "%u\t%s\n", id, name);
+       para_printf(&aca->pbout, "%u\t%s\n", id, name);
+       return 1;
 }
 
-static void com_lsblob_callback(struct osl_table *table,
-               int fd, const struct osl_object *query)
+static int com_lsblob_callback(struct osl_table *table,
+               struct afs_callback_arg *aca)
 {
-       struct lsblob_action_data lbad = {
-               .flags = *(uint32_t *)query->data,
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &(struct afs_max_size_handler_data) {
-                               .fd = fd,
-                               .band = SBD_OUTPUT
-                       },
-                       .max_size_handler = afs_max_size_handler,
-               }
-       };
+       uint32_t flags = *(uint32_t *)aca->query.data;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = {.data = (char *)query->data + sizeof(uint32_t),
-                       .size = query->size - sizeof(uint32_t)},
+               .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
+                       .size = aca->query.size - sizeof(uint32_t)},
                .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING | PM_SKIP_EMPTY_NAME,
                .match_col_num = BLOBCOL_NAME,
-               .data = &lbad,
+               .data = aca,
                .action = print_blob,
        };
        int ret;
 
-       if (lbad.flags & BLOB_LS_FLAG_REVERSE)
+       if (flags & BLOB_LS_FLAG_REVERSE)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       if (!(lbad.flags & BLOB_LS_FLAG_SORT_BY_ID))
+       if (!(flags & BLOB_LS_FLAG_SORT_BY_ID))
                pmd.loop_col_num = BLOBCOL_NAME;
        else
                pmd.loop_col_num = BLOBCOL_ID;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               para_printf(&lbad.pb, "%s\n", para_strerror(-ret));
-       else if (pmd.num_matches == 0 && pmd.patterns.size > 0)
-               para_printf(&lbad.pb, "no matches\n");
-       if (lbad.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, lbad.pb.buf, lbad.pb.offset);
-       free(lbad.pb.buf);
+               goto out;
+       if (pmd.num_matches == 0 && pmd.patterns.size > 0)
+               ret = -E_NO_MATCH;
+out:
+       return ret;
 }
 
-static int com_lsblob(callback_function *f, struct command_context *cc)
+static int com_lsblob(afs_callback *f, struct command_context *cc)
 {
        uint32_t flags = 0;
        struct osl_object options = {.data = &flags, .size = sizeof(flags)};
@@ -217,26 +202,28 @@ static int cat_blob(struct osl_table *table, struct osl_row *row,
        return (ret < 0)? ret : ret2;
 }
 
-static void com_catblob_callback(struct osl_table *table, int fd,
-               const struct osl_object *query)
+static int com_catblob_callback(struct osl_table *table,
+               struct afs_callback_arg *aca)
 {
+       int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = *query,
+               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
-               .data = &fd,
+               .data = &aca->fd,
                .action = cat_blob
        };
-       for_each_matching_row(&pmd);
-       if (pmd.num_matches == 0) {
-               char err_msg[] = "no matches\n";
-               pass_buffer_as_shm(fd, SBD_OUTPUT, err_msg, sizeof(err_msg));
-       }
+       ret = for_each_matching_row(&pmd);
+       if (ret < 0)
+               return ret;
+       if (pmd.num_matches == 0)
+               ret = -E_NO_MATCH;
+       return ret;
 }
 
-static int com_catblob(callback_function *f, struct command_context *cc)
+static int com_catblob(afs_callback *f, struct command_context *cc)
 {
        if (cc->argc < 2)
                return -E_BLOB_SYNTAX;
@@ -244,66 +231,46 @@ static int com_catblob(callback_function *f, struct command_context *cc)
                afs_cb_result_handler, cc);
 }
 
-/** Used for removing rows from a blob table. */
-struct rmblob_data {
-       /** Message buffer. */
-       struct para_buffer pb;
-};
-
 static int remove_blob(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
-       struct rmblob_data *rmbd = data;
+       struct afs_callback_arg *aca = data;
        int ret = osl(osl_del_row(table, row));
        if (ret < 0) {
-               para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
+               para_printf(&aca->pbout, "cannot remove %s\n", name);
                return ret;
        }
        return 1;
 }
 
-static void com_rmblob_callback(struct osl_table *table, int fd,
-               const struct osl_object *query)
+static int com_rmblob_callback(struct osl_table *table,
+               struct afs_callback_arg *aca)
 {
-       int ret, ret2 = 0;
-       struct rmblob_data rmbd = {
-               .pb = {
-                       .max_size = shm_get_shmmax(),
-                       .private_data = &(struct afs_max_size_handler_data) {
-                               .fd = fd,
-                               .band = SBD_OUTPUT
-                       },
-                       .max_size_handler = afs_max_size_handler,
-               }
-       };
+       int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = *query,
+               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
-               .data = &rmbd,
+               .data = aca,
                .action = remove_blob
        };
        ret = for_each_matching_row(&pmd);
-       if (ret < 0) {
-               ret2 = para_printf(&rmbd.pb, "%s\n", para_strerror(-ret));
-               if (ret2 < 0)
-                       goto out;
-       }
+       if (ret < 0)
+               goto out;
        if (pmd.num_matches == 0)
-               ret2 = para_printf(&rmbd.pb, "no matches, nothing removed\n");
+               ret = -E_NO_MATCH;
        else {
-               ret2 = para_printf(&rmbd.pb, "removed %d blobs\n", pmd.num_matches);
-               afs_event(BLOB_REMOVE, NULL, table);
+               para_printf(&aca->pbout, "removed %d blob(s)\n",
+                       pmd.num_matches);
+               ret = afs_event(BLOB_REMOVE, NULL, table);
        }
 out:
-       if (ret2 >= 0 && rmbd.pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, rmbd.pb.buf, rmbd.pb.offset);
-       free(rmbd.pb.buf);
+       return ret;
 }
 
-static int com_rmblob(callback_function *f, struct command_context *cc)
+static int com_rmblob(afs_callback *f, struct command_context *cc)
 {
        if (cc->argc < 2)
                return -E_MOOD_SYNTAX;
@@ -311,11 +278,11 @@ static int com_rmblob(callback_function *f, struct command_context *cc)
                afs_cb_result_handler, cc);
 }
 
-static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
-               const struct osl_object *query)
+static int com_addblob_callback(struct osl_table *table,
+               struct afs_callback_arg *aca)
 {
        struct osl_object objs[NUM_BLOB_COLUMNS];
-       char *name = query->data;
+       char *name = aca->query.data;
        size_t name_len = strlen(name) + 1;
        uint32_t id;
        unsigned num_rows;
@@ -344,8 +311,12 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
                if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                        goto out;
                if (ret >= 0) { /* we already have a blob with this name */
+                       ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
+                       if (ret < 0)
+                               goto out;
+                       id = *(uint32_t *)obj.data;
                        obj.data = name + name_len;
-                       obj.size = query->size - name_len;
+                       obj.size = aca->query.size - name_len;
                        ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj));
                        goto out;
                }
@@ -370,95 +341,89 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
        objs[BLOBCOL_NAME].data = name;
        objs[BLOBCOL_NAME].size = name_len;
        objs[BLOBCOL_DEF].data = name + name_len;
-       objs[BLOBCOL_DEF].size = query->size - name_len;
+       objs[BLOBCOL_DEF].size = aca->query.size - name_len;
        ret = osl(osl_add_row(table, objs));
        if (ret < 0)
                goto out;
-       afs_event(BLOB_ADD, NULL, table);
+       ret = afs_event(BLOB_ADD, NULL, table);
 out:
        if (ret < 0)
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+               para_printf(&aca->pbout, "cannot add %s\n", name);
+       else
+               para_printf(&aca->pbout, "added %s as id %u\n", name, id);
+       return ret;
 }
 
-/*
- * write input from fd to dynamically allocated buffer,
- * but maximal max_size byte.
- */
-static int fd2buf(struct stream_cipher_context *scc, unsigned max_size, struct osl_object *obj)
+/* Write input from fd to dynamically allocated buffer, but maximal 10M. */
+static int fd2buf(struct stream_cipher_context *scc, struct osl_object *obj)
 {
-       const size_t chunk_size = 1024;
-       size_t size = 2048, received = 0;
+       size_t max_size = 10 * 1024 * 1024;
        int ret;
-       char *buf = para_malloc(size);
+       struct iovec iov;
 
-       for (;;) {
-               ret = sc_recv_bin_buffer(scc, buf + received, chunk_size);
-               if (ret <= 0)
-                       break;
-               received += ret;
-               if (received + chunk_size >= size) {
-                       size *= 2;
-                       ret = -E_INPUT_TOO_LARGE;
-                       if (size > max_size)
-                               break;
-                       buf = para_realloc(buf, size);
-               }
+       obj->data = NULL;
+       obj->size = 0;
+again:
+       do {
+               ret = recv_sb(scc, SBD_BLOB_DATA, max_size, &iov);
+       } while (ret == 0);
+
+       if (ret < 0) {
+               free(obj->data);
+               obj->data = NULL;
+               obj->size = 0;
+               return ret;
        }
-       obj->data = buf;
-       obj->size = received;
-       if (ret < 0)
-               free(buf);
-       return ret;
+       if (iov.iov_len == 0) /* end of blob */
+               return 1;
+       if (!obj->data) {
+               obj->data = iov.iov_base;
+               obj->size = iov.iov_len;
+       } else {
+               obj->data = para_realloc(obj->data, obj->size + iov.iov_len);
+               memcpy(obj->data + obj->size, iov.iov_base, iov.iov_len);
+               obj->size += iov.iov_len;
+               free(iov.iov_base);
+               max_size -= iov.iov_len;
+       }
+       goto again;
+       return 1;
 }
 
 /*
- * Read data from a file descriptor, and send it to the afs process.
- *
- * \param scc crypt context containing the file descriptor to read data from.
- * \param arg_obj Pointer to the arguments to \a f.
- * \param f The callback function.
- * \param max_len Don't read more than that many bytes from stdin.
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
- *
- * This function is used by commands that wish to let para_server store
- * arbitrary data specified by the user (for instance the add_blob family of
- * commands). First, at most \a max_len bytes are read and decrypted from the
- * file descriptor given by \a scc. The result is concatenated with the buffer
- * given by \a arg_obj, and the combined buffer is made available to the afs
- * process via the callback method. See \ref send_callback_request for details.
+ * Read blob from a file descriptor and send it to afs.
  *
- * \return Negative on errors, the return value of the underlying call to
- * send_callback_request() otherwise.
+ * This function is called from the addblob command handlers to instruct the
+ * afs process to store the input in a blob table. Input is read and decrypted
+ * from the file descriptor given by cc and appended to arg_obj, which contains
+ * the name of the blob to create. The combined buffer is made available to the
+ * afs process via the callback method.
  */
 static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
-               callback_function *f, unsigned max_len,
-               callback_result_handler *result_handler,
-               void *private_result_data)
+               afs_callback *f)
 {
        struct osl_object query, stdin_obj;
        int ret;
 
-       if (cc->use_sideband)
-               ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
-       else
-               ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
+       ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
        if (ret < 0)
                return ret;
-       ret = fd2buf(&cc->scc, max_len, &stdin_obj);
+       ret = fd2buf(&cc->scc, &stdin_obj);
        if (ret < 0)
                return ret;
        query.size = arg_obj->size + stdin_obj.size;
        query.data = para_malloc(query.size);
        memcpy(query.data, arg_obj->data, arg_obj->size);
-       memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size);
+       if (stdin_obj.size > 0)
+               memcpy((char *)query.data + arg_obj->size, stdin_obj.data,
+                       stdin_obj.size);
        free(stdin_obj.data);
-       ret = send_callback_request(f, &query, result_handler, private_result_data);
+       ret = send_callback_request(f, &query, afs_cb_result_handler, cc);
        free(query.data);
        return ret;
 }
 
-static int com_addblob(callback_function *f, struct command_context *cc)
+static int com_addblob(afs_callback *f, struct command_context *cc)
 {
        struct osl_object arg_obj;
 
@@ -468,44 +433,47 @@ static int com_addblob(callback_function *f, struct command_context *cc)
                return -E_BLOB_SYNTAX;
        arg_obj.size = strlen(cc->argv[1]) + 1;
        arg_obj.data = (char *)cc->argv[1];
-       return stdin_command(cc, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
+       return stdin_command(cc, &arg_obj, f);
 }
 
-/* FIXME: Print output to client, not to log file */
-static void com_mvblob_callback(struct osl_table *table, __a_unused int fd,
-               const struct osl_object *query)
+static int com_mvblob_callback(struct osl_table *table,
+               struct afs_callback_arg *aca)
 {
-       char *src = (char *) query->data;
+       char *src = (char *)aca->query.data;
        struct osl_object obj = {.data = src, .size = strlen(src) + 1};
        char *dest = src + obj.size;
        struct osl_row *row;
        int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
-       if (ret < 0)
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot find source blob %s\n", src);
                goto out;
+       }
        obj.data = dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
-       if (ret < 0)
+       if (ret < 0) {
+               para_printf(&aca->pbout, "cannot rename blob %s to %s\n",
+                       src, dest);
                goto out;
-       afs_event(BLOB_RENAME, NULL, table);
+       }
+       ret = afs_event(BLOB_RENAME, NULL, table);
 out:
-       if (ret < 0)
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       return ret;
 }
 
-static int com_mvblob(callback_function *f, struct command_context *cc)
+static int com_mvblob(afs_callback *f, struct command_context *cc)
 {
        if (cc->argc != 3)
                return -E_MOOD_SYNTAX;
        return send_option_arg_callback_request(NULL, cc->argc - 1,
-               cc->argv + 1, f, NULL, NULL);
+               cc->argv + 1, f, afs_cb_result_handler, cc);
 }
 
 #define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
-       static void com_ ## cmd_name ## cmd_prefix ## _callback(int fd, const struct osl_object *query) \
+       static int com_ ## cmd_name ## cmd_prefix ## _callback(struct afs_callback_arg *aca) \
        { \
-               return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \
+               return com_ ## cmd_name ## blob_callback(table_name ## _table, aca); \
        } \
        int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
        { \
index aa9f1cdb0c95b77635981cea0e6bb1588bbcdc03..b0cd6665416627806d922cda50fac2e2eeeab17f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -544,14 +544,16 @@ static bool btr_no_children(struct btr_node *btrn)
 }
 
 /**
- * Find out whether a node is an orphan node.
+ * Find out whether a node is an orphan.
  *
  * \param btrn The buffer tree node.
  *
  * \return True if \a btrn has no parent.
  *
- * This function will always return true for the root node.  However in case
- * nodes have been removed from the tree, other nodes may become orphans too.
+ * This function returns true for the root node and false for any other node.
+ *
+ * After a (non-leaf) node was removed removed from the tree, the function
+ * returns true for all child nodes.
  */
 bool btr_no_parent(struct btr_node *btrn)
 {
@@ -769,6 +771,12 @@ void btr_drain(struct btr_node *btrn)
                btr_drop_buffer_reference(br);
 }
 
+static void btr_free_node(struct btr_node *btrn)
+{
+       free(btrn->name);
+       free(btrn);
+}
+
 /**
  * Remove a node from a buffer tree.
  *
@@ -796,8 +804,7 @@ void btr_remove_node(struct btr_node **btrnp)
        btr_drain(btrn);
        if (btrn->parent)
                list_del(&btrn->node);
-       free(btrn->name);
-       free(btrn);
+       btr_free_node(btrn);
 out:
        *btrnp = NULL;
 }
@@ -833,7 +840,7 @@ size_t btr_get_input_queue_size(struct btr_node *btrn)
 /**
  * Remove a node from the buffer tree, reconnecting parent and children.
  *
- * \param btrn The node to splice out.
+ * \param btrnp The node to splice out.
  *
  * This function is used by buffer tree nodes that do not exist during the
  * whole lifetime of the buffer tree. Unlike btr_remove_node(), calling
@@ -841,9 +848,9 @@ size_t btr_get_input_queue_size(struct btr_node *btrn)
  * but reconnects the buffer tree by making all child nodes of \a btrn children
  * of the parent of \a btrn.
  */
-void btr_splice_out_node(struct btr_node *btrn)
+void btr_splice_out_node(struct btr_node **btrnp)
 {
-       struct btr_node *ch, *tmp;
+       struct btr_node *btrn = *btrnp, *ch, *tmp;
 
        assert(btrn);
        PARA_NOTICE_LOG("splicing out %s\n", btrn->name);
@@ -860,7 +867,8 @@ void btr_splice_out_node(struct btr_node *btrn)
                        list_del(&ch->node);
        }
        assert(list_empty(&btrn->children));
-       btrn->parent = NULL;
+       btr_free_node(btrn);
+       *btrnp = NULL;
 }
 
 /**
@@ -1201,21 +1209,20 @@ int btr_node_status(struct btr_node *btrn, size_t min_iqs,
        size_t iqs;
 
        assert(btrn);
-       if (type != BTR_NT_LEAF) {
-               if (btr_no_children(btrn))
-                       return -E_BTR_NO_CHILD;
-               if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING)
-                       return 0;
-       }
-       if (type != BTR_NT_ROOT) {
-               if (btr_eof(btrn))
-                       return -E_BTR_EOF;
-               iqs = btr_get_input_queue_size(btrn);
-               if (iqs == 0) /* we have a parent, because not eof */
-                       return 0;
-               if (iqs < min_iqs && !btr_no_parent(btrn))
-                       return 0;
-       }
+       if (type != BTR_NT_LEAF && btr_no_children(btrn))
+               return -E_BTR_NO_CHILD;
+       if (type != BTR_NT_ROOT && btr_eof(btrn))
+               return -E_BTR_EOF;
+
+       if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING)
+               return 0;
+       if (type == BTR_NT_ROOT)
+               return 1;
+       iqs = btr_get_input_queue_size(btrn);
+       if (iqs == 0) /* we have a parent, because not eof */
+               return 0;
+       if (iqs < min_iqs && !btr_no_parent(btrn))
+               return 0;
        return 1;
 }
 
index 20dcd62dda5a531fdf10fe4a01b3669dad39e2ca..3ee469c91b3fe56583b305fbb5dcb92d6ac9a4aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -192,7 +192,7 @@ size_t btr_next_buffer(struct btr_node *btrn, char **bufp);
 size_t btr_next_buffer_omit(struct btr_node *btrn, size_t omit, char **bufp);
 void btr_consume(struct btr_node *btrn, size_t numbytes);
 int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result);
-void btr_splice_out_node(struct btr_node *btrn);
+void btr_splice_out_node(struct btr_node **btrnp);
 void btr_pushdown(struct btr_node *btrn);
 void *btr_context(struct btr_node *btrn);
 void btr_merge(struct btr_node *btrn, size_t dest_size);
index 11459e08c375c62285664637125c599f0e5b02be..d1d278724f123100a96358779c1cff4520db177d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index ba1a8eca48f2bf9407c7f3efbbdbd74e7414b2c7..0957fe0364c914b7f50c3c1148ef16cbb2350f31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index c870d4d2a0d397d17777f09fe3f94ff527cfc86a..c9f47b2b009174f92d813e9ebd2c9e02eb25d09c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -13,6 +13,7 @@
 #include "afh.h"
 #include "string.h"
 #include "error.h"
+#include "chunk_queue.h"
 
 /**
  * Senders may use the chunk queue facility to deal with laggy connections.  It
index 7514682464b787295863119b90a03ae5b9eb8439..1e7fed2d4f5b5fdeef4680169a31fe480ecbb72b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 40ae9bc87f432b3812182d44da7953052afb2a4b..31cfff09798b2089e2b815a544fd207464f0e76c 100644 (file)
--- a/client.c
+++ b/client.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -35,28 +35,28 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = stderr_log;
 
 #ifdef HAVE_READLINE
 #include "interactive.h"
-#include "server_completion.h"
-#include "afs_completion.h"
+#include "server.completion.h"
+#include "afs.completion.h"
 
 struct exec_task {
-       struct task task;
+       struct task *task;
        struct btr_node *btrn;
        char *result_buf;
        size_t result_size;
 };
 
-static void exec_pre_select(struct sched *s, struct task *t)
+static void exec_pre_select(struct sched *s, void *context)
 {
-       struct exec_task *et = container_of(t, struct exec_task, task);
+       struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
 
        if (ret != 0)
                sched_min_delay(s);
 }
 
-static int exec_post_select(__a_unused struct sched *s, struct task *t)
+static int exec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct exec_task *et = container_of(t, struct exec_task, task);
+       struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
        char *buf;
        size_t sz;
@@ -93,11 +93,6 @@ static int execute_client_command(const char *cmd, char **result)
        int ret;
        struct sched command_sched = {.default_timeout = {.tv_sec = 1}};
        struct exec_task exec_task = {
-               .task = {
-                       .pre_select = exec_pre_select,
-                       .post_select = exec_post_select,
-                       .status = "client exec task",
-               },
                .result_buf = para_strdup(""),
                .result_size = 1,
        };
@@ -107,14 +102,19 @@ static int execute_client_command(const char *cmd, char **result)
                goto out;
        exec_task.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "exec_collect"));
-       register_task(&command_sched, &exec_task.task);
+       exec_task.task = task_register(&(struct task_info) {
+               .name = "client exec",
+               .pre_select = exec_pre_select,
+               .post_select = exec_post_select,
+               .context = &exec_task,
+       }, &command_sched);
        ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
        if (ret < 0)
                goto out;
        schedule(&command_sched);
+       sched_shutdown(&command_sched);
        *result = exec_task.result_buf;
        btr_remove_node(&exec_task.btrn);
-       client_disconnect(ct);
        ret = 1;
 out:
        btr_remove_node(&exec_task.btrn);
@@ -204,10 +204,10 @@ I9E_DUMMY_COMPLETER(pause);
 I9E_DUMMY_COMPLETER(play);
 I9E_DUMMY_COMPLETER(si);
 I9E_DUMMY_COMPLETER(term);
-I9E_DUMMY_COMPLETER(version);
 I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(addatt);
 I9E_DUMMY_COMPLETER(init);
+I9E_DUMMY_COMPLETER(tasks);
 
 static struct i9e_completer completers[];
 
@@ -217,6 +217,13 @@ static void help_completer(struct i9e_completion_info *ci,
        result->matches = i9e_complete_commands(ci->word, completers);
 }
 
+static void version_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       char *opts[] = {"-v", NULL};
+       i9e_complete_option(opts, ci, cr);
+}
+
 static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -269,9 +276,9 @@ static void ls_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
        char *opts[] = {
-               "--", "-l", "-ls", "-ll", "-lv", "-lp", "-lm", "-lc", "-p",
-               "-a", "-r", "-d", "-sp", "-sl", "-ss", "-sn", "-sf", "-sc",
-               "-si", "-sy", "-sb", "-sd", "-sa", NULL
+               "--", "-l", "-l=s", "-l=l", "-l=v", "-l=p", "-l=m", "-l=c",
+               "-p", "-a", "-r", "-d", "-s=p", "-s=l", "-s=s", "-s=n", "-s=f",
+               "-s=c", "-s=i", "-s=y", "-s=b", "-s=d", "-s=a", NULL
        };
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -440,7 +447,6 @@ static int client_i9e_line_handler(char *line)
 {
        int ret;
 
-       client_disconnect(ct);
        PARA_DEBUG_LOG("line: %s\n", line);
        ret = make_client_argv(line);
        if (ret <= 0)
@@ -448,7 +454,7 @@ static int client_i9e_line_handler(char *line)
        ret = client_connect(ct, &sched, NULL, NULL);
        if (ret < 0)
                return ret;
-       i9e_attach_to_stdout(ct->btrn);
+       i9e_attach_to_stdout(ct->btrn[0]);
        return 1;
 }
 
@@ -493,6 +499,7 @@ __noreturn static void interactive_session(void)
                goto out;
        para_log = i9e_log;
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        i9e_close();
        para_log = stderr_log;
 out:
@@ -524,27 +531,31 @@ __noreturn static void print_completions(void)
 
 #endif /* HAVE_READLINE */
 
-static int supervisor_post_select(struct sched *s, __a_unused struct task *t)
+struct supervisor_task {
+       bool stdout_task_started;
+       struct task *task;
+};
+
+static int supervisor_post_select(struct sched *s, void *context)
 {
-       if (ct->task.error < 0)
-               return ct->task.error;
-       if (ct->status == CL_SENDING) {
-               stdin_set_defaults(&sit);
-               register_task(s, &sit.task);
-               return -E_TASK_STARTED;
+       struct supervisor_task *svt = context;
+       int ret = task_status(ct->task);
+
+       if (ret < 0)
+               return ret;
+       if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
+               stdout_task_register(&sot, s);
+               svt->stdout_task_started = true;
+               return 1;
        }
-       if (ct->status == CL_RECEIVING) {
-               stdout_set_defaults(&sot);
-               register_task(s, &sot.task);
+       if (ct->status == CL_SENDING) {
+               stdin_task_register(&sit, s);
                return -E_TASK_STARTED;
        }
        return 0;
 }
 
-static struct task svt = {
-       .post_select = supervisor_post_select,
-       .status = "supervisor task"
-};
+static struct supervisor_task supervisor_task;
 
 /**
  * The client program to connect to para_server.
@@ -591,21 +602,30 @@ int main(int argc, char *argv[])
        if (ret < 0)
                goto out;
        sot.btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = "stdout", .parent = ct->btrn));
-       register_task(&sched, &svt);
+               EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
+       supervisor_task.task = task_register(&(struct task_info) {
+               .name = "supervisor",
+               .post_select = supervisor_post_select,
+               .context = &supervisor_task,
+       }, &sched);
+
        ret = schedule(&sched);
-       if (ret >= 0 && ct->task.error < 0) {
-               switch(ct->task.error) {
-               /* these are not errors */
-               case -E_SERVER_CMD_SUCCESS:
-               case -E_EOF:
-               case -E_SERVER_EOF:
-               case -E_BTR_EOF:
-                       ret = 0;
-                       break;
-               default: ret = -E_SERVER_CMD_FAILURE;
+       if (ret >= 0) {
+               ret = task_status(ct->task);
+               if (ret < 0) {
+                       switch (ret) {
+                       /* these are not errors */
+                       case -E_SERVER_CMD_SUCCESS:
+                       case -E_EOF:
+                       case -E_SERVER_EOF:
+                       case -E_BTR_EOF:
+                               ret = 0;
+                               break;
+                       default: ret = -E_SERVER_CMD_FAILURE;
+                       }
                }
        }
+       sched_shutdown(&sched);
 out:
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
index 92e14b15eabc686b95a077d0097598f99b3f97a6..2f257221510b1cb0b0848eb615d1dfb894f0cc81 100644 (file)
--- a/client.h
+++ b/client.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -20,12 +20,10 @@ enum {
        CL_SENT_CH_RESPONSE,
        /** Server accepts this authentication. */
        CL_RECEIVED_PROCEED,
-       /** Client sends the command. */
-       CL_SENT_COMMAND,
-       /** Server expects data. */
+       /** Command is executing. */
+       CL_EXECUTING,
+       /** Server is expecting data (addblob commands only). */
        CL_SENDING,
-       /** Client expects data. */
-       CL_RECEIVING,
 };
 
 /** Data specific to a client task. */
@@ -34,10 +32,10 @@ struct client_task {
        int status;
        /** The file descriptor and the session keys. */
        struct stream_cipher_context scc;
-       /** True if this connections uses the sideband API. */
-       bool use_sideband;
-       /** The sideband context, ignored if \a use_sideband is false. */
-       struct sb_context *sbc;
+       /** The sideband contexts for receiving/sending. */
+       struct sb_context *sbc[2];
+       /** The buffer tree nodes for receiving/sending. */
+       struct btr_node *btrn[2];
        /** The hash value of the decrypted challenge. */
        unsigned char *challenge_hash;
        /** The configuration (including the command). */
@@ -49,14 +47,11 @@ struct client_task {
        /** Paraslash user name. */
        char *user;
        /** The client task structure. */
-       struct task task;
-       /** The buffer tree node of the client task. */
-       struct btr_node *btrn;
+       struct task *task;
        /** List of features supported by the server. */
        char **features;
 };
 
-void client_disconnect(struct client_task *ct);
 void client_close(struct client_task *ct);
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel);
index 1ecba73068f04c4dbf43480f5ccf32a932e01279..cd1ccc619ce06710d9fad42652d6868942fc17d8 100644 (file)
@@ -1,13 +1,18 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file client_common.c Common functions of para_client and para_audiod. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 /** The size of the receiving buffer. */
 #define CLIENT_BUFSIZE 4000
 
-/**
- * Close the connection to para_server and deallocate per-command resources.
- *
- * \param ct The client task.
- *
- * This frees all resources of the current command but keeps the configuration
- * in \p ct->conf.
- *
- * \sa \ref client_close().
- */
-void client_disconnect(struct client_task *ct)
-{
-       if (!ct)
-               return;
-       if (ct->scc.fd >= 0)
-               close(ct->scc.fd);
-       free_argv(ct->features);
-       ct->features = NULL;
-       sc_free(ct->scc.recv);
-       ct->scc.recv = NULL;
-       sc_free(ct->scc.send);
-       ct->scc.send = NULL;
-       btr_remove_node(&ct->btrn);
-}
-
 /**
  * Close the connection to para_server and free all resources.
  *
  * \param ct Pointer to the client data.
  *
- * \sa \ref client_open(), \ref client_disconnect().
+ * \sa \ref client_open().
  */
 void client_close(struct client_task *ct)
 {
        if (!ct)
                return;
-       client_disconnect(ct);
        free(ct->user);
        free(ct->config_file);
        free(ct->key_file);
        client_cmdline_parser_free(&ct->conf);
        free(ct->challenge_hash);
-       sb_free(ct->sbc);
+       sb_free(ct->sbc[0]);
+       sb_free(ct->sbc[1]);
        free(ct);
 }
 
-/**
+/*
  * The preselect hook for server commands.
  *
- * \param s Pointer to the scheduler.
- * \param t Pointer to the task struct for this command.
- *
  * The task pointer must contain a pointer to the initialized client data
  * structure as it is returned by client_open().
  *
  * This function checks the state of the connection and adds the file descriptor
- * of the connection to the read or write fd set of \a s accordingly.
- *
- * \sa register_task() client_open(), struct sched, struct task.
+ * of the connection to the read or write fd set of s accordingly.
  */
-static void client_pre_select(struct sched *s, struct task *t)
+static void client_pre_select(struct sched *s, void *context)
 {
        int ret;
-       struct client_task *ct = container_of(t, struct client_task, task);
-       struct btr_node *btrn = ct->btrn;
+       struct client_task *ct = context;
 
        if (ct->scc.fd < 0)
                return;
@@ -99,7 +73,6 @@ static void client_pre_select(struct sched *s, struct task *t)
        case CL_CONNECTED:
        case CL_SENT_AUTH:
        case CL_SENT_CH_RESPONSE:
-       case CL_SENT_COMMAND:
                para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
                return;
 
@@ -109,76 +82,49 @@ static void client_pre_select(struct sched *s, struct task *t)
                para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                return;
 
-       case CL_RECEIVING:
-               ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
-               if (ret != 0) {
+       case CL_SENDING:
+               if (ct->btrn[1]) {
+                       ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
                        if (ret < 0)
                                sched_min_delay(s);
-                       else
-                               para_fd_set(ct->scc.fd, &s->rfds,
-                                       &s->max_fileno);
+                       else if (ret > 0)
+                               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                }
-               return;
-       case CL_SENDING:
-               ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
-               if (ret != 0) {
+               /* fall though */
+       case CL_EXECUTING:
+               if (ct->btrn[0]) {
+                       ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
                        if (ret < 0)
                                sched_min_delay(s);
-                       else
-                               para_fd_set(ct->scc.fd, &s->wfds,
-                                       &s->max_fileno);
+                       else if (ret > 0)
+                               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
                }
                return;
        }
 }
 
-static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
-               char *buf, size_t sz, size_t *n)
-{
-       int ret;
-
-       if (ct->status < CL_SENT_CH_RESPONSE)
-               return read_nonblock(ct->scc.fd, buf, sz, rfds, n);
-
-       *n = 0;
-       ret = sc_recv_buffer(&ct->scc, buf, sz);
-       /*
-        * sc_recv_buffer is used with blocking fds elsewhere, so it
-        * does not use the nonblock-API. Therefore we need to
-        * check for EOF and EAGAIN.
-        */
-       if (ret == 0)
-               return -E_SERVER_EOF;
-       if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
-               return 0;
-       if (ret < 0)
-               return ret;
-       *n = ret;
-       return 0;
-}
-
-static int send_sb(struct client_task *ct, void *buf, size_t numbytes,
+static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbytes,
                enum sb_designator band, bool dont_free)
 {
        int ret, fd = ct->scc.fd;
        struct iovec iov[2];
 
-       if (!ct->sbc) {
+       if (!ct->sbc[channel]) {
                struct sb_buffer sbb;
                sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED?
                        NULL : sc_trafo;
                sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes);
-               ct->sbc = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
+               ct->sbc[channel] = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
        }
-       ret = sb_get_send_buffers(ct->sbc, iov);
+       ret = sb_get_send_buffers(ct->sbc[channel], iov);
        ret = xwritev(fd, iov, ret);
        if (ret < 0) {
-               sb_free(ct->sbc);
-               ct->sbc = NULL;
+               sb_free(ct->sbc[channel]);
+               ct->sbc[channel] = NULL;
                return ret;
        }
-       if (sb_sent(ct->sbc, ret)) {
-               ct->sbc = NULL;
+       if (sb_sent(ct->sbc[channel], ret)) {
+               ct->sbc[channel] = NULL;
                return 1;
        }
        return 0;
@@ -201,21 +147,24 @@ static int recv_sb(struct client_task *ct, fd_set *rfds,
                trafo = sc_trafo;
                trafo_context = ct->scc.recv;
        }
-       if (!ct->sbc)
-               ct->sbc = sb_new_recv(0, trafo, trafo_context);
+       if (!ct->sbc[0])
+               ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
 again:
-       sb_get_recv_buffer(ct->sbc, &iov);
+       sb_get_recv_buffer(ct->sbc[0], &iov);
        ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
        if (ret < 0) {
-               sb_free(ct->sbc);
-               ct->sbc = NULL;
+               sb_free(ct->sbc[0]);
+               ct->sbc[0] = NULL;
                return ret;
        }
        if (n == 0)
                return 0;
-       if (!sb_received(ct->sbc, n, result))
+       ret = sb_received(ct->sbc[0], n, result);
+       if (ret < 0)
+               return ret;
+       if (ret == 0)
                goto again;
-       ct->sbc = NULL;
+       ct->sbc[0] = NULL;
        return 1;
 }
 
@@ -251,10 +200,14 @@ static int dispatch_sbb(struct client_task *ct, struct sb_buffer *sbb)
                PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]);
 
        switch (sbb->band) {
+       case SBD_AWAITING_DATA:
+               ct->status = CL_SENDING;
+               ret = 1;
+               goto out;
        case SBD_OUTPUT:
                if (iov_valid(&sbb->iov))
                        btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len,
-                               ct->btrn);
+                               ct->btrn[0]);
                ret = 1;
                goto out;
        case SBD_DEBUG_LOG:
@@ -299,8 +252,8 @@ static int send_sb_command(struct client_task *ct)
        char *command, *p;
        size_t len = 0;
 
-       if (ct->sbc)
-               return send_sb(ct, NULL, 0, 0, false);
+       if (ct->sbc[1])
+               return send_sb(ct, 0, NULL, 0, 0, false);
 
        for (i = 0; i < ct->conf.inputs_num; i++)
                len += strlen(ct->conf.inputs[i]) + 1;
@@ -310,51 +263,47 @@ static int send_sb_command(struct client_task *ct)
                p += strlen(ct->conf.inputs[i]) + 1;
        }
        PARA_DEBUG_LOG("--> %s\n", command);
-       return send_sb(ct, command, len, SBD_COMMAND, false);
+       return send_sb(ct, 0, command, len, SBD_COMMAND, false);
 }
 
-/**
+/*
  * The post select hook for client commands.
  *
- * \param s Pointer to the scheduler.
- * \param t Pointer to the task struct for this command.
- *
  * Depending on the current state of the connection and the status of the read
- * and write fd sets of \a s, this function performs the necessary steps to
- * authenticate the connection, to send the command given by \a t->private_data
+ * and write fd sets of s, this function performs the necessary steps to
+ * authenticate the connection, to send the command given by t->private_data
  * and to receive para_server's output, if any.
- *
- * \sa struct sched, struct task.
  */
-static int client_post_select(struct sched *s, struct task *t)
+static int client_post_select(struct sched *s, void *context)
 {
-       struct client_task *ct = container_of(t, struct client_task, task);
-       struct btr_node *btrn = ct->btrn;
+       struct client_task *ct = context;
        int ret = 0;
        size_t n;
        char buf[CLIENT_BUFSIZE];
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(ct->task);
        if (ret < 0)
                goto out;
        if (ct->scc.fd < 0)
                return 0;
        switch (ct->status) {
        case CL_CONNECTED: /* receive welcome message */
-               ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
+               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
                if (ret < 0 || n == 0)
                        goto out;
                ct->features = parse_features(buf);
+               if (!has_feature("sideband", ct)) {
+                       PARA_ERROR_LOG("server has no sideband support\n");
+                       ret = -E_INCOMPAT_FEAT;
+                       goto out;
+               }
                ct->status = CL_RECEIVED_WELCOME;
                return 0;
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               if (has_feature("sideband", ct)) {
-                       ct->use_sideband = true;
-                       sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
-               } else
-                       sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
+               sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
+                       has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
@@ -369,202 +318,140 @@ static int client_post_select(struct sched *s, struct task *t)
                {
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
-               /* the SHA1 of the decrypted challenge */
+               struct sb_buffer sbb;
+               bool use_aes;
 
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       if (sbb.band != SBD_CHALLENGE) {
-                               ret = -E_BAD_BAND;
-                               free(sbb.iov.iov_base);
-                                       goto out;
-                       }
-                       n = sbb.iov.iov_len;
-                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-                       ret = priv_decrypt(ct->key_file, crypt_buf,
-                               sbb.iov.iov_base, n);
+               ret = recv_sb(ct, &s->rfds, &sbb);
+               if (ret <= 0)
+                       goto out;
+               if (sbb.band != SBD_CHALLENGE) {
+                       ret = -E_BAD_BAND;
                        free(sbb.iov.iov_base);
-                       if (ret < 0)
-                               goto out;
-               } else {
-                       ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
-                       if (ret < 0 || n == 0)
-                               goto out;
-                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-                       ret = priv_decrypt(ct->key_file, crypt_buf,
-                               (unsigned char *)buf, n);
-                       if (ret < 0)
                                goto out;
                }
+               n = sbb.iov.iov_len;
+               PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
+               ret = priv_decrypt(ct->key_file, crypt_buf,
+                       sbb.iov.iov_base, n);
+               free(sbb.iov.iov_base);
+               if (ret < 0)
+                       goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
-               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
+               use_aes = has_feature("aes_ctr128", ct);
+               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN);
+                       SESSION_KEY_LEN, use_aes);
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
                }
        case CL_RECEIVED_CHALLENGE:
-               if (ct->use_sideband) {
-                       ret = send_sb(ct, ct->challenge_hash, HASH_SIZE,
-                               SBD_CHALLENGE_RESPONSE, false);
-                       if (ret != 0)
-                               ct->challenge_hash = NULL;
-                       if (ret <= 0)
-                               goto out;
-               } else {
-                       ret = write_all(ct->scc.fd, (char *)ct->challenge_hash, HASH_SIZE);
-                       if (ret < 0)
-                               goto out;
-               }
+               ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
+                       SBD_CHALLENGE_RESPONSE, false);
+               if (ret != 0)
+                       ct->challenge_hash = NULL;
+               if (ret <= 0)
+                       goto out;
                ct->status = CL_SENT_CH_RESPONSE;
                goto out;
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       free(sbb.iov.iov_base);
-                       if (sbb.band != SBD_PROCEED)
-                               ret = -E_BAD_BAND;
-                       else
-                               ct->status = CL_RECEIVED_PROCEED;
-                       goto out;
-               }
-               ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
-               if (ret < 0 || n == 0)
-                       goto out;
-               /* check if server has sent "Proceed" message */
-               ret = -E_CLIENT_AUTH;
-               if (n < PROCEED_MSG_LEN)
-                       goto out;
-               if (!strstr(buf, PROCEED_MSG))
+               struct sb_buffer sbb;
+               ret = recv_sb(ct, &s->rfds, &sbb);
+               if (ret <= 0)
                        goto out;
-               ct->status = CL_RECEIVED_PROCEED;
-               return 0;
+               free(sbb.iov.iov_base);
+               if (sbb.band != SBD_PROCEED)
+                       ret = -E_BAD_BAND;
+               else
+                       ct->status = CL_RECEIVED_PROCEED;
+               goto out;
                }
        case CL_RECEIVED_PROCEED: /* concat args and send command */
                {
-               int i;
-               char *command = NULL;
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               if (ct->use_sideband) {
-                       ret = send_sb_command(ct);
-                       if (ret <= 0)
-                               goto out;
-                       ct->status = CL_SENT_COMMAND;
-                       return 0;
-               }
-               for (i = 0; i < ct->conf.inputs_num; i++) {
-                       char *tmp = command;
-                       command = make_message("%s\n%s", command?
-                               command : "", ct->conf.inputs[i]);
-                       free(tmp);
-               }
-               command = para_strcat(command, EOC_MSG "\n");
-               PARA_DEBUG_LOG("--> %s\n", command);
-               ret = sc_send_buffer(&ct->scc, command);
-               free(command);
-               if (ret < 0)
+               ret = send_sb_command(ct);
+               if (ret <= 0)
                        goto out;
-               ct->status = CL_SENT_COMMAND;
+               ct->status = CL_EXECUTING;
                return 0;
                }
-       case CL_SENT_COMMAND:
-               {
-               char *buf2;
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       if (sbb.band == SBD_AWAITING_DATA) {
-                               ct->status = CL_SENDING;
-                               free(sbb.iov.iov_base);
-                               goto out;
+       case CL_SENDING:
+               if (ct->btrn[1]) {
+                       char *buf2;
+                       size_t sz;
+                       ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
+                       if (ret == -E_BTR_EOF) {
+                               /* empty blob data packet indicates EOF */
+                               PARA_INFO_LOG("blob sent\n");
+                               ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true);
+                               if (ret >= 0)
+                                       ret = -E_BTR_EOF;
                        }
-                       ct->status = CL_RECEIVING;
-                       ret = dispatch_sbb(ct, &sbb);
-                       goto out;
-               }
-               /* can not use "buf" here because we need a malloced buffer */
-               buf2 = para_malloc(CLIENT_BUFSIZE);
-               ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
-               if (n > 0) {
-                       if (strstr(buf2, AWAITING_DATA_MSG)) {
-                               free(buf2);
-                               ct->status = CL_SENDING;
-                               return 0;
+                       if (ret < 0)
+                               goto close1;
+                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+                               sz = btr_next_buffer(ct->btrn[1], &buf2);
+                               assert(sz);
+                               ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
+                               if (ret < 0)
+                                       goto close1;
+                               if (ret > 0)
+                                       btr_consume(ct->btrn[1], sz);
                        }
-                       ct->status = CL_RECEIVING;
-                       btr_add_output(buf2, n, btrn);
-               } else
-                       free(buf2);
-               goto out;
                }
-       case CL_SENDING:
-               {
-               char *buf2;
-               size_t sz;
-               ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
-               if (ret < 0)
-                       goto out;
-               if (ret == 0)
-                       return 0;
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
-                       return 0;
-               sz = btr_next_buffer(btrn, &buf2);
-               ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
-               if (ret < 0)
-                       goto out;
-               btr_consume(btrn, sz);
-               return 0;
-               }
-       case CL_RECEIVING:
-               {
-               char *buf2;
-               ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
-               if (ret < 0)
-                       goto out;
-               if (ret == 0)
-                       return 0;
-               /*
-                * The FD_ISSET() is not strictly necessary, but is allows us
-                * to skip the malloc below if there is nothing to read anyway.
-                */
-               if (!FD_ISSET(ct->scc.fd, &s->rfds))
-                       return 0;
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret > 0)
-                               ret = dispatch_sbb(ct, &sbb);
-                       goto out;
+               /* fall through */
+       case CL_EXECUTING:
+               if (ct->btrn[0]) {
+                       ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
+                       if (ret < 0)
+                               goto close0;
+                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) {
+                               struct sb_buffer sbb;
+                               ret = recv_sb(ct, &s->rfds, &sbb);
+                               if (ret < 0)
+                                       goto close0;
+                               if (ret > 0) {
+                                       ret = dispatch_sbb(ct, &sbb);
+                                       if (ret < 0)
+                                               goto close0;
+                               }
+                       }
                }
-               buf2 = para_malloc(CLIENT_BUFSIZE);
-               ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
-               if (n > 0) {
-                       buf2 = para_realloc(buf2, n);
-                       btr_add_output(buf2, n, btrn);
-               } else
-                       free(buf2);
+               ret = 0;
                goto out;
-               }
        }
+close1:
+       PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret));
+       btr_remove_node(&ct->btrn[1]);
+       if (ct->btrn[0])
+               return 0;
+       goto out;
+close0:
+       PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret));
+       btr_remove_node(&ct->btrn[0]);
+       if (ct->btrn[1] && ct->status == CL_SENDING)
+               return 0;
 out:
-       if (ret < 0) {
-               if (!ct->use_sideband && ret != -E_SERVER_EOF &&
-                               ret != -E_BTR_EOF && ret != -E_EOF)
-                       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               btr_remove_node(&ct->btrn);
+       if (ret >= 0)
+               return 0;
+       btr_remove_node(&ct->btrn[0]);
+       btr_remove_node(&ct->btrn[1]);
+       if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       if (ct->scc.fd >= 0) {
+               close(ct->scc.fd);
+               ct->scc.fd = -1;
        }
+       free_argv(ct->features);
+       ct->features = NULL;
+       sc_free(ct->scc.recv);
+       ct->scc.recv = NULL;
+       sc_free(ct->scc.send);
+       ct->scc.send = NULL;
        return ret;
 }
 
@@ -598,13 +485,17 @@ int client_connect(struct client_task *ct, struct sched *s,
        if (ret < 0)
                goto err_out;
        ct->status = CL_CONNECTED;
-       ct->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = "client", .parent = parent, .child = child));
-       ct->task.pre_select = client_pre_select;
-       ct->task.post_select = client_post_select;
-       ct->task.error = 0;
-       sprintf(ct->task.status, "client");
-       register_task(s, &ct->task);
+       ct->btrn[0] = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "client recv", .parent = NULL, .child = child));
+       ct->btrn[1] = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "client send", .parent = parent, .child = NULL));
+
+       ct->task = task_register(&(struct task_info) {
+               .name = "client",
+               .pre_select = client_pre_select,
+               .post_select = client_post_select,
+               .context = ct,
+       }, s);
        return 1;
 err_out:
        close(ct->scc.fd);
index 37f727e278c6b61cedede8e6a84e77db3ba4c74b..c606d98c46aeb4c9a15e76b90ea5856231c6a38e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -11,6 +11,7 @@
 #include "para.h"
 #include "list.h"
 #include "string.h"
+#include "close_on_fork.h"
 
 static struct list_head close_on_fork_list;
 static int initialized;
index f1f00fca8a29add0e35a2e415a927bdeb75c4376..93de2d2d99d9f75cb041211e8c4f45dfef28c6a1 100644 (file)
--- a/command.c
+++ b/command.c
@@ -1,15 +1,20 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file command.c Client authentication and server commands. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 #include "fd.h"
 #include "ipc.h"
 #include "user_list.h"
-#include "server_command_list.h"
-#include "afs_command_list.h"
+#include "server.command_list.h"
+#include "afs.command_list.h"
 #include "signal.h"
 #include "version.h"
 
-struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
-struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
+typedef int server_command_handler_t(struct command_context *);
+static server_command_handler_t SERVER_COMMAND_HANDLERS;
+server_command_handler_t AFS_COMMAND_HANDLERS;
+
+/* Defines one command of para_server. */
+struct server_command {
+       /* The name of the command. */
+       const char *name;
+       /* Pointer to the function that handles the command. */
+       server_command_handler_t *handler;
+       /* The privileges a user must have to execute this command. */
+       unsigned int perms;
+       /* One-line description of the command. */
+       const char *description;
+       /* Summary of the command line options. */
+       const char *usage;
+       /* The long help text. */
+       const char *help;
+};
+
+static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
+static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
 
 /** Commands including options must be shorter than this. */
 #define MAX_COMMAND_LEN 32768
@@ -50,20 +75,14 @@ static void dummy(__a_unused int s)
 {
 }
 
-static void mmd_dup(struct misc_meta_data *new_mmd)
-{
-       mutex_lock(mmd_mutex);
-       *new_mmd = *mmd;
-       mutex_unlock(mmd_mutex);
-}
-
 /*
- * Compute human readable string containing vss status for given integer value.
+ * Compute human readable vss status text.
  *
- * We don't want to use vss_playing() and friends here because we take a
- * snapshot of the mmd struct and use the copy for computing the state of the
- * vss. If the real data were used, we would take the mmd lock for a rather
- * long time or risk to get an inconsistent view.
+ * We can't call vss_playing() and friends here because those functions read
+ * the flags from the primary mmd structure, so calling them from command
+ * handler context would require to take the mmd lock. At the time the function
+ * is called we already took a copy of the mmd structure and want to use the
+ * flags value of the copy for computing the vss status text.
  */
 static char *vss_status_tohuman(unsigned int flags)
 {
@@ -107,22 +126,15 @@ static char *vss_get_status_flags(unsigned int flags)
 static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
                char **result)
 {
-       char mtime[30] = "";
        char *status, *flags; /* vss status info */
        /* nobody updates our version of "now" */
-       char *ut = get_server_uptime_str(NULL);
        long offset = (nmmd->offset + 500) / 1000;
        struct timeval current_time;
-       struct tm mtime_tm;
        struct para_buffer b = {.flags = parser_friendly? PBF_SIZE_PREFIX : 0};
 
        /* report real status */
        status = vss_status_tohuman(nmmd->vss_status_flags);
        flags = vss_get_status_flags(nmmd->vss_status_flags);
-       if (nmmd->size) { /* parent currently has an audio file open */
-               localtime_r(&nmmd->mtime, &mtime_tm);
-               strftime(mtime, 29, "%b %d %Y", &mtime_tm);
-       }
        clock_get_realtime(&current_time);
        /*
         * The calls to WRITE_STATUS_ITEM() below never fail because
@@ -130,8 +142,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
         * is not smart enough to prove this and complains nevertheless.
         * Casting the return value to void silences clang.
         */
-       (void)WRITE_STATUS_ITEM(&b, SI_FILE_SIZE, "%zu\n", nmmd->size / 1024);
-       (void)WRITE_STATUS_ITEM(&b, SI_MTIME, "%s\n", mtime);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS_FLAGS, "%s\n", flags);
        (void)WRITE_STATUS_ITEM(&b, SI_OFFSET, "%li\n", offset);
@@ -144,7 +154,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
                (long unsigned)current_time.tv_usec);
        free(flags);
        free(status);
-       free(ut);
        *result = b.buf;
        return b.offset;
 }
@@ -152,9 +161,8 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
 static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
 {
        int i;
-       /* this has to match sender.h */
-       const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
 
+       const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
        scd->sender_num = -1;
        if (argc < 3)
                return -E_COMMAND_SYNTAX;
@@ -174,19 +182,19 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
        if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
                return -E_SENDER_CMD;
        switch (scd->cmd_num) {
-       case SENDER_ON:
-       case SENDER_OFF:
+       case SENDER_on:
+       case SENDER_off:
                if (argc != 3)
                        return -E_COMMAND_SYNTAX;
                break;
-       case SENDER_DENY:
-       case SENDER_ALLOW:
+       case SENDER_deny:
+       case SENDER_allow:
                if (argc != 4 || parse_cidr(argv[3], scd->host,
                                sizeof(scd->host), &scd->netmask) == NULL)
                        return -E_COMMAND_SYNTAX;
                break;
-       case SENDER_ADD:
-       case SENDER_DELETE:
+       case SENDER_add:
+       case SENDER_delete:
                if (argc != 4)
                        return -E_COMMAND_SYNTAX;
                return parse_fec_url(argv[3], scd);
@@ -207,7 +215,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
  *
  * The nonblock flag must be disabled for the file descriptor given by \a scc.
  *
- * Stream cipher encryption is automatically activated if neccessary via the
+ * Stream cipher encryption is automatically activated if necessary via the
  * sideband transformation, depending on the value of \a band.
  *
  * \return Standard.
@@ -220,8 +228,8 @@ int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
        int ret;
        struct sb_context *sbc;
        struct iovec iov[2];
-       struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
        sb_transformation trafo = band < SBD_PROCEED? NULL : sc_trafo;
+       struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
 
        sbc = sb_new_send(&sbb, dont_free, trafo, scc->send);
        do {
@@ -268,10 +276,7 @@ __printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
  */
 int send_strerror(struct command_context *cc, int err)
 {
-       return cc->use_sideband?
-               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err))
-       :
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err));
+       return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
 }
 
 /**
@@ -338,28 +343,22 @@ static int com_sender(struct command_context *cc)
                        free(msg);
                        msg = tmp;
                }
-               if (cc->use_sideband)
-                       return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
-               ret = sc_send_buffer(&cc->scc, msg);
-               free(msg);
-               return ret;
+               return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
        }
        ret = check_sender_args(cc->argc, cc->argv, &scd);
        if (ret < 0) {
                if (scd.sender_num < 0)
                        return ret;
-               msg = senders[scd.sender_num].help();
-               if (cc->use_sideband)
-                       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT,
-                               false);
-               ret = sc_send_buffer(&cc->scc, msg);
-               free(msg);
-               return ret;
+               if (strcmp(cc->argv[2], "status") == 0)
+                       msg = senders[scd.sender_num].status();
+               else
+                       msg = senders[scd.sender_num].help();
+               return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
        }
 
        switch (scd.cmd_num) {
-       case SENDER_ADD:
-       case SENDER_DELETE:
+       case SENDER_add:
+       case SENDER_delete:
                assert(senders[scd.sender_num].resolve_target);
                ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
                if (ret < 0)
@@ -369,11 +368,13 @@ static int com_sender(struct command_context *cc)
        for (i = 0; i < 10; i++) {
                mutex_lock(mmd_mutex);
                if (mmd->sender_cmd_data.cmd_num >= 0) {
+                       /* another sender command is active, retry in 100ms */
+                       struct timespec ts = {.tv_nsec = 100 * 1000 * 1000};
                        mutex_unlock(mmd_mutex);
-                       usleep(100 * 1000);
+                       nanosleep(&ts, NULL);
                        continue;
                }
-               memcpy(&mmd->sender_cmd_data, &scd, sizeof(scd));
+               mmd->sender_cmd_data = scd;
                mutex_unlock(mmd_mutex);
                break;
        }
@@ -383,28 +384,20 @@ static int com_sender(struct command_context *cc)
 /* server info */
 static int com_si(struct command_context *cc)
 {
-       int i, ret;
-       char *msg, *ut, *sender_info = NULL;
+       int ret;
+       char *msg, *ut;
 
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
-       for (i = 0; senders[i].name; i++) {
-               char *info = senders[i].info();
-               sender_info = para_strcat(sender_info, info);
-               free(info);
-       }
-       ut = get_server_uptime_str(now);
+       ut = daemon_get_uptime_str(now);
        ret = xasprintf(&msg,
-               "version: %s\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
                "current loglevel: %s\n"
-               "supported audio formats: %s\n"
-               "%s",
-               version_git(),
+               "supported audio formats: %s\n",
                ut, mmd->num_played,
                (int)getppid(),
                (int)mmd->afs_pid,
@@ -412,17 +405,11 @@ static int com_si(struct command_context *cc)
                mmd->num_commands,
                mmd->num_connects,
                conf.loglevel_arg,
-               AUDIO_FORMAT_HANDLERS,
-               sender_info
+               AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
        free(ut);
-       free(sender_info);
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
-       ret = sc_send_bin_buffer(&cc->scc, msg, ret);
-       free(msg);
-       return ret;
+       return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
 }
 
 /* version */
@@ -431,12 +418,11 @@ static int com_version(struct command_context *cc)
        char *msg;
        size_t len;
 
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
-       len = xasprintf(&msg, "%s", version_text("server"));
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
-       return sc_send_bin_buffer(&cc->scc, msg, len);
+       if (cc->argc > 1 && strcmp(cc->argv[1], "-v") == 0)
+               len = xasprintf(&msg, "%s", version_text("server"));
+       else
+               len = xasprintf(&msg, "%s\n", version_single_line("server"));
+       return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
 }
 
 /** These status items are cleared if no audio file is currently open. */
@@ -539,26 +525,21 @@ static int com_stat(struct command_context *cc)
        if (i != cc->argc)
                return -E_COMMAND_SYNTAX;
        for (;;) {
-               mmd_dup(nmmd);
+               /*
+                * Copy the mmd structure to minimize the time we hold the mmd
+                * lock.
+                */
+               mutex_lock(mmd_mutex);
+               *nmmd = *mmd;
+               mutex_unlock(mmd_mutex);
                ret = get_status(nmmd, parser_friendly, &s);
-               if (cc->use_sideband)
-                       ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
-               else {
-                       ret = sc_send_bin_buffer(&cc->scc, s, ret);
-                       free(s);
-               }
+               ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
                if (ret < 0)
                        goto out;
                if (nmmd->vss_status_flags & VSS_NEXT) {
                        char *esi;
                        ret = empty_status_items(parser_friendly, &esi);
-                       if (cc->use_sideband)
-                               ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT,
-                                       false);
-                       else {
-                               ret = sc_send_bin_buffer(&cc->scc, esi, ret);
-                               free(esi);
-                       }
+                       ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false);
                        if (ret < 0)
                                goto out;
                } else
@@ -578,7 +559,6 @@ out:
 static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
                const char *handler)
 {
-       int ret;
        char *msg = NULL;
 
        for (; cmd->name; cmd++) {
@@ -589,11 +569,8 @@ static int send_list_of_commands(struct command_context *cc, struct server_comma
                msg = para_strcat(msg, tmp);
                free(tmp);
        }
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
-       ret = sc_send_buffer(&cc->scc, msg);
-       free(msg);
-       return ret;
+       assert(msg);
+       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
 }
 
 /* returns string that must be freed by the caller */
@@ -649,11 +626,7 @@ static int com_help(struct command_context *cc)
        );
        free(perms);
        free(handler);
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
-       ret = sc_send_buffer(&cc->scc, buf);
-       free(buf);
-       return ret;
+       return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
 }
 
 /* hup */
@@ -762,7 +735,7 @@ static int com_ff(struct command_context *cc)
                promille += 1000 * i / mmd->afd.afhi.seconds_total;
        if (promille < 0)
                promille = 0;
-       if (promille >  1000) {
+       if (promille > 1000) {
                mmd->new_vss_status_flags |= VSS_NEXT;
                goto out;
        }
@@ -793,8 +766,8 @@ static int com_jmp(struct command_context *cc)
        if (i > 100)
                i = 100;
        PARA_INFO_LOG("jumping to %lu%%\n", i);
-       mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50)/ 100;
-       PARA_INFO_LOG("sent: %lu,  offset before jmp: %lu\n",
+       mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100;
+       PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
                mmd->chunks_sent, mmd->offset);
        mmd->new_vss_status_flags |= VSS_REPOS;
        mmd->new_vss_status_flags &= ~VSS_NEXT;
@@ -805,68 +778,26 @@ out:
        return ret;
 }
 
+static int com_tasks(struct command_context *cc)
+{
+       char *tl = server_get_tasks();
+       int ret = 1;
+
+       if (tl)
+               ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
+       return ret;
+}
+
 /*
  * check if perms are sufficient to exec a command having perms cmd_perms.
  * Returns 0 if perms are sufficient, -E_PERM otherwise.
  */
-static int check_perms(unsigned int perms, struct server_command *cmd_ptr)
+static int check_perms(unsigned int perms, const struct server_command *cmd_ptr)
 {
        PARA_DEBUG_LOG("checking permissions\n");
        return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
 }
 
-/*
- * Parse first string from *cmd and lookup in table of valid commands.
- * On error, NULL is returned.
- */
-static struct server_command *parse_cmd(const char *cmdstr)
-{
-       char buf[255];
-       int n = 0;
-
-       sscanf(cmdstr, "%200s%n", buf, &n);
-       if (!n)
-               return NULL;
-       buf[n] = '\0';
-       return get_cmd_ptr(buf, NULL);
-}
-
-static int read_command(struct stream_cipher_context *scc, char **result)
-{
-       int ret;
-       char buf[4096];
-       char *command = NULL;
-
-       for (;;) {
-               size_t numbytes;
-               char *p;
-
-               ret = sc_recv_buffer(scc, buf, sizeof(buf));
-               if (ret < 0)
-                       goto out;
-               if (!ret)
-                       break;
-               numbytes = ret;
-               ret = -E_COMMAND_SYNTAX;
-               if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */
-                       goto out;
-               command = para_strcat(command, buf);
-               p = strstr(command, EOC_MSG);
-               if (p) {
-                       *p = '\0';
-                       break;
-               }
-       }
-       ret = command? 1 : -E_COMMAND_SYNTAX;
-out:
-       if (ret < 0)
-               free(command);
-       else
-               *result = command;
-       return ret;
-
-}
-
 static void reset_signals(void)
 {
        para_sigaction(SIGCHLD, SIG_IGN);
@@ -875,15 +806,20 @@ static void reset_signals(void)
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
+struct connection_features {
+       bool sideband_requested;
+       bool aes_ctr128_requested;
+};
+
 static int parse_auth_request(char *buf, int len, struct user **u,
-               bool *use_sideband)
+               struct connection_features *cf)
 {
        int ret;
        char *p, *username, **features = NULL;
        size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
 
        *u = NULL;
-       *use_sideband = false;
+       memset(cf, 0, sizeof(*cf));
        if (len < auth_rq_len + 2)
                return -E_AUTH_REQUEST;
        if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
@@ -899,15 +835,16 @@ static int parse_auth_request(char *buf, int len, struct user **u,
                create_argv(p, ",", &features);
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
-                               *use_sideband = true;
+                               cf->sideband_requested = true;
+                       else if (strcmp(features[i], "aes_ctr128") == 0)
+                               cf->aes_ctr128_requested = true;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
                        }
                }
        }
-       PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n",
-               username, *use_sideband? "true" : "false");
+       PARA_DEBUG_LOG("received auth request for user %s\n", username);
        *u = lookup_user(username);
        ret = 1;
 out:
@@ -917,24 +854,25 @@ out:
 
 #define HANDSHAKE_BUFSIZE 4096
 
-static int parse_sb_command(struct command_context *cc, struct iovec *iov)
+static int run_command(struct command_context *cc, struct iovec *iov,
+               const char *peername)
 {
        int ret, i;
        char *p, *end;
+       struct server_command *cmd;
 
-       ret = -E_BAD_CMD;
        if (iov->iov_base == NULL || iov->iov_len == 0)
-               goto out;
+               return -E_BAD_CMD;
        p = iov->iov_base;
        p[iov->iov_len - 1] = '\0'; /* just to be sure */
-       cc->cmd = get_cmd_ptr(p, NULL);
-       if (!cc->cmd)
-               goto out;
-       ret = check_perms(cc->u->perms, cc->cmd);
+       cmd = get_cmd_ptr(p, NULL);
+       if (!cmd)
+               return -E_BAD_CMD;
+       ret = check_perms(cc->u->perms, cmd);
        if (ret < 0)
-               goto out;
+               return ret;
        end = iov->iov_base + iov->iov_len;
-       for (i = 0, p = iov->iov_base; p < end; i++)
+       for (i = 0; p < end; i++)
                p += strlen(p) + 1;
        cc->argc = i;
        cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
@@ -943,9 +881,15 @@ static int parse_sb_command(struct command_context *cc, struct iovec *iov)
                p += strlen(p) + 1;
        }
        cc->argv[cc->argc] = NULL;
-       ret = cc->argc;
-out:
-       free(iov->iov_base);
+       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
+               cc->u->name, peername);
+       ret = cmd->handler(cc);
+       free_argv(cc->argv);
+       mutex_lock(mmd_mutex);
+       mmd->num_commands++;
+       if (ret >= 0 && (cmd->perms & AFS_WRITE))
+               mmd->events++;
+       mutex_unlock(mmd_mutex);
        return ret;
 }
 
@@ -955,24 +899,22 @@ out:
  * \param fd The file descriptor to send output to.
  * \param peername Identifies the connecting peer.
  *
- * Whenever para_server accepts an incoming tcp connection on
- * the port it listens on, it forks and the resulting child
- * calls this function.
+ * Whenever para_server accepts an incoming tcp connection on the port it
+ * listens on, it forks and the resulting child calls this function.
  *
- * An RSA-based challenge/response is used to authenticate
- * the peer. It that authentication succeeds, a random
- * session key is generated and sent back to the peer,
- * encrypted with its RSA public key.  From this point on,
- * all transfers are crypted with this session key.
+ * An RSA-based challenge/response is used to authenticate the peer. It that
+ * authentication succeeds, a random session key is generated and sent back to
+ * the peer, encrypted with its RSA public key. From this point on, all
+ * transfers are crypted with this session key.
  *
- * Next it is checked if the peer supplied  a valid server command or a command
- * for the audio file selector.  If yes, and if the user has sufficient
+ * Next it is checked if the peer supplied a valid server command or a command
+ * for the audio file selector. If yes, and if the user has sufficient
  * permissions to execute that command, the function calls the corresponding
  * command handler which does argument checking and further processing.
  *
- * In order to cope with a DOS attacks, a timeout is set up
- * which terminates the function if the connection was not
- * authenticated when the timeout expires.
+ * In order to cope with a DOS attacks, a timeout is set up which terminates
+ * the function if the connection was not authenticated when the timeout
+ * expires.
  *
  * \sa alarm(2), crypt.c, crypt.h
  */
@@ -981,9 +923,11 @@ __noreturn void handle_connect(int fd, const char *peername)
        int ret;
        unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
-       char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
+       char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
+       struct iovec iov;
+       struct connection_features cf;
 
        cc->scc.fd = fd;
        reset_signals();
@@ -993,8 +937,8 @@ __noreturn void handle_connect(int fd, const char *peername)
                goto net_err;
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
-               PACKAGE_VERSION  ".\n"
-               "Features: sideband\n"
+               PACKAGE_VERSION ".\n"
+               "Features: sideband,aes_ctr128\n"
        );
        if (ret < 0)
                goto net_err;
@@ -1002,12 +946,14 @@ __noreturn void handle_connect(int fd, const char *peername)
        ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
-       ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
+       ret = parse_auth_request(buf, ret, &cc->u, &cf);
        if (ret < 0)
                goto net_err;
-       p = buf + strlen(AUTH_REQUEST_MSG);
-       PARA_DEBUG_LOG("received auth request for user %s\n", p);
-       cc->u = lookup_user(p);
+       if (!cf.sideband_requested) { /* sideband is mandatory */
+               PARA_ERROR_LOG("client did not request sideband\n");
+               ret = -E_BAD_FEATURE;
+               goto net_err;
+       }
        if (cc->u) {
                get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
                ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
@@ -1024,30 +970,18 @@ __noreturn void handle_connect(int fd, const char *peername)
                numbytes = 256;
                get_random_bytes_or_die((unsigned char *)buf, numbytes);
        }
-       PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
+       PARA_DEBUG_LOG("sending %u byte challenge + session key (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
-       if (cc->use_sideband) {
-               struct iovec iov;
-               ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
-               buf = NULL;
-               if (ret < 0)
-                       goto net_err;
-               ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
-                       HANDSHAKE_BUFSIZE, &iov);
-               if (ret < 0)
-                       goto net_err;
-               buf = iov.iov_base;
-               numbytes = iov.iov_len;
-       } else {
-               ret = write_all(fd, buf, numbytes);
-               if (ret < 0)
-                       goto net_err;
-               /* recv challenge response */
-               ret = recv_bin_buffer(fd, buf, HASH_SIZE);
-               if (ret < 0)
-                       goto net_err;
-               numbytes = ret;
-       }
+       ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
+       buf = NULL;
+       if (ret < 0)
+               goto net_err;
+       ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
+               HANDSHAKE_BUFSIZE, &iov);
+       if (ret < 0)
+               goto net_err;
+       buf = iov.iov_base;
+       numbytes = iov.iov_len;
        PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
@@ -1066,54 +1000,24 @@ __noreturn void handle_connect(int fd, const char *peername)
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
        /* init stream cipher keys with the second part of the random buffer */
-       cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
-       cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN);
-       if (cc->use_sideband)
-               ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
-       else
-               ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
+       cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN,
+               cf.aes_ctr128_requested);
+       cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+               SESSION_KEY_LEN, cf.aes_ctr128_requested);
+       ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
-       if (cc->use_sideband) {
-               struct iovec iov;
-               ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
-               if (ret < 0)
-                       goto net_err;
-               ret = parse_sb_command(cc, &iov);
-               if (ret < 0)
-                       goto err_out;
-               cc->argc = ret;
-       } else {
-               ret = read_command(&cc->scc, &command);
-               if (ret == -E_COMMAND_SYNTAX)
-                       goto err_out;
-               if (ret < 0)
-                       goto net_err;
-               ret = -E_BAD_CMD;
-               cc->cmd = parse_cmd(command);
-               if (!cc->cmd)
-                       goto err_out;
-               /* valid command, check permissions */
-               ret = check_perms(cc->u->perms, cc->cmd);
-               if (ret < 0)
-                       goto err_out;
-               /* valid command and sufficient perms */
-               ret = create_argv(command, "\n", &cc->argv);
-               if (ret < 0)
-                       goto err_out;
-               cc->argc = ret;
-       }
-       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name,
-               cc->u->name, peername);
-       ret = cc->cmd->handler(cc);
-       free_argv(cc->argv);
-       mutex_lock(mmd_mutex);
-       mmd->num_commands++;
-       mutex_unlock(mmd_mutex);
+       ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
+       if (ret < 0)
+               goto net_err;
+       ret = run_command(cc, &iov, peername);
+       free(iov.iov_base);
+       if (ret < 0)
+               goto err_out;
        if (ret >= 0)
                goto out;
 err_out:
-       if (send_strerror(cc, -ret) >= 0 && cc->use_sideband)
+       if (send_strerror(cc, -ret) >= 0)
                send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true);
 net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
@@ -1121,11 +1025,9 @@ out:
        free(buf);
        free(command);
        mutex_lock(mmd_mutex);
-       if (cc->cmd && (cc->cmd->perms & AFS_WRITE) && ret >= 0)
-               mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
-       if (ret >= 0 && cc->use_sideband) {
+       if (ret >= 0) {
                ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
                if (ret < 0)
                        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
index e4159e6bda00131bf4a1ef6b5a46627c9f7d88b1..5e1a4ce3edc1a8812948ada45eecd2f76df4f7be 100644 (file)
--- a/command.h
+++ b/command.h
@@ -10,30 +10,8 @@ struct command_context {
        int argc;
        /** Argument vector. */
        char **argv;
-       /** The command being executed. */
-       struct server_command *cmd;
        /** File descriptor and crypto keys. */
        struct stream_cipher_context scc;
-       /** Whether to use the sideband API for this command. */
-       bool use_sideband;
-};
-
-/**
- * Defines one command of para_server.
- */
-struct server_command {
-       /** The name of the command. */
-       const char *name;
-       /** Pointer to the function that handles the command. */
-       int (*handler)(struct command_context *);
-       /** The privileges a user must have to execute this command. */
-       unsigned int perms;
-       /** One-line description of the command. */
-       const char *description;
-       /** Summary of the command line options. */
-       const char *usage;
-       /** The long help text. */
-       const char *help;
 };
 
 int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
diff --git a/command_util.bash b/command_util.bash
new file mode 100755 (executable)
index 0000000..5e9dbcc
--- /dev/null
@@ -0,0 +1,291 @@
+#!/usr/bin/env bash
+
+read_header()
+{
+       local key value i
+
+       while read key value; do
+               case "$key" in
+               ---)
+                       break
+                       ;;
+               BN:)
+                       base_name="$value"
+                       ;;
+               SF:)
+                       source_files="$value"
+                       ;;
+               SN:)
+                       section_name="$value"
+                       ;;
+               TM:)
+                       template_members="$value"
+               esac
+       done
+}
+
+read_one_command()
+{
+       local line
+
+       name_txt=""
+       desc_txt=""
+       usage_txt=""
+       help_txt=""
+       perms_txt=""
+       template=0
+       template_name=""
+       template_prototype=""
+       while read key value; do
+               case "$key" in
+               ---)
+                       break
+                       ;;
+               N:)
+                       name_txt="$value"
+                       ;;
+               T:)
+                       template_name="$value"
+                       template=1
+                       ;;
+               O:)
+                       template_prototype="$value"
+                       template=1
+                       ;;
+               P:)
+                       perms_txt="$value"
+                       ;;
+               D:)
+                       desc_txt="$value"
+                       ;;
+               U:)
+                       usage_txt="$value"
+                       ;;
+               H:)
+                       help_txt="${value}"
+                       while read line; do
+                               if test "$line" = "---"; then
+                                       break;
+                               fi
+                               line=${line#H:}
+                               help_txt="$help_txt
+${line# }"
+                       done
+                       break
+                       ;;
+               esac
+       done
+       if test $template -eq 0; then
+               if test -n "$name_txt" -a -n "$desc_txt" -a -n "$usage_txt" \
+                               -a -n "$help_txt"; then
+                       ret=1
+                       return
+               fi
+       else
+               if test -n "$template_name" -a -n "$template_prototype" \
+                               -a -n "$name_txt " -a -n "$template_members" \
+                               -a -n "$desc_txt" -a -n "$usage_txt" \
+                               -a -n "$help_txt"; then
+                       ret=1
+                       return
+               fi
+       fi
+       if test -z "$name_txt" -a -z "$desc_txt" -a -z "$usage_txt" \
+                       -a -z "$help_txt"; then
+               ret=0
+               return
+       fi
+       ret=-1
+       #return
+       echo "!ERROR!"
+       echo "N: $name_txt"
+       echo "D: $desc_txt"
+       echo "S: $usage_txt"
+       echo "P: $perms_txt"
+       echo "H: $help_txt"
+       echo "O: $template_prototype"
+}
+
+dump_man()
+{
+       if test $template -eq 0; then
+               echo ".SS \"$name_txt\""
+               echo "$desc_txt"
+               echo
+               echo "\\fBUsage: \\fP$usage_txt"
+       else
+               for member in $template_members; do
+                       local sed_cmd="sed -e s/@member@/$member/g"
+                       local t_name_txt=$(echo $name_txt | $sed_cmd)
+                       echo ".SS \"$t_name_txt\""
+               done
+               echo "$desc_txt" | sed -e "s/@member@/{$(echo $template_members | sed -e 's/ / | /g')}/g"
+               echo
+               echo "\\fBUsage: \\fP"
+               echo
+               echo ".nf"
+               for member in $template_members; do
+                       local sed_cmd="sed -e s/@member@/$member/g"
+                       local t_usage_txt=$(echo $usage_txt | $sed_cmd)
+                       printf "\t$t_usage_txt\n"
+               done
+               echo ".fi"
+       fi
+       echo
+       echo "$help_txt" | sed -e 's/^  //'
+       echo
+       if test -n "$perms_txt"; then
+               echo -n "\\fBpermissions:\\fP "
+               if test "$perms_txt" = "0"; then
+                       echo "(none)"
+               else
+                       echo "$perms_txt"
+               fi
+       fi
+       echo
+}
+
+
+com_man()
+{
+       echo "[$section_name]"
+       echo
+       while : ; do
+               read_one_command
+               if test $ret -lt 0; then
+                       exit 1
+               fi
+               if test $ret -eq 0; then
+                       break
+               fi
+               dump_man
+       done
+}
+
+cmd_handler_name()
+{
+       result="com_$name_txt,"
+}
+
+make_array_member()
+{
+       local TAB='     ' CR='
+'
+       local tmp
+
+       result="{.name = \"$name_txt\", .handler = com_$name_txt, "
+       if test -n "$perms_txt"; then
+               result="$result .perms = $perms_txt,"
+       fi
+       result="$result.description = \"$desc_txt\", .usage = \"$usage_txt\", \\$CR .help = "
+       tmp="$(printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
+               -e "s/$TAB/\\\t/g" -e's/$/\\/g')"
+       result="$result$tmp$CR}, \\$CR"
+}
+
+make_completion()
+{
+       local CR='
+'
+       result="  {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\$CR"
+}
+
+template_loop()
+{
+       local loop_result=
+
+       local t_name="$name_txt"
+       local t_perms="$perms_txt"
+       local t_desc="$desc_txt"
+       local t_usage="$usage_txt"
+       local t_help="$help_txt"
+       local t_source_files="$source_files"
+       local member
+       for member in $template_members; do
+               name_txt="${t_name//@member@/$member}"
+               perms_txt="${t_perms//@member@/$member}"
+               desc_txt="${t_desc//@member@/$member}"
+               usage_txt="${t_usage//@member@/$member}"
+               help_txt="${t_help//@member@/$member}"
+               prototype="${template_prototype//@member@/$member}"
+               result=
+               $1
+               loop_result="$loop_result$result"
+       done
+       result="$loop_result"
+       # reset global variables
+       name_txt="$t_name"
+       perms_txt="$t_perms"
+       desc_txt="$t_desc"
+       usage_txt="$t_usage"
+       help_txt="$t_help"
+       source_files="$t_source_files"
+}
+
+com_header()
+{
+       local array_members handlers= CR='
+'
+
+       while : ; do
+               read_one_command
+               if test $ret -lt 0; then
+                       exit 1
+               fi
+               if test $ret -eq 0; then
+                       break
+               fi
+               if test $template -eq 0; then
+                       cmd_handler_name
+                       handlers+="$result"
+                       make_array_member
+                       array_members="$array_members$result"
+                       continue
+               fi
+               template_loop cmd_handler_name
+               handlers+="$result"
+               template_loop make_array_member
+               array_members="$array_members$result"
+       done
+       array_members="$array_members{.name = NULL} \\$CR"
+       echo "#define DEFINE_${base_name^^}_CMD_ARRAY $array_members"
+       echo "#define ${base_name^^}_COMMAND_HANDLERS ${handlers%,}"
+}
+
+com_completion()
+{
+
+       echo "#define $1 \\"
+       while : ; do
+               read_one_command
+               if test $ret -lt 0; then
+                       exit 1
+               fi
+               if test $ret -eq 0; then
+                       break
+               fi
+               if test $template -eq 0; then
+                       make_completion
+                       printf "%s" "$result"
+                       continue
+               fi
+               template_loop make_completion
+               printf "%s" "$result"
+       done
+       echo
+}
+
+read_header
+arg="$1"
+shift
+case "$arg" in
+       "h")
+               com_header
+               ;;
+       "man")
+               com_man $*
+               ;;
+       "compl")
+               com_completion $*
+               ;;
+esac
diff --git a/command_util.sh b/command_util.sh
deleted file mode 100755 (executable)
index e33e076..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-#!/usr/bin/env bash
-
-read_header()
-{
-       local key value i
-
-       while read key value; do
-               case "$key" in
-               ---)
-                       break
-                       ;;
-               BN:)
-                       base_name="$value"
-                       ;;
-               SF:)
-                       source_files="$value"
-                       ;;
-               SN:)
-                       section_name="$value"
-                       ;;
-               TM:)
-                       template_members="$value"
-               esac
-       done
-}
-
-read_one_command()
-{
-       local line
-
-       name_txt=""
-       desc_txt=""
-       usage_txt=""
-       help_txt=""
-       perms_txt=""
-       template=0
-       template_name=""
-       template_prototype=""
-       while read key value; do
-               case "$key" in
-               ---)
-                       break
-                       ;;
-               N:)
-                       name_txt="$value"
-                       ;;
-               T:)
-                       template_name="$value"
-                       template=1
-                       ;;
-               O:)
-                       template_prototype="$value"
-                       template=1
-                       ;;
-               P:)
-                       perms_txt="$value"
-                       ;;
-               D:)
-                       desc_txt="$value"
-                       ;;
-               U:)
-                       usage_txt="$value"
-                       ;;
-               H:)
-                       help_txt="${value}"
-                       while read line; do
-                               if test "$line" = "---"; then
-                                       break;
-                               fi
-                               line=${line#H:}
-                               help_txt="$help_txt
-${line# }"
-                       done
-                       break
-                       ;;
-               esac
-       done
-       if test $template -eq 0; then
-               if test -n "$name_txt" -a -n "$desc_txt" -a -n "$usage_txt" \
-                               -a -n "$help_txt"; then
-                       ret=1
-                       return
-               fi
-       else
-               if test -n "$template_name" -a -n "$template_prototype" \
-                               -a -n "$name_txt " -a -n "$template_members" \
-                               -a -n "$desc_txt" -a -n "$usage_txt" \
-                               -a -n "$help_txt"; then
-                       ret=1
-                       return
-               fi
-       fi
-       if test -z "$name_txt" -a -z "$desc_txt" -a -z "$usage_txt" \
-                       -a -z "$help_txt"; then
-               ret=0
-               return
-       fi
-       ret=-1
-       #return
-       echo "!ERROR!"
-       echo "N: $name_txt"
-       echo "D: $desc_txt"
-       echo "S: $usage_txt"
-       echo "P: $perms_txt"
-       echo "H: $help_txt"
-       echo "O: $template_prototype"
-}
-
-dump_man()
-{
-       if test $template -eq 0; then
-               echo ".SS \"$name_txt\""
-               echo "$desc_txt"
-               echo
-               echo "\\fBUsage: \\fP$usage_txt"
-       else
-               for member in $template_members; do
-                       local sed_cmd="sed -e s/@member@/$member/g"
-                       local t_name_txt=$(echo $name_txt | $sed_cmd)
-                       echo ".SS \"$t_name_txt\""
-               done
-               echo "$desc_txt" | sed -e "s/@member@/{$(echo $template_members | sed -e 's/ / | /g')}/g"
-               echo
-               echo "\\fBUsage: \\fP"
-               echo
-               echo ".nf"
-               for member in $template_members; do
-                       local sed_cmd="sed -e s/@member@/$member/g"
-                       local t_usage_txt=$(echo $usage_txt | $sed_cmd)
-                       printf "\t$t_usage_txt\n"
-               done
-               echo ".fi"
-       fi
-       echo
-       echo "$help_txt" | sed -e 's/^  //'
-       echo
-       if test -n "$perms_txt"; then
-               echo -n "\\fBpermissions:\\fP "
-               if test "$perms_txt" = "0"; then
-                       echo "(none)"
-               else
-                       echo "$perms_txt"
-               fi
-       fi
-       echo
-}
-
-
-com_man()
-{
-       echo "[$section_name]"
-       echo
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               dump_man
-       done
-}
-
-make_proto()
-{
-       local regex='\(__noreturn \)*\(static \)*int com_'
-       local source_file match="" all_commands CR='
-'
-       if test -n "$prototype"; then
-               result="$prototype$CR"
-               return
-       fi
-       all_commands="$(cat $source_files | grep "$regex")"
-       result=
-       for source_file in $source_files; do
-               match=$(grep "$regex$name_txt(" <<< "$all_commands" | head -n 1 | sed -e 's/$/;/1')
-               if test -n "$match"; then
-                       result="$result$match$CR"
-                       break
-               fi
-       done
-}
-
-make_array_member()
-{
-       local TAB='     ' CR='
-'
-       local tmp
-
-       result="{.name = \"$name_txt\", .handler = com_$name_txt, "
-       if test -n "$perms_txt"; then
-               result="$result .perms = $perms_txt,"
-       fi
-       result="$result.description = \"$desc_txt\", .usage = \"$usage_txt\", \\$CR .help = "
-       tmp="$(printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
-               -e "s/$TAB/\\\t/g" -e's/$/\\/g')"
-       result="$result$tmp$CR}, \\$CR"
-}
-
-make_completion()
-{
-       local CR='
-'
-       result="  {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\$CR"
-}
-
-template_loop()
-{
-       local loop_result=
-
-       local t_name="$name_txt"
-       local t_perms="$perms_txt"
-       local t_desc="$desc_txt"
-       local t_usage="$usage_txt"
-       local t_help="$help_txt"
-       local t_source_files="$source_files"
-       local member
-       for member in $template_members; do
-               name_txt="${t_name//@member@/$member}"
-               perms_txt="${t_perms//@member@/$member}"
-               desc_txt="${t_desc//@member@/$member}"
-               usage_txt="${t_usage//@member@/$member}"
-               help_txt="${t_help//@member@/$member}"
-               prototype="${template_prototype//@member@/$member}"
-               result=
-               $1
-               loop_result="$loop_result$result"
-       done
-       result="$loop_result"
-       # reset global variables
-       name_txt="$t_name"
-       perms_txt="$t_perms"
-       desc_txt="$t_desc"
-       usage_txt="$t_usage"
-       help_txt="$t_help"
-       source_files="$t_source_files"
-}
-
-com_header()
-{
-       local array_members CR='
-'
-
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               if test $template -eq 0; then
-                       make_proto
-                       printf "%s" "$result"
-                       make_array_member
-                       array_members="$array_members$result"
-                       continue
-               fi
-               template_loop make_proto
-               printf "%s" "$result"
-               template_loop make_array_member
-               array_members="$array_members$result"
-       done
-       array_members="$array_members{.name = NULL} \\$CR"
-       echo "#define DEFINE_$(tr 'a-z' 'A-Z' <<< "$base_name")_CMD_ARRAY $array_members"
-}
-
-com_completion()
-{
-
-       echo "#define $1 \\"
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               if test $template -eq 0; then
-                       make_completion
-                       printf "%s" "$result"
-                       continue
-               fi
-               template_loop make_completion
-               printf "%s" "$result"
-       done
-       echo
-}
-
-read_header
-arg="$1"
-shift
-case "$arg" in
-       "h")
-               com_header
-               ;;
-       "man")
-               com_man $*
-               ;;
-       "compl")
-               com_completion $*
-               ;;
-esac
index 0c0200e7e3b522af5aad7bb0a9f86f7e32024218..d0d96fd960955bcccedc731eb33221d5369e778f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -41,9 +41,9 @@ static void compress_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
-static int compress_post_select(__a_unused struct sched *s, struct task *t)
+static int compress_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_compress_data *pcd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        bool inplace = btr_inplace_ok(btrn);
index 1d1d6e3c079a16fc73c20685929cd8799f4a8c45..5960b0871ec7fc720bef13a90d9af5a58dd75e10 100644 (file)
 
 AC_PREREQ([2.61])
 
-
-AC_INIT([paraslash],[git],[maan@systemlinux.org])
-AC_CONFIG_HEADER([config.h])
+AC_INIT([paraslash], [m4_esyscmd_s(./GIT-VERSION-GEN)],
+       [maan@tuebingen.mpg.de], [], [http://people.tuebingen.mpg.de/maan/paraslash/])
+AC_CONFIG_HEADERS([config.h])
 
 AC_CONFIG_FILES([Makefile])
 AC_DEFUN([add_dot_o],[$(for i in $@; do printf "$i.o "; done)])
-AC_DEFUN([add_para],[$(for i in $@; do printf "para_$i "; done)])
-AC_DEFUN([objlist_to_errlist],[$(for i in $@; do printf "DEFINE_ERRLIST($(echo $i| tr 'a-z' 'A-Z'));"; done) [const char **para_errlist[[]]] = {$(for i in $@; do printf "PARA_ERRLIST($(echo $i | tr 'a-z' 'A-Z')), "; done) }])
-AC_PATH_PROG(UNAMEPATH, uname, no)
-if test "$UNAMEPATH" = "no"; then
-       AC_MSG_ERROR(unable to determine system type)
-fi
-AC_MSG_CHECKING(os type)
-OSTYPE="`$UNAMEPATH -s`"
-AC_MSG_RESULT("$OSTYPE")
+AC_DEFUN([add_cmdline],[$(for i in $@; do printf "${i}.cmdline "; done)])
+AC_DEFUN([make_errlist_defines], \
+       $(for i in $@; do \
+               printf "DEFINE_ERRLIST($(echo $i | tr 'a-z' 'A-Z'));"; \
+       done) \
+)
+AC_DEFUN([make_para_errlists], \
+       $(for i in $@; do \
+               printf "PARA_ERRLIST($(echo $i | tr 'a-z' 'A-Z')), "; \
+       done) \
+)
+AC_DEFUN([objlist_to_errlist],[ \
+       make_errlist_defines($@) \
+       [const char **para_errlist[[]]] = {make_para_errlists($@)} \
+])
+AC_DEFUN([LIB_ARG_WITH], [
+       AC_ARG_WITH($1-headers, [AS_HELP_STRING(--with-$1-headers=dir,
+               [look for $1 headers in dir])])
+       AC_ARG_WITH($1-libs, [AS_HELP_STRING(--with-$1-libs=dir,
+               [look for $1 libraries in dir])])
+       if test -n "$with_$1_headers"; then
+               $1_cppflags="-I$with_$1_headers"
+               CPPFLAGS="$CPPFLAGS $$1_cppflags"
+       fi
+       if test -n "$with_$1_libs"; then
+               $1_ldflags="-L$with_$1_libs $2"
+       else
+               $1_ldflags="$2"
+       fi
+       LDFLAGS="$LDFLAGS $$1_ldflags"
+])
 
-if test "$OSTYPE" = "SunOS"; then
-       # needed on SunOS for socket magic
-       arch_cppflags="-D_XOPEN_SOURCE=500 -D__EXTENSIONS__"
-       AC_SUBST(arch_cppflags)
-fi
+AC_DEFUN([STASH_FLAGS], [
+       OLD_CPPFLAGS="$CPPFLAGS"
+       OLD_LDFLAGS="$LDFLAGS"
+       OLD_LIBS="$LIBS"
+])
+
+AC_DEFUN([UNSTASH_FLAGS], [
+       CPPFLAGS="$OLD_CPPFLAGS"
+       LDFLAGS="$OLD_LDFLAGS"
+       LIBS="$OLD_LIBS"
+])
+AC_DEFUN([LIB_SUBST_FLAGS], [
+       if test "$HAVE_[]m4_toupper([$1])" = 'yes'; then
+               AC_DEFINE(HAVE_[]m4_toupper([$1]), 1,
+                       define to 1 to turn on $1 support)
+       else
+               $1_cppflags=
+               $1_ldflags=
+       fi
+       AC_SUBST(HAVE_[]m4_toupper([$1]))
+       AC_SUBST($1_cppflags)
+       AC_SUBST($1_ldflags)
+])
 
 AC_C_BIGENDIAN()
 
-AC_PATH_PROG([gengetopt], [gengetopt])
-test -z "$gengetopt" && AC_MSG_ERROR(
+AC_PATH_PROG([GENGETOPT], [gengetopt])
+test -z "$GENGETOPT" && AC_MSG_ERROR(
        [gengetopt is required to build this package])
 
-AC_PATH_PROG([help2man], [help2man])
-test -z "$help2man" && AC_MSG_ERROR(
+AC_PATH_PROG([HELP2MAN], [help2man])
+test -z "$HELP2MAN" && AC_MSG_ERROR(
        [help2man is required to build this package])
 
+AC_PATH_PROG([INSTALL], [install])
+test -z "$INSTALL" && AC_MSG_ERROR(
+       [The install program is required to build this package])
+
 AC_PROG_CC
 AC_PROG_CPP
-AC_PROG_INSTALL
-AC_SUBST(install_sh, [$INSTALL])
-AC_REPLACE_FNMATCH
-
-AC_HEADER_DIRENT
-AC_HEADER_STDC
-AC_HEADER_SYS_WAIT
-AC_HEADER_STDBOOL
-AC_CHECK_HEADERS([arpa/inet.h ctype.h fcntl.h limits.h netdb.h netinet/in.h \
-       stdlib.h string.h sys/socket.h sys/time.h sys/timeb.h sys/un.h \
-       sys/ipc.h unistd.h utime.h stddef.h],
-       [], [AC_MSG_ERROR([$ac_header not found])])
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_C_CONST
-AC_C_INLINE
-AC_TYPE_OFF_T
-AC_TYPE_PID_T
-AC_TYPE_SIZE_T
-AC_HEADER_TIME
-AC_STRUCT_TM
-AC_TYPE_INT8_T
-AC_TYPE_INT16_T
-AC_TYPE_INT32_T
-AC_TYPE_INT64_T
-AC_TYPE_MODE_T
-AC_TYPE_SSIZE_T
-AC_TYPE_UID_T
-AC_TYPE_UINT8_T
-AC_TYPE_UINT16_T
-AC_TYPE_UINT32_T
-AC_TYPE_UINT64_T
-
-# Checks for library functions.
-AC_FUNC_FORK
-AC_PROG_GCC_TRADITIONAL
-AC_FUNC_MALLOC
-AC_FUNC_MEMCMP
-AC_FUNC_MKTIME
-AC_FUNC_MMAP
-AC_FUNC_REALLOC
-AC_FUNC_SELECT_ARGTYPES
-AC_FUNC_STAT
-AC_FUNC_STRFTIME
-AC_FUNC_VPRINTF
-AC_FUNC_CLOSEDIR_VOID
-AC_FUNC_LSTAT
-
-AC_CHECK_FUNCS([atexit dup2 memchr memmove memset \
-       regcomp select strchr strdup strerror strstr strtol uname \
-       fchdir gettimeofday localtime_r munmap strcasecmp strcspn \
-       strncasecmp strrchr strspn alarm mkdir inet_ntoa socket], [],
-       [AC_MSG_ERROR([function not found, cannot live without it])])
-
-AC_DEFUN([add_cmdline],[$(for i in $@; do printf "${i}.cmdline "; done)])
-
-
-all_errlist_objs="mp3_afh afh_common net string signal time daemon
-       stat afh amp_filter fd ringbuffer sched audiod
-       grab_client filter_common wav_filter compress_filter http_recv
-       dccp_recv recv_common write_common file_write audiod_command
-       client_common recv stdout filter stdin audioc write client
-       exec send_common ggo udp_recv color fec fecdec_filter
-       prebuffer_filter bitstream imdct check_wav
-       wma_afh wma_common wmadec_filter buffer_tree crypt_common
-       gui gui_theme sideband afh_recv play version"
-
-executables="recv filter audioc write client afh audiod play"
-
-recv_cmdline_objs="add_cmdline(recv http_recv dccp_recv udp_recv afh_recv)"
-
-recv_errlist_objs="
-       http_recv recv_common recv time string net dccp_recv fd
-       sched stdout ggo udp_recv buffer_tree afh_recv afh_common
-       wma_afh wma_common mp3_afh version
-"
-
-recv_ldflags=""
-
-filter_cmdline_objs="add_cmdline(filter compress_filter amp_filter prebuffer_filter)"
-filter_errlist_objs="filter_common wav_filter compress_filter filter string
-       stdin stdout sched fd amp_filter ggo fecdec_filter fec version
-       prebuffer_filter time bitstream imdct wma_common wmadec_filter buffer_tree"
-filter_ldflags="-lm"
-filters=" compress wav amp fecdec wmadec prebuffer"
-
-audioc_cmdline_objs="add_cmdline(audioc)"
-audioc_errlist_objs="
-       audioc
-       string
-       net
-       fd
-       version
-       ggo
-"
-audioc_ldflags=""
-
-audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
-audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband
-       time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
-       recv_common fd sched write_common file_write audiod_command fecdec_filter
-       client_common ggo udp_recv color fec prebuffer_filter version
-       bitstream imdct wma_common wmadec_filter buffer_tree"
-audiod_ldflags="-lm"
-audiod_audio_formats="wma"
-
-afh_cmdline_objs="add_cmdline(afh)"
-afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common
-       version ggo"
-afh_ldflags=""
-
-write_cmdline_objs="add_cmdline(write file_write)"
-write_errlist_objs="write write_common file_write time fd string sched stdin
-       buffer_tree ggo check_wav version"
-write_ldflags=""
-writers=" file"
-default_writer="FILE_WRITE"
-
-client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="
-       client
-       net
-       string
-       fd
-       sched
-       stdin
-       stdout
-       time
-       sideband
-       client_common
-       buffer_tree
-       crypt_common
-       version
-       ggo
-"
-client_ldflags=""
 
-gui_cmdline_objs="add_cmdline(gui)"
-gui_errlist_objs="
-       exec
-       signal
-       string
-       stat
-       ringbuffer
-       fd
-       gui
-       gui_theme
-       time
-       version
-       ggo
-"
-gui_objs="$gui_cmdline_objs $gui_errlist_objs"
-play_errlist_objs="play fd sched ggo buffer_tree time string net
-       afh_recv afh_common
-       wma_afh wma_common mp3_afh
-       recv_common udp_recv http_recv dccp_recv
-       filter_common fec bitstream imdct
-       wav_filter compress_filter amp_filter prebuffer_filter fecdec_filter
-               wmadec_filter
-       write_common file_write
-       version
-"
-play_cmdline_objs="add_cmdline(http_recv dccp_recv udp_recv afh_recv compress_filter amp_filter prebuffer_filter file_write play)"
-play_ldflags="-lm"
-########################################################################### snprintf
-# ===========================================================================
-#        http://www.nongnu.org/autoconf-archive/ax_func_snprintf.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_FUNC_SNPRINTF
-#
-# DESCRIPTION
-#
-#   Checks for a fully C99 compliant snprintf, in particular checks whether
-#   it does bounds checking and returns the correct string length; does the
-#   same check for vsnprintf. If no working snprintf or vsnprintf is found,
-#   it prints an error message and aborts.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Ruediger Kuhlmann <info@ruediger-kuhlmann.de>
-#
-#   Copying and distribution of this file, with or without modification, are
-#   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved.
-
-AU_ALIAS([AC_FUNC_SNPRINTF], [AX_FUNC_SNPRINTF])
-AC_DEFUN([AX_FUNC_SNPRINTF],
-[AC_CHECK_FUNCS(snprintf vsnprintf)
-AC_MSG_CHECKING(for working snprintf)
-AC_CACHE_VAL(ac_cv_have_working_snprintf,
-[AC_RUN_IFELSE([AC_LANG_SOURCE([[
-#include <stdio.h>
-
-int main(void)
-{
-    char bufs[5] = { 'x', 'x', 'x', '\0', '\0' };
-    char bufd[5] = { 'x', 'x', 'x', '\0', '\0' };
-    int i;
-    i = snprintf (bufs, 2, "%s", "111");
-    if (strcmp (bufs, "1")) exit (1);
-    if (i != 3) exit (1);
-    i = snprintf (bufd, 2, "%d", 111);
-    if (strcmp (bufd, "1")) exit (1);
-    if (i != 3) exit (1);
-    exit(0);
-}]])],[ac_cv_have_working_snprintf=yes],
-[ac_cv_have_working_snprintf=no],[ac_cv_have_working_snprintf=cross])])
-AC_MSG_RESULT([$ac_cv_have_working_snprintf])
-AC_MSG_CHECKING(for working vsnprintf)
-AC_CACHE_VAL(ac_cv_have_working_vsnprintf,
-[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
-#include <stdarg.h>
-
-int my_vsnprintf (char *buf, const char *tmpl, ...)
-{
-    int i;
-    va_list args;
-    va_start (args, tmpl);
-    i = vsnprintf (buf, 2, tmpl, args);
-    va_end (args);
-    return i;
-}
-
-int main(void)
-{
-    char bufs[5] = { 'x', 'x', 'x', '\0', '\0' };
-    char bufd[5] = { 'x', 'x', 'x', '\0', '\0' };
-    int i;
-    i = my_vsnprintf (bufs, "%s", "111");
-    if (strcmp (bufs, "1")) exit (1);
-    if (i != 3) exit (1);
-    i = my_vsnprintf (bufd, "%d", 111);
-    if (strcmp (bufd, "1")) exit (1);
-    if (i != 3) exit (1);
-    exit(0);
-}]])],[ac_cv_have_working_vsnprintf=yes],
-[ac_cv_have_working_vsnprintf=no],[ac_cv_have_working_vsnprintf=cross])])
-AC_MSG_RESULT([$ac_cv_have_working_vsnprintf])
-if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; then
-AC_MSG_ERROR([fatal: buggy snprintf() detected])
-fi])
-AX_FUNC_SNPRINTF()
+executables="recv filter audioc write afh play"
 ################################################################## clock_gettime
 clock_gettime_lib=
 AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
@@ -294,88 +95,32 @@ fi
 if test "$clock_gettime_lib" = "rt"; then
        AC_SUBST(clock_gettime_ldflags, -lrt)
 fi
-########################################################################### osl
-have_osl=yes
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-AC_ARG_WITH(osl_headers, [AS_HELP_STRING(--with-osl-headers=dir,
-       [look for osl.h also in dir])])
-if test -n "$with_osl_headers"; then
-       osl_cppflags="-I$with_osl_headers"
-       CPPFLAGS="$CPPFLAGS $osl_cppflags"
-fi
-AC_ARG_WITH(osl_libs, [AS_HELP_STRING(--with-osl-libs=dir,
-       [look for libosl also in dir])])
-if test -n "$with_osl_libs"; then
-       osl_libs="-L$with_osl_libs"
-       LDFLAGS="$LDFLAGS $osl_libs"
-fi
-
-AC_CHECK_HEADER(osl.h, [], have_osl=no)
-AC_CHECK_LIB([osl], [osl_open_table], [], have_osl=no)
-if test "$have_osl" = "no"; then
-       AC_MSG_WARN([libosl not found, can not build para_server.
-Download libosl at
-       http://systemlinux.org/~maan/osl
-or execute
-       git clone git://git.tuebingen.mpg.de/osl
-       ])
-else
-       extras="$extras server"
-       executables="$executables server"
-       server_cmdline_objs="add_cmdline(server)"
-       server_errlist_objs="
-               server
-               afh_common
-               mp3_afh
-               vss command
-               net
-               string
-               signal
-               time
-               daemon
-               http_send
-               close_on_fork
-               mm
-               crypt_common
-               ipc dccp_send
-               fd
-               user_list
-               chunk_queue
-               afs
-               aft
-               mood
-               score
-               attribute
-               blob
-               playlist
-               sched
-               acl
-               send_common
-               udp_send
-               color
-               fec
-               wma_afh
-               wma_common
-               sideband
-               version
-               ggo
-       "
-       all_errlist_objs="$all_errlist_objs server vss command
-               http_send close_on_fork mm ipc dccp_send user_list
-               chunk_queue afs aft mood score attribute blob playlist
-               acl udp_send"
 
-       server_ldflags=""
-       audio_format_handlers="mp3 wma"
-       AC_SUBST(osl_cppflags)
-       server_ldflags="$server_ldflags $osl_libs -losl"
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### crypto
+########################################################################### osl
+STASH_FLAGS
+LIB_ARG_WITH([osl], [-losl])
+HAVE_OSL=yes
+AC_CHECK_HEADER(osl.h, [], [HAVE_OSL=no])
+AC_CHECK_LIB([osl], [osl_open_table], [], [HAVE_OSL=no])
+LIB_SUBST_FLAGS(osl)
+UNSTASH_FLAGS
+######################################################################## openssl
+STASH_FLAGS
+HAVE_OPENSSL=yes
+LIB_ARG_WITH([openssl], [-lssl -lcrypto])
+AC_CHECK_HEADER(openssl/ssl.h, [], [HAVE_OPENSSL=no])
+AC_CHECK_LIB([crypto], [RAND_bytes], [], [HAVE_OPENSSL=no])
+LIB_SUBST_FLAGS(openssl)
+UNSTASH_FLAGS
+######################################################################### gcrypt
+STASH_FLAGS
+HAVE_GCRYPT=yes
+LIB_ARG_WITH([gcrypt], [-lgcrypt])
+AC_CHECK_HEADER(gcrypt.h, [], [HAVE_GCRYPT=no])
+AC_CHECK_LIB([gcrypt], [gcry_randomize], [], [HAVE_GCRYPT=no])
+LIB_SUBST_FLAGS(gcrypt)
+UNSTASH_FLAGS
+######################################################################### crypto
 AC_ARG_ENABLE(cryptolib, [AS_HELP_STRING(--enable-cryptolib=lib, [
        Force using crypto library "lib". This package requires either
        openssl or libgcrypt being installed. Possible values for "lib"
@@ -383,139 +128,68 @@ AC_ARG_ENABLE(cryptolib, [AS_HELP_STRING(--enable-cryptolib=lib, [
        openssl is tried first. If openssl was not found, gcrypt is
        tried next.])])
 
+CRYPTOLIB="$enable_cryptolib"
 case "$enable_cryptolib" in
-       "openssl") check_openssl="yes"; check_gcrypt="no";;
-       "gcrypt") check_openssl="no"; check_gcrypt="yes";;
-       "") check_openssl="yes"; check_gcrypt="yes";;
-       *) AC_MSG_ERROR([invalid value "$enable_cryptolib" for --enable-cryptolib]);;
-esac
-###################################################################### openssl
-if test "$check_openssl" = "yes"; then
-       OLD_CPPFLAGS="$CPPFLAGS"
-       OLD_LDFLAGS="$LDFLAGS"
-       OLD_LIBS="$LIBS"
-       have_openssl="yes"
-       AC_ARG_WITH(openssl_headers, [AS_HELP_STRING(--with-openssl-headers=dir,
-               [look for openssl headers also in dir])])
-       if test -n "$with_openssl_headers"; then
-               openssl_cppflags="-I$with_openssl_headers"
-               CPPFLAGS="$CPPFLAGS $openssl_cppflags"
-       fi
-       AC_ARG_WITH(openssl_libs, [AS_HELP_STRING(--with-openssl-libs=dir,
-               [look for openssl libraries also in dir])])
-       if test -n "$with_openssl_libs"; then
-               openssl_libs="-L$with_openssl_libs"
-               LDFLAGS="$LDFLAGS $openssl_libs"
-       fi
-       AC_CHECK_HEADER(openssl/ssl.h, [], [have_openssl="no"])
-       AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
-       if test "$have_openssl" = "no" -a -z "$with_openssl_headers$with_openssl_libs"; then
-               # try harder: openssl is sometimes installed in /usr/local/ssl
-               openssl_cppflags="-I/usr/local/ssl/include"
-               CPPFLAGS="$CPPFLAGS $openssl_cppflags"
-               openssl_libs="-L/usr/local/ssl/lib"
-               LDFLAGS="$LDFLAGS $openssl_libs"
-               # clear cache
-               unset ac_cv_header_openssl_ssl_h 2> /dev/null
-               unset ac_cv_lib_crypto_RAND_bytes 2> /dev/null
-               AC_CHECK_HEADER(openssl/ssl.h, [have_openssl="yes"], [])
-               if test "$have_openssl" = "yes"; then
-                       AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
-               fi
-       fi
-       if test "$have_openssl" = "yes"; then
-               AC_DEFINE(HAVE_OPENSSL, 1, [define to 1 to turn on openssl support])
-               AC_SUBST(openssl_cppflags)
-               openssl_libs="$openssl_libs -lssl -lcrypto"
-               server_ldflags="$server_ldflags $openssl_libs"
-               client_ldflags="$client_ldflags $openssl_libs"
-               audiod_ldflags="$audiod_ldflags $openssl_libs"
-
-               all_errlist_objs="$all_errlist_objs crypt"
-               server_errlist_objs="$server_errlist_objs crypt"
-               client_errlist_objs="$client_errlist_objs crypt"
-               audiod_errlist_objs="$audiod_errlist_objs crypt"
-
-               check_gcrypt="no"
-       else
-               AC_MSG_WARN([openssl libraries not found])
-       fi
-       CPPFLAGS="$OLD_CPPFLAGS"
-       LDFLAGS="$OLD_LDFLAGS"
-       LIBS="$OLD_LIBS"
-else
-       have_openssl="no"
-fi
-########################################################################### gcrypt
-if test "$check_gcrypt" = "yes"; then
-       OLD_CPPFLAGS="$CPPFLAGS"
-       OLD_LDFLAGS="$LDFLAGS"
-       OLD_LIBS="$LIBS"
-       have_gcrypt="yes"
-       AC_ARG_WITH(gcrypt_headers, [AS_HELP_STRING(--with-gcrypt-headers=dir,
-               [look for gcrypt headers also in dir])])
-       if test -n "$with_gcrypt_headers"; then
-               gcrypt_cppflags="-I$with_gcrypt_headers"
-               CPPFLAGS="$CPPFLAGS $gcrypt_cppflags"
-       fi
-       AC_ARG_WITH(gcrypt_libs, [AS_HELP_STRING(--with-gcrypt-libs=dir,
-               [look for libgcrypt also in dir])])
-       if test -n "$with_gcrypt_libs"; then
-               gcrypt_libs="-L$with_gcrypt_libs"
-               LDFLAGS="$LDFLAGS $gcrypt_libs"
+"openssl")
+       test $HAVE_OPENSSL = no && AC_MSG_ERROR(openssl not found)
+       crypto_ldflags="$openssl_ldflags"
+       ;;
+"gcrypt")
+       test $HAVE_GCRYPT = no && AC_MSG_ERROR(gcrypt not found)
+       crypto_ldflags="$gcrypt_ldflags"
+       ;;
+"")
+       crypto_ldflags=
+       if test $HAVE_GCRYPT = yes; then
+               CRYPTOLIB=gcrypt
+               crypto_ldflags="$gcrypt_ldflags"
        fi
-       AC_CHECK_HEADER(gcrypt.h, [], [have_gcrypt="no"])
-       AC_CHECK_LIB([gcrypt], [gcry_randomize], [], [have_gcrypt="no"])
-       if test "$have_gcrypt" = "yes"; then
-               AC_DEFINE(HAVE_GCRYPT, 1, [define to 1 to turn on gcrypt support])
-               AC_SUBST(gcrypt_cppflags)
-               gcrypt_libs="$gcrypt_libs -lgcrypt"
-               server_ldflags="$server_ldflags $gcrypt_libs"
-               client_ldflags="$client_ldflags $gcrypt_libs"
-               audiod_ldflags="$audiod_ldflags $gcrypt_libs"
-
-               all_errlist_objs="$all_errlist_objs gcrypt"
-               server_errlist_objs="$server_errlist_objs gcrypt"
-               client_errlist_objs="$client_errlist_objs gcrypt"
-               audiod_errlist_objs="$audiod_errlist_objs gcrypt"
-       else
-               AC_MSG_WARN([gcrypt library not found])
+       if test $HAVE_OPENSSL = yes; then
+               CRYPTOLIB=openssl
+               crypto_ldflags="$openssl_ldflags"
        fi
-       CPPFLAGS="$OLD_CPPFLAGS"
-       LDFLAGS="$OLD_LDFLAGS"
-       LIBS="$OLD_LIBS"
-else
-       have_gcrypt="no"
-fi
-###########################################################################
-if test "$have_openssl" = "no" -a "$have_gcrypt" = "no"; then
-       AC_MSG_ERROR([neither openssl nor gcrypt usable])
-fi
+       ;;
+*)
+       AC_MSG_ERROR([invalid value "$enable_cryptolib" for --enable-cryptolib])
+       ;;
+esac
+AC_SUBST(crypto_ldflags)
 ########################################################################### libsocket
 AC_CHECK_LIB([c], [socket],
-       [socket_lib=],
-       [socket_lib="-lsocket"]
+       [socket_ldlflags=],
+       [socket_ldflags="-lsocket"]
 )
-server_ldflags="$server_ldflags $socket_lib"
-client_ldflags="$client_ldflags $socket_lib"
-audioc_ldflags="$audioc_ldflags $socket_lib"
-audiod_ldflags="$audiod_ldflags $socket_lib"
-recv_ldflags="$recv_ldflags $socket_lib"
-AC_SEARCH_LIBS([connect],[socket],[],[
-       AC_MSG_ERROR([Fatal: Did not find connect().])
-],[])
+AC_SUBST(socket_ldflags)
+########################################################################## iconv
+STASH_FLAGS
+LIBS=
+AC_SEARCH_LIBS([libiconv_open], [iconv],
+       [iconv_ldflags="$LIBS"],
+       []
+)
+AC_SUBST(iconv_ldflags)
+AC_MSG_CHECKING([whether iconv needs const char ** cast])
+AC_COMPILE_IFELSE([
+        AC_LANG_PROGRAM([
+                #include <iconv.h>
+        ],[
+                size_t iconv(iconv_t cd, const char **inbuf,
+                        size_t *inbytesleft, char **outbuf,
+                        size_t *outbytesleft);
+        ])
+],
+        [cast='(const char **)'; msg=yes],
+        [cast=; msg=no]
+)
+AC_DEFINE_UNQUOTED(ICONV_CAST, $cast, [cast for second arg to iconv()])
+AC_MSG_RESULT($msg)
+UNSTASH_FLAGS
 ########################################################################### libnsl
 AC_CHECK_LIB([c], [gethostbyname],
-       [nsl_lib=],
-       [nsl_lib="-lnsl"]
+       [nsl_ldflags=],
+       [nsl_ldflags="-lnsl"]
 )
-server_ldflags="$server_ldflags $nsl_lib"
-client_ldflags="$client_ldflags $nsl_lib"
-audioc_ldflags="$audioc_ldflags $nsl_lib"
-recv_ldflags="$recv_ldflags $nsl_lib"
-AC_SEARCH_LIBS([inet_ntoa],[nsl],[],[
-       AC_MSG_ERROR([Fatal: Did not find inet_ntoa().])
-],[])
+AC_SUBST(nsl_ldflags)
 ########################################################################### ucred
 AC_MSG_CHECKING(for struct ucred)
 AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@@ -530,7 +204,7 @@ if test ${have_ucred} = yes; then
        AC_DEFINE(HAVE_UCRED, 1, define to 1 you have struct ucred)
 fi
 ########################################################################### gengetopt
-echo 'option "z" z "" flag off' | $gengetopt --file-name conftest-ggo &&
+echo 'option "z" z "" flag off' | $GENGETOPT --file-name conftest-ggo &&
 AC_CHECK_DECL(
        [gengetopt_args_info_description],
        [ggo_descriptions_declared=yes],
@@ -539,45 +213,14 @@ AC_CHECK_DECL(
 )
 AC_SUBST(ggo_descriptions_declared)
 ########################################################################### curses
-have_curses="yes"
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-AC_ARG_WITH(curses_headers, [AS_HELP_STRING(--with-curses-headers=dir,
-       [look for curses.h also in dir])])
-if test -n "$with_curses_headers"; then
-       curses_cppflags="-I$with_curses_headers"
-       CPPFLAGS="$CPPFLAGS $curses_cppflags"
-fi
-AC_ARG_WITH(curses_libs, [AS_HELP_STRING(--with-curses-libs=dir,
-       [look for libcurses also in dir])])
-if test -n "$with_curses_libs"; then
-       curses_libs="-L$with_curses_libs"
-       LDFLAGS="$LDFLAGS $curses_libs"
-fi
-AC_CHECK_HEADER(curses.h, [], [
-       have_curses="no"
-])
-gui_ldflags="$curses_libs"
-AC_CHECK_LIB([ncursesw], [initscr],
-       [gui_ldflags="$curses_libs -lncursesw"], [
-               AC_CHECK_LIB([curses], [initscr],
-                       [gui_ldflags="$curses_libs -lcurses"],
-                       [have_curses="no"]
-               )
-       ]
-)
-if test "$have_curses" = "yes"; then
-       AC_SUBST(curses_cppflags)
-       extras="$extras gui"
-       executables="$executables gui"
-else
-       AC_MSG_WARN([no curses lib, cannot build para_gui])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-
+STASH_FLAGS
+LIB_ARG_WITH([curses], [])
+HAVE_CURSES=yes
+AC_CHECK_HEADER(curses.h, [], [HAVE_CURSES=no])
+AC_SEARCH_LIBS([initscr], [ncursesw curses], [], [HAVE_CURSES=no])
+curses_ldflags="$curses_ldflags $LIBS"
+LIB_SUBST_FLAGS(curses)
+UNSTASH_FLAGS
 ########################################################################### ip_mreqn
 AC_MSG_CHECKING(for struct ip_mreqn (UDPv4 multicast))
 AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@@ -605,482 +248,400 @@ if test ${have_core_audio} = yes; then
        f2="-framework AudioToolbox"
        f3="-framework AudioUnit"
        f4="-framework CoreServices"
-       f="$f1 $f2 $f3 $f4"
-
-       all_errlist_objs="$all_errlist_objs osx_write"
-       # ipc is linked into para_server server and into the osx writer. If osl
-       # was not found, para_server will not be built and ipc has not yet been
-       # added to the list of all objects, so we must add it here.
-       if test "$have_osl" = "no"; then
-               all_errlist_objs="$all_errlist_objs ipc"
-       fi
-       audiod_errlist_objs="$audiod_errlist_objs osx_write ipc"
-       audiod_cmdline_objs="$audiod_cmdline_objs osx_write.cmdline"
-       audiod_ldflags="$audiod_ldflags $f"
-
-       play_errlist_objs="$play_errlist_objs osx_write ipc"
-       play_cmdline_objs="$play_cmdline_objs osx_write.cmdline"
-       play_ldflags="$play_ldflags $f"
-
-       write_errlist_objs="$write_errlist_objs osx_write ipc"
-       write_cmdline_objs="$write_cmdline_objs osx_write.cmdline"
-       write_ldflags="$write_ldflags $f"
-       writers="$writers osx"
-       default_writer="OSX_WRITE"
+       core_audio_ldflags="$f1 $f2 $f3 $f4"
+       AC_SUBST(core_audio_ldflags)
        AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X)
 fi
-####################################################### ogg/vorbis/speex/opus
-have_ogg="yes"
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-AC_ARG_WITH(ogg_headers, [AS_HELP_STRING(--with-ogg-headers=dir,
-       [look for ogg headers also in dir])])
-AC_ARG_WITH(ogg_libs, [AS_HELP_STRING(--with-ogg-libs=dir,
-       [look for ogg libs also in dir])])
-AC_ARG_WITH(vorbis_headers, [AS_HELP_STRING(--with-vorbis-headers=dir,
-       [look for vorbis headers also in dir])])
-AC_ARG_WITH(vorbis_libs, [AS_HELP_STRING(--with-vorbis-libs=dir,
-       [look for vorbis libs also in dir])])
-AC_ARG_WITH(speex_headers, [AS_HELP_STRING(--with-speex-headers=dir,
-       [look for speex headers also in dir])])
-AC_ARG_WITH(speex_libs, [AS_HELP_STRING(--with-speex-libs=dir,
-       [look for speex libs also in dir])])
-AC_ARG_WITH(opus_headers, [AS_HELP_STRING(--with-opus-headers=dir,
-       [look for opus headers also in dir])])
-AC_ARG_WITH(opus_libs, [AS_HELP_STRING(--with-opus-libs=dir,
-       [look for opus libs also in dir])])
-
-if test -n "$with_ogg_headers"; then
-       ogg_cppflags="-I$with_ogg_headers"
-       CPPFLAGS="$CPPFLAGS $ogg_cppflags"
-fi
-if test -n "$with_ogg_libs"; then
-       ogg_libs="-L$with_ogg_libs"
-       LDFLAGS="$LDFLAGS $ogg_libs"
-fi
-AC_CHECK_HEADERS([ogg/ogg.h], [], [ have_ogg="no"; ])
-AC_CHECK_LIB([ogg], [ogg_stream_init], [], [ have_ogg="no" ])
-
-have_vorbis="yes"
-have_speex="yes"
-have_opus="yes"
-if test "$have_ogg" = "yes"; then
-       # vorbis
-       if test -n "$with_vorbis_headers"; then
-               vorbis_cppflags="-I$with_vorbis_headers"
-               CPPFLAGS="$CPPFLAGS $vorbis_cppflags"
-       fi
-       if test -n "$with_vorbis_libs"; then
-               vorbis_libs="-L$with_vorbis_libs"
-               LDFLAGS="$LDFLAGS $vorbis_libs"
-       fi
-       AC_CHECK_HEADERS([vorbis/codec.h], [], [ have_vorbis="no" ])
-       AC_CHECK_LIB([vorbis], [vorbis_info_init], [], [ have_vorbis="no" ])
-
-       # speex
-       if test -n "$with_speex_headers"; then
-               speex_cppflags="-I$with_speex_headers"
-               CPPFLAGS="$CPPFLAGS $speex_cppflags"
-       fi
-       if test -n "$with_speex_libs"; then
-               speex_libs="-L$with_speex_libs"
-               LDFLAGS="$LDFLAGS $speex_libs"
-       fi
-       AC_CHECK_LIB([speex], [speex_decoder_init], [], [ have_speex="no" ])
-       AC_CHECK_HEADERS([speex/speex.h], [], [ have_speex="no" ])
-
-       # opus
-       if test -n "$with_opus_headers"; then
-               opus_cppflags="-I$with_opus_headers"
-               CPPFLAGS="$CPPFLAGS $opus_cppflags"
+########################################################################### ogg
+STASH_FLAGS
+LIB_ARG_WITH([ogg], [-logg])
+HAVE_OGG=yes
+AC_CHECK_HEADERS([ogg/ogg.h], [], [HAVE_OGG=no])
+AC_CHECK_LIB([ogg], [ogg_stream_init], [], [HAVE_OGG=no])
+AC_CHECK_LIB([ogg], [ogg_stream_flush_fill], [
+       AC_DEFINE(HAVE_OGG_STREAM_FLUSH_FILL, 1, [libogg >= 1.3.0])])
+LIB_SUBST_FLAGS(ogg)
+UNSTASH_FLAGS
+######################################################################### vorbis
+STASH_FLAGS
+LIB_ARG_WITH([vorbis], [-lvorbis -lvorbisfile])
+HAVE_VORBIS=yes
+AC_CHECK_HEADERS([vorbis/codec.h], [], [HAVE_VORBIS=no])
+AC_CHECK_LIB([vorbis], [vorbis_info_init], [], [HAVE_VORBIS=no])
+LIB_SUBST_FLAGS(vorbis)
+UNSTASH_FLAGS
+######################################################################### speex
+STASH_FLAGS
+LIB_ARG_WITH([speex], [-lspeex])
+HAVE_SPEEX=yes
+AC_CHECK_HEADERS([speex/speex.h], [], [HAVE_SPEEX=no])
+AC_CHECK_LIB([speex], [speex_decoder_init], [], [HAVE_SPEEX=no])
+LIB_SUBST_FLAGS(speex)
+UNSTASH_FLAGS
+######################################################################### opus
+STASH_FLAGS
+LIB_ARG_WITH([opus], [-lopus])
+HAVE_OPUS=yes
+AC_CHECK_HEADERS([opus/opus.h], [], [HAVE_OPUS=no])
+AC_CHECK_LIB([opus], [opus_multistream_decode], [], [HAVE_OPUS=no])
+LIB_SUBST_FLAGS(opus)
+UNSTASH_FLAGS
+########################################################################### flac
+STASH_FLAGS
+LIB_ARG_WITH([flac], [-lFLAC -lm])
+HAVE_FLAC=yes
+AC_CHECK_HEADER(FLAC/stream_decoder.h, [], HAVE_FLAC=no)
+AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_init_file], [], HAVE_FLAC=no)
+LIB_SUBST_FLAGS(flac)
+UNSTASH_FLAGS
+
+# some helper functions for codecs which use the ogg container format
+AC_DEFUN([NEED_OGG_OBJECTS], [{
+       test "$HAVE_OGG" = 'yes' -a \( \
+                "$HAVE_VORBIS" = 'yes' \
+               -o "$HAVE_SPEEX" = 'yes' \
+               -o "$HAVE_OPUS" = 'yes' \
+               -o "$HAVE_FLAC" = 'yes' \
+       \)
+}])
+AC_DEFUN([NEED_VORBIS_OBJECTS], [{
+       test "$HAVE_OGG" = 'yes' -a "$HAVE_VORBIS" = 'yes'
+}])
+AC_DEFUN([NEED_SPEEX_OBJECTS], [{
+       test "$HAVE_OGG" = 'yes' -a "$HAVE_SPEEX" = 'yes'
+}])
+AC_DEFUN([NEED_OPUS_OBJECTS], [{
+       test "$HAVE_OGG" = 'yes' -a "$HAVE_OPUS" = 'yes'
+}])
+AC_DEFUN([NEED_FLAC_OBJECTS], [{
+       test "$HAVE_OGG" = 'yes' -a "$HAVE_FLAC" = 'yes'
+}])
+########################################################################### faad
+STASH_FLAGS
+LIB_ARG_WITH([faad], [-lfaad])
+HAVE_FAAD=yes
+AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
+AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
+LIB_SUBST_FLAGS(faad)
+UNSTASH_FLAGS
+########################################################################### mad
+STASH_FLAGS
+LIB_ARG_WITH([mad], [-lmad])
+HAVE_MAD=yes
+AC_CHECK_HEADER(mad.h, [], HAVE_MAD=no)
+AC_CHECK_LIB([mad], [mad_stream_init], [], HAVE_MAD=no)
+LIB_SUBST_FLAGS(mad)
+UNSTASH_FLAGS
+###################################################################### libid3tag
+STASH_FLAGS
+LIB_ARG_WITH([id3tag], [-lid3tag -lz])
+HAVE_ID3TAG=yes
+AC_CHECK_HEADER(id3tag.h, [], HAVE_ID3TAG=no)
+AC_CHECK_LIB([id3tag], [id3_file_fdopen], [], HAVE_ID3TAG=no)
+LIB_SUBST_FLAGS(id3tag)
+UNSTASH_FLAGS
+########################################################################### oss
+STASH_FLAGS
+LIB_ARG_WITH([oss], [])
+AC_CHECK_HEADER(sys/soundcard.h, [HAVE_OSS=yes], [HAVE_OSS=no])
+AC_CHECK_LIB(ossaudio, _oss_ioctl, [oss_ldflags="$oss_ldflags -lossaudio"], [])
+LIB_SUBST_FLAGS(oss)
+UNSTASH_FLAGS
+########################################################################### alsa
+STASH_FLAGS
+LIB_ARG_WITH([alsa], [-lasound])
+HAVE_ALSA=yes
+AC_CHECK_HEADER(alsa/asoundlib.h, [], HAVE_ALSA=no)
+AC_CHECK_LIB([asound], [snd_pcm_open], [], HAVE_ALSA=no)
+LIB_SUBST_FLAGS(alsa)
+UNSTASH_FLAGS
+######################################################################### pthread
+STASH_FLAGS
+LIB_ARG_WITH([pthread], [-lpthread])
+HAVE_PTHREAD=yes
+AC_CHECK_HEADER(pthread.h, [], HAVE_PTHREAD=no)
+AC_CHECK_LIB([pthread], [pthread_create], [], HAVE_PTHREAD=no)
+LIB_SUBST_FLAGS(pthread)
+UNSTASH_FLAGS
+########################################################################### libao
+STASH_FLAGS
+LIB_ARG_WITH([ao], [-lao])
+HAVE_AO=yes
+AC_CHECK_HEADER(ao/ao.h, [], HAVE_AO=no)
+AC_CHECK_LIB([ao], [ao_initialize], [], HAVE_AO=no)
+LIB_SUBST_FLAGS(ao)
+UNSTASH_FLAGS
+AC_DEFUN([NEED_AO_OBJECTS], [{ test $HAVE_AO = yes -a $HAVE_PTHREAD = yes; }])
+######################################################################## readline
+STASH_FLAGS
+AC_SEARCH_LIBS([tgetent], [tinfo curses terminfo termcap])
+LIB_ARG_WITH([readline], [-lreadline $LIBS])
+HAVE_READLINE=yes
+AC_CHECK_HEADER([readline/readline.h], [], [HAVE_READLINE=no])
+AC_CHECK_LIB([readline], [rl_free_keymap], [], HAVE_READLINE=no)
+AC_CHECK_DECL(
+       [rl_free_keymap],
+       [AC_DEFINE(RL_FREE_KEYMAP_DECLARED, 1, readline >= 6.3)],
+       [],
+       [
+               #include <stdio.h>
+               #include <readline/readline.h>
+       ]
+)
+LIB_SUBST_FLAGS(readline)
+UNSTASH_FLAGS
+############################################################# libsamplerate
+STASH_FLAGS
+LIB_ARG_WITH([samplerate], [-lsamplerate])
+HAVE_SAMPLERATE=yes
+AC_CHECK_HEADER(samplerate.h, [], HAVE_SAMPLERATE=no)
+AC_CHECK_LIB([samplerate], [src_process], [], HAVE_SAMPLERATE=no)
+LIB_SUBST_FLAGS(samplerate)
+UNSTASH_FLAGS
+########################################################################## mp4v2
+STASH_FLAGS
+LIB_ARG_WITH([mp4v2], [-lmp4v2])
+HAVE_MP4V2=yes
+AC_CHECK_HEADER([mp4v2/mp4v2.h], [], [HAVE_MP4V2=no])
+AC_CHECK_LIB([mp4v2], [MP4Read], [], [HAVE_MP4V2=no])
+LIB_SUBST_FLAGS(mp4v2)
+UNSTASH_FLAGS
+######################################################################### server
+if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
+       build_server="yes"
+       executables="$executables server"
+       server_cmdline_objs="server"
+       server_errlist_objs="
+               server
+               afh_common
+               mp3_afh
+               vss
+               command
+               net
+               string
+               signal
+               time
+               daemon
+               http_send
+               close_on_fork
+               mm
+               crypt_common
+               ipc
+               dccp_send
+               fd
+               user_list
+               chunk_queue
+               afs
+               aft
+               mood
+               score
+               attribute
+               blob
+               playlist
+               sched
+               acl
+               send_common
+               udp_send
+               color
+               fec
+               wma_afh
+               wma_common
+               sideband
+               version
+               ggo
+       "
+       if test "$CRYPTOLIB" = openssl; then
+               server_errlist_objs="$server_errlist_objs crypt"
+       else
+               server_errlist_objs="$server_errlist_objs gcrypt"
        fi
-       if test -n "$with_opus_libs"; then
-               opus_libs="-L$with_opus_libs"
-               LDFLAGS="$LDFLAGS $opus_libs"
+       NEED_OGG_OBJECTS() && server_errlist_objs="$server_errlist_objs ogg_afh_common"
+       NEED_VORBIS_OBJECTS() && server_errlist_objs="$server_errlist_objs ogg_afh"
+       NEED_SPEEX_OBJECTS() && server_errlist_objs="$server_errlist_objs spx_afh spx_common"
+       NEED_OPUS_OBJECTS() && server_errlist_objs="$server_errlist_objs opus_afh opus_common"
+       NEED_FLAC_OBJECTS && server_errlist_objs="$server_errlist_objs flac_afh"
+       if test $HAVE_FAAD = yes && test $HAVE_MP4V2 = yes; then
+               server_errlist_objs="$server_errlist_objs aac_afh aac_common"
        fi
-       AC_CHECK_LIB([opus], [opus_multistream_decode], [], [ have_opus="no" ])
-       AC_CHECK_HEADERS([opus/opus.h], [], [ have_opus="no" ])
+       server_objs="add_cmdline($server_cmdline_objs) $server_errlist_objs"
+       AC_SUBST(server_objs, add_dot_o($server_objs))
+       AC_DEFINE_UNQUOTED(INIT_SERVER_ERRLISTS,
+               objlist_to_errlist($server_errlist_objs), errors used by para_server)
 else
-       AC_MSG_WARN([vorbis/speex/opus depend on libogg, which was not detected])
-       have_vorbis="no"
-       have_speex="no"
-       have_opus="no"
+       build_server="no"
 fi
-
-msg="support in para_server/para_filter/para_afh"
-if test "$have_vorbis" = "yes" || \
-               test "$have_speex" = "yes" || \
-               test "$have_opus" = "yes"; then
-       AC_SUBST(ogg_cppflags)
-       ogg_libs="$ogg_libs -logg"
-       if test "$OSTYPE" = "Darwin"; then
-               ogg_libs="-Wl,-bind_at_load $ogg_libs"
+############################################################# client
+if test -n "$CRYPTOLIB"; then
+       build_client="yes"
+       executables="$executables client"
+       client_cmdline_objs="client"
+       client_errlist_objs="
+               client
+               net
+               string
+               fd
+               sched
+               stdin
+               stdout
+               time
+               sideband
+               client_common
+               buffer_tree
+               crypt_common
+               version
+               ggo
+       "
+       if test "$CRYPTOLIB" = openssl; then
+               client_errlist_objs="$client_errlist_objs crypt"
+       else
+               client_errlist_objs="$client_errlist_objs gcrypt"
+       fi
+       if test $HAVE_READLINE = yes; then
+               client_errlist_objs="$client_errlist_objs interactive"
        fi
-       server_ldflags="$server_ldflags $ogg_libs"
-       filter_ldflags="$filter_ldflags $ogg_libs"
-       audiod_ldflags="$audiod_ldflags $ogg_libs"
-       play_ldflags="$play_ldflags $ogg_libs"
-       afh_ldflags="$afh_ldflags $ogg_libs"
-       recv_ldflags="$recv_ldflags $ogg_libs"
-       all_errlist_objs="$all_errlist_objs ogg_afh_common"
-       afh_errlist_objs="$afh_errlist_objs ogg_afh_common"
-       recv_errlist_objs="$recv_errlist_objs ogg_afh_common"
-       server_errlist_objs="$server_errlist_objs ogg_afh_common"
-       play_errlist_objs="$play_errlist_objs ogg_afh_common"
+       client_objs="add_cmdline($client_cmdline_objs) $client_errlist_objs"
+       AC_SUBST(client_objs, add_dot_o($client_objs))
+       AC_DEFINE_UNQUOTED(INIT_CLIENT_ERRLISTS,
+               objlist_to_errlist($client_errlist_objs), errors used by para_client)
+else
+       build_client="no"
 fi
-if test "$have_vorbis" = "yes"; then
-       all_errlist_objs="$all_errlist_objs oggdec_filter ogg_afh"
-       AC_DEFINE(HAVE_OGGVORBIS, 1, define to 1 to turn on ogg/vorbis support)
-       filters="$filters oggdec"
-       vorbis_libs="-lvorbis -lvorbisfile"
-       server_ldflags="$server_ldflags $vorbis_libs"
-       filter_ldflags="$filter_ldflags $vorbis_libs"
-       audiod_ldflags="$audiod_ldflags $vorbis_libs"
-       play_ldflags="$play_ldflags $vorbis_libs"
-       afh_ldflags="$afh_ldflags $vorbis_libs"
-       recv_ldflags="$recv_ldflags $vorbis_libs"
-
-       server_errlist_objs="$server_errlist_objs ogg_afh"
-       filter_errlist_objs="$filter_errlist_objs oggdec_filter"
-       audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
-       play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
-       afh_errlist_objs="$afh_errlist_objs ogg_afh"
-       recv_errlist_objs="$recv_errlist_objs ogg_afh"
-
-       audiod_audio_formats="$audiod_audio_formats ogg"
-       audio_format_handlers="$audio_format_handlers ogg"
-else
-       AC_MSG_WARN([no ogg/vorbis $msg])
-fi
-if test "$have_speex" = "yes"; then
-       all_errlist_objs="$all_errlist_objs spxdec_filter spx_afh spx_common"
-       AC_DEFINE(HAVE_SPEEX, 1, define to 1 to turn on ogg/speex support)
-       filters="$filters spxdec"
-       speex_libs="-lspeex"
-       server_ldflags="$server_ldflags $speex_libs"
-       filter_ldflags="$filter_ldflags $speex_libs"
-       audiod_ldflags="$audiod_ldflags $speex_libs"
-       play_ldflags="$play_ldflags $speex_libs"
-       afh_ldflags="$afh_ldflags $speex_libs"
-       recv_ldflags="$recv_ldflags $speex_libs"
-
-       server_errlist_objs="$server_errlist_objs spx_afh spx_common"
-       filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
-       audiod_errlist_objs="$audiod_errlist_objs spxdec_filter spx_common"
-       play_errlist_objs="$play_errlist_objs spxdec_filter spx_afh spx_common"
-       afh_errlist_objs="$afh_errlist_objs spx_afh spx_common"
-       recv_errlist_objs="$recv_errlist_objs spx_afh spx_common"
-
-       audiod_audio_formats="$audiod_audio_formats spx"
-       audio_format_handlers="$audio_format_handlers spx"
-else
-       AC_MSG_WARN([no ogg/speex $msg])
-fi
-if test "$have_opus" = "yes"; then
-       all_errlist_objs="$all_errlist_objs opusdec_filter opus_afh opus_common"
-       AC_DEFINE(HAVE_OPUS, 1, define to 1 to turn on ogg/opus support)
-       filters="$filters opusdec"
-       opus_libs="-lopus"
-       server_ldflags="$server_ldflags $opus_libs"
-       filter_ldflags="$filter_ldflags $opus_libs"
-       audiod_ldflags="$audiod_ldflags $opus_libs"
-       afh_ldflags="$afh_ldflags $opus_libs"
-       play_ldflags="$play_ldflags $opus_libs"
-       recv_ldflags="$recv_ldflags $opus_libs"
-
-       server_errlist_objs="$server_errlist_objs opus_afh opus_common"
-       filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
-       audiod_errlist_objs="$audiod_errlist_objs opusdec_filter opus_common"
-       afh_errlist_objs="$afh_errlist_objs opus_afh opus_common"
-       play_errlist_objs="$play_errlist_objs opusdec_filter opus_afh opus_common"
-       recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
-
-       audiod_audio_formats="$audiod_audio_formats opus"
-       audio_format_handlers="$audio_format_handlers opus"
-else
-       AC_MSG_WARN([no ogg/opus $msg])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### faad
-have_faad=yes
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-AC_ARG_WITH(faad_headers, [AS_HELP_STRING(--with-faad-headers=dir,
-       [look for neaacdec.h also in dir])])
-if test -n "$with_faad_headers"; then
-       faad_cppflags="-I$with_faad_headers"
-       CPPFLAGS="$CPPFLAGS $faad_cppflags"
-fi
-AC_ARG_WITH(faad_libs, [AS_HELP_STRING(--with-faad-libs=dir,
-       [look for libfaad also in dir])])
-if test -n "$with_faad_libs"; then
-       faad_libs="-L$with_faad_libs"
-       LDFLAGS="$LDFLAGS $faad_libs"
-fi
-AC_CHECK_HEADER(neaacdec.h, [], have_faad=no)
-AC_CHECK_LIB([faad], [NeAACDecOpen], [], have_faad=no)
-if test "$have_faad" = "yes"; then
-       AC_DEFINE(HAVE_FAAD, 1, define to 1 if you want to build the aacdec filter)
-       all_errlist_objs="$all_errlist_objs aac_common aacdec_filter aac_afh"
-       filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
-       afh_errlist_objs="$afh_errlist_objs aac_common aac_afh"
-       audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
-       play_errlist_objs="$play_errlist_objs aacdec_filter aac_afh aac_common"
-       server_errlist_objs="$server_errlist_objs aac_afh aac_common"
-       recv_errlist_objs="$recv_errlist_objs aac_afh aac_common"
-
-       server_ldflags="$server_ldflags $faad_libs -lfaad"
-       filter_ldflags="$filter_ldflags $faad_libs -lfaad"
-       audiod_ldflags="$audiod_ldflags $faad_libs -lfaad"
-       play_ldflags="$play_ldflags $faad_libs -lfaad"
-       afh_ldflags="$afh_ldflags $faad_libs -lfaad"
-       recv_ldflags="$afh_ldflags $faad_libs -lfaad"
-
-       audiod_audio_formats="$audiod_audio_formats aac"
-       audio_format_handlers="$audio_format_handlers aac"
-       filters="$filters aacdec"
-       AC_SUBST(faad_cppflags)
-else
-       AC_MSG_WARN([no aac support in para_audiod/para_filter])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### mad
-have_mad="yes"
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-AC_ARG_WITH(mad_headers, [AS_HELP_STRING(--with-mad-headers=dir,
-       [look for mad.h also in dir])])
-if test -n "$with_mad_headers"; then
-       mad_cppflags="-I$with_mad_headers"
-       CPPFLAGS="$CPPFLAGS $mad_cppflags"
-fi
-AC_ARG_WITH(mad_libs, [AS_HELP_STRING(--with-mad-libs=dir,
-       [look for libmad also in dir])])
-if test -n "$with_mad_libs"; then
-       mad_libs="-L$with_mad_libs"
-       LDFLAGS="$LDFLAGS $mad_libs"
-fi
-AC_CHECK_HEADERS([mad.h], [], [
-       have_mad="no"
-])
-AC_CHECK_LIB([mad], [mad_stream_init], [], [
-       have_mad="no"
-])
-if test "$have_mad" = "yes"; then
-       AC_DEFINE(HAVE_MAD, 1, define to 1 if you want to build the mp3dec filter)
-       filter_cmdline_objs="$filter_cmdline_objs add_cmdline(mp3dec_filter)"
-       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(mp3dec_filter)"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(mp3dec_filter)"
-       all_errlist_objs="$all_errlist_objs mp3dec_filter"
-       filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
-       audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
-       play_errlist_objs="$play_errlist_objs mp3dec_filter"
-       filter_ldflags="$filter_ldflags $mad_libs -lmad"
-       audiod_ldflags="$audiod_ldflags $mad_libs -lmad"
-       play_ldflags="$play_ldflags $mad_libs -lmad"
-       audiod_audio_formats="$audiod_audio_formats mp3"
-       filters="$filters mp3dec"
-       AC_SUBST(mad_cppflags)
-else
-       AC_MSG_WARN([no mp3dec support in para_audiod/para_filter])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-###################################################################### libid3tag
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_libid3tag="yes"
-AC_ARG_WITH(id3tag_headers, [AS_HELP_STRING(--with-id3tag-headers=dir,
-       [look for id3tag header files also in dir])])
-if test -n "$with_id3tag_headers"; then
-       id3tag_cppflags="-I$with_id3tag_headers"
-       CPPFLAGS="$CPPFLAGS $id3tag_cppflags"
-fi
-AC_ARG_WITH(id3tag_libs, [AS_HELP_STRING(--with-id3tag-libs=dir,
-       [look for id3tag libs also in dir])])
-if test -n "$with_id3tag_libs"; then
-       id3tag_libs="-L$with_id3tag_libs"
-       LDFLAGS="$LDFLAGS $id3tag_libs"
-fi
-
-AC_MSG_CHECKING(for libid3tag)
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-       #include <id3tag.h>
-]], [[
-       struct id3_tag t = {.flags = 0};
-]])],[],[have_libid3tag=no])
-AC_MSG_RESULT($have_libid3tag)
-
-if test ${have_libid3tag} = yes; then
-       AC_DEFINE(HAVE_LIBID3TAG, 1, define to 1 you have libid3tag)
-       server_ldflags="$server_ldflags $id3tag_libs -lid3tag -lz"
-       afh_ldflags="$afh_ldflags $id3tag_libs -lid3tag -lz"
-       play_ldflags="$play_ldflags -lz"
-       recv_ldflags="$recv_ldflags $id3tag_libs -lid3tag"
-       play_ldflags="$play_ldflags $id3tag_libs -lid3tag"
-       AC_SUBST(id3tag_cppflags)
-else
-       AC_MSG_WARN([no support for id3v2 tags])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### flac
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_flac="yes"
-AC_ARG_WITH(flac_headers, [AS_HELP_STRING(--with-flac-headers=dir,
-       [look for flac headers also in dir])])
-if test -n "$with_flac_headers"; then
-       flac_cppflags="-I$with_flac_headers"
-       CPPFLAGS="$CPPFLAGS $flac_cppflags"
-fi
-AC_ARG_WITH(flac_libs, [AS_HELP_STRING(--with-flac-libs=dir,
-       [look for flac libs also in dir])])
-if test -n "$with_flac_libs"; then
-       flac_libs="-L$with_flac_libs"
-       LDFLAGS="$LDFLAGS $flac_libs"
-fi
-AC_CHECK_HEADER(FLAC/stream_decoder.h, [], have_flac=no)
-AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_init_file], [], have_flac=no, -logg -lm)
-if test "$have_flac" = "yes"; then
-       AC_DEFINE(HAVE_FLAC, 1, define to 1 if you want to build the flacdec filter)
-       all_errlist_objs="$all_errlist_objs flacdec_filter flac_afh"
-       filter_errlist_objs="$filter_errlist_objs flacdec_filter"
-       audiod_errlist_objs="$audiod_errlist_objs flacdec_filter"
-       play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
-       afh_errlist_objs="$afh_errlist_objs flac_afh"
-       server_errlist_objs="$server_errlist_objs flac_afh"
-       recv_errlist_objs="$recv_errlist_objs flac_afh"
-       filter_ldflags="$filter_ldflags $flac_libs -lFLAC"
-       audiod_ldflags="$audiod_ldflags $flac_libs -lFLAC"
-       play_ldflags="$play_ldflags $flac_libs -lFLAC"
-       server_ldflags="$server_ldflags $flac_libs -lFLAC"
-       afh_ldflags="$afh_ldflags $flac_libs -lFLAC"
-       recv_ldflags="$recv_ldflags $flac_libs -lFLAC"
-       filters="$filters flacdec"
-       audio_format_handlers="$audio_format_handlers flac"
-       audiod_audio_formats="$audiod_audio_formats flac"
-       AC_SUBST(flac_cppflags)
-else
-       AC_MSG_WARN([no flac support in para_audiod/para_filter/para_afh/para_server])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### oss
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_oss="yes"
-msg="=> will not build oss writer"
-
-AC_CHECK_HEADER(sys/soundcard.h, [
-       audiod_errlist_objs="$audiod_errlist_objs oss_write"
-       play_errlist_objs="$play_errlist_objs oss_write"
-       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(oss_write)"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(oss_write)"
-
-       write_errlist_objs="$write_errlist_objs oss_write"
-       write_cmdline_objs="$write_cmdline_objs add_cmdline(oss_write)"
-       fade_errlist_objs="$fade_errlist_objs oss_mix"
-       all_errlist_objs="$all_errlist_objs oss_write oss_mix"
-
-       writers="$writers oss"
-       default_writer="OSS_WRITE"
-       mixers="${mixers}oss "
-       default_mixer="OSS_MIX"
-
-       AC_CHECK_LIB(ossaudio, _oss_ioctl, [
-                       audiod_ldflags="$audiod_ldflags -lossaudio"
-                       play_ldflags="$play_ldflags -lossaudio"
-                       write_ldflags="$write_ldflags -lossaudio"
-                       fade_ldflags="$fade_ldflags -lossaudio"
-               ]
-       )
-       ],
-       [
-               have_oss="no"
-               AC_MSG_WARN([no sys/soundcard.h $msg])
-       ]
-)
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-
-########################################################################### alsa
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-msg="=> no alsa support for para_audiod/para_write"
-if test "$OSTYPE" != "Linux"; then
-       have_alsa="no"
+############################################################# audiod
+if test -n "$CRYPTOLIB"; then
+       build_audiod="yes"
+       executables="$executables audiod"
+       audiod_audio_formats="wma"
+       audiod_cmdline_objs="$audiod_cmdline_objs
+               audiod
+               compress_filter
+               http_recv
+               dccp_recv
+               file_write
+               client
+               amp_filter
+               udp_recv
+               prebuffer_filter
+               sync_filter
+       "
+       audiod_errlist_objs="$audiod_errlist_objs
+               audiod
+               signal
+               string
+               daemon
+               stat
+               net
+               crypt_common
+               sideband
+               time
+               grab_client
+               filter_common
+               wav_filter
+               compress_filter
+               amp_filter
+               http_recv
+               dccp_recv
+               recv_common
+               fd
+               sched
+               write_common
+               file_write
+               audiod_command
+               fecdec_filter
+               client_common
+               ggo
+               udp_recv
+               color
+               fec
+               prebuffer_filter
+               version
+               bitstream
+               imdct
+               wma_common
+               wmadec_filter
+               buffer_tree
+               sync_filter
+       "
+       if test "$CRYPTOLIB" = openssl; then
+               audiod_errlist_objs="$audiod_errlist_objs crypt"
+       else
+               audiod_errlist_objs="$audiod_errlist_objs gcrypt"
+       fi
+       if test "$have_core_audio" = "yes"; then
+               audiod_errlist_objs="$audiod_errlist_objs osx_write ipc"
+               audiod_cmdline_objs="$audiod_cmdline_objs osx_write"
+       fi
+       NEED_VORBIS_OBJECTS && {
+               audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
+               audiod_audio_formats="$audiod_audio_formats ogg"
+       }
+       NEED_SPEEX_OBJECTS && {
+               audiod_errlist_objs="$audiod_errlist_objs spxdec_filter spx_common"
+               audiod_audio_formats="$audiod_audio_formats spx"
+       }
+       NEED_OPUS_OBJECTS && {
+               audiod_errlist_objs="$audiod_errlist_objs opusdec_filter opus_common"
+               audiod_audio_formats="$audiod_audio_formats opus"
+       }
+       NEED_FLAC_OBJECTS && {
+               audiod_errlist_objs="$audiod_errlist_objs flacdec_filter"
+               audiod_audio_formats="$audiod_audio_formats flac"
+       }
+       if test $HAVE_FAAD = yes; then
+               audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
+               audiod_audio_formats="$audiod_audio_formats aac"
+       fi
+       if test $HAVE_MAD = yes; then
+               audiod_audio_formats="$audiod_audio_formats mp3"
+               audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter"
+               audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
+       fi
+       if test $HAVE_OSS = yes; then
+               audiod_errlist_objs="$audiod_errlist_objs oss_write"
+               audiod_cmdline_objs="$audiod_cmdline_objs oss_write"
+       fi
+       if test $HAVE_ALSA = yes; then
+               audiod_errlist_objs="$audiod_errlist_objs alsa_write"
+               audiod_cmdline_objs="$audiod_cmdline_objs alsa_write"
+       fi
+       NEED_AO_OBJECTS && {
+               audiod_errlist_objs="$audiod_errlist_objs ao_write"
+               audiod_cmdline_objs="$audiod_cmdline_objs ao_write"
+       }
+       if test $HAVE_SAMPLERATE = yes; then
+               audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav"
+               audiod_cmdline_objs="$audiod_cmdline_objs resample_filter"
+       fi
+       audiod_objs="add_cmdline($audiod_cmdline_objs) $audiod_errlist_objs"
+       AC_SUBST(audiod_objs, add_dot_o($audiod_objs))
+       AC_DEFINE_UNQUOTED(INIT_AUDIOD_ERRLISTS, objlist_to_errlist($audiod_errlist_objs),
+               errors used by para_audiod)
+
+       enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
+       AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMATS_ENUM, $enum NUM_AUDIO_FORMATS,
+               enum of audio formats supported by audiod)
+       names="$(for i in $audiod_audio_formats; do printf \"$i\",' ' ; done)"
+       AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMAT_ARRAY, $names, array of audio formats supported by audiod)
 else
-       have_alsa="yes"
-fi
-if test "$have_alsa" = "yes"; then
-       AC_CHECK_HEADERS([alsa/asoundlib.h], [], [
-               have_alsa="no"
-               AC_MSG_WARN([no alsa/asoundlib $msg])
-       ])
-fi
-
-if test "$have_alsa" = "yes"; then
-       AC_CHECK_LIB([asound], [snd_pcm_open], [], [
-               have_alsa="no"
-               AC_MSG_WARN([no libasound $msg])
-       ])
-fi
-
-if test "$have_alsa" = "yes"; then
-       audiod_errlist_objs="$audiod_errlist_objs alsa_write"
-       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(alsa_write)"
-       audiod_ldflags="$audiod_ldflags -lasound"
-       play_errlist_objs="$play_errlist_objs alsa_write"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(alsa_write)"
-       play_ldflags="$play_ldflags -lasound"
-
-       write_errlist_objs="$write_errlist_objs alsa_write"
-       write_cmdline_objs="$write_cmdline_objs add_cmdline(alsa_write)"
-       write_ldflags="$write_ldflags -lasound"
-       fade_errlist_objs="$fade_errlist_objs alsa_mix"
-       fade_ldflags="$fade_ldflags -lasound"
-       all_errlist_objs="$all_errlist_objs alsa_write alsa_mix"
-
-       writers="$writers alsa"
-       default_writer="ALSA_WRITE"
-       mixers="${mixers}alsa "
-       default_mixer="ALSA_MIX"
+       build_audiod="no"
 fi
-
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
 ########################################################################### fade
-if test -n "$mixers"; then
-       extras="$extras fade"
+if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
+       build_fade="yes"
        executables="$executables fade"
-       all_errlist_objs="$all_errlist_objs fade"
-       fade_errlist_objs="$fade_errlist_objs fade exec string fd version ggo"
-       fade_cmdline_objs="add_cmdline(fade)"
-       fade_objs="$fade_cmdline_objs $fade_errlist_objs"
+       fade_cmdline_objs="fade"
+       fade_errlist_objs="fade exec string fd version ggo"
+       if test $HAVE_OSS = yes; then
+               fade_errlist_objs="$fade_errlist_objs oss_mix"
+               mixers="${mixers}oss "
+               default_mixer="OSS_MIX"
+       fi
+       if test $HAVE_ALSA = yes; then
+               fade_errlist_objs="$fade_errlist_objs alsa_mix"
+               mixers="${mixers}alsa "
+               default_mixer="ALSA_MIX"
+       fi
+       fade_objs="add_cmdline($fade_cmdline_objs) $fade_errlist_objs"
        AC_SUBST(fade_objs, add_dot_o($fade_objs))
-       AC_SUBST(fade_ldflags, $fade_ldflags)
        AC_DEFINE_UNQUOTED(INIT_FADE_ERRLISTS,
                objlist_to_errlist($fade_errlist_objs),
                errors used by para_fade)
@@ -1104,200 +665,440 @@ if test -n "$mixers"; then
                init functions of the supported mixers)
        array="$(for i in $mixers; do printf '{.init = '$i'_mix_init},'; done)"
        AC_DEFINE_UNQUOTED(MIXER_ARRAY, $array, array of supported mixers)
-       mixer_summary="supported mixers:: $mixers, default: $default_mixer"
 else
+       build_fade="no"
        AC_MSG_WARN([no mixer support])
-       mixer_summary="para_fade: no"
 fi
-########################################################################### libao
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_ao="yes"
-AC_ARG_WITH(ao_headers, [AS_HELP_STRING(--with-ao-headers=dir,
-       [look for ao/ao.h also in dir])])
-if test -n "$with_ao_headers"; then
-       ao_cppflags="-I$with_ao_headers"
-       CPPFLAGS="$CPPFLAGS $ao_cppflags"
+########################################################################### gui
+if test $HAVE_CURSES = yes; then
+       build_gui="yes"
+       executables="$executables gui"
+       gui_cmdline_objs="gui"
+       gui_errlist_objs="
+               exec
+               signal
+               string
+               stat
+               ringbuffer
+               fd
+               gui
+               gui_theme
+               time
+               sched
+               version
+               ggo
+       "
+       gui_objs="add_cmdline($gui_cmdline_objs) $gui_errlist_objs"
+       AC_SUBST(gui_objs, add_dot_o($gui_objs))
+       AC_DEFINE_UNQUOTED(INIT_GUI_ERRLISTS,
+               objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
+else
+       build_gui="no"
+       AC_MSG_WARN([no curses lib, cannot build para_gui])
 fi
-AC_ARG_WITH(ao_libs, [AS_HELP_STRING(--with-ao-libs=dir,
-       [look for libao also in dir])])
-if test -n "$with_ao_libs"; then
-       ao_libs="-L$with_ao_libs"
-       LDFLAGS="$LDFLAGS $ao_libs"
+######################################################################## filter
+filters="
+       compress
+       wav
+       amp
+       fecdec
+       wmadec
+       prebuffer
+       sync
+"
+filter_errlist_objs="
+       filter_common
+       wav_filter
+       compress_filter
+       filter
+       string
+       stdin
+       stdout
+       sched
+       fd
+       amp_filter
+       ggo
+       fecdec_filter
+       fec
+       version
+       prebuffer_filter
+       time
+       bitstream
+       imdct
+       wma_common
+       wmadec_filter
+       buffer_tree
+       net
+       sync_filter
+"
+filter_cmdline_objs="
+       filter
+       compress_filter
+       amp_filter
+       prebuffer_filter
+       sync_filter
+"
+NEED_VORBIS_OBJECTS && {
+       filters="$filters oggdec"
+       filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+}
+NEED_SPEEX_OBJECTS && {
+       filters="$filters spxdec"
+       filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
+}
+NEED_OPUS_OBJECTS && {
+       filters="$filters opusdec"
+       filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+}
+NEED_FLAC_OBJECTS && {
+       filter_errlist_objs="$filter_errlist_objs flacdec_filter"
+       filters="$filters flacdec"
+}
+if test $HAVE_FAAD = yes; then
+       filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
+       filters="$filters aacdec"
 fi
-msg="no libao support for para_audiod/para_write"
-AC_CHECK_HEADERS([ao/ao.h], [
-       ], [
-       have_ao="no"
-       AC_MSG_WARN([ao.h not found, $msg])
-])
-if test "$have_ao" = "yes"; then
-       AC_CHECK_LIB([ao], [ao_initialize], [], [
-               have_ao="no"
-               AC_MSG_WARN([ao lib not found or not working, $msg])
-       ])
+if test $HAVE_MAD = yes; then
+       filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter"
+       filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
+       filters="$filters mp3dec"
 fi
-if test "$have_ao" = "yes"; then
-       AC_CHECK_HEADERS([pthread.h], [
-               ], [
-               have_ao="no"
-               AC_MSG_WARN([pthread.h not found, $msg])
-       ])
+if test $HAVE_SAMPLERATE = yes; then
+       filter_errlist_objs="$filter_errlist_objs resample_filter check_wav"
+       filter_cmdline_objs="$filter_cmdline_objs resample_filter"
+       filters="$filters resample"
+fi
+filters="$(echo $filters)"
+AC_SUBST(filters)
+filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs"
+
+AC_SUBST(filter_objs, add_dot_o($filter_objs))
+AC_DEFINE_UNQUOTED(INIT_FILTER_ERRLISTS,
+       objlist_to_errlist($filter_errlist_objs), errors used by para_filter)
+
+enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
+       enum of supported filters)
+inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
+AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
+array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
+########################################################################## recv
+recv_cmdline_objs="
+       recv
+       http_recv
+       dccp_recv
+       udp_recv
+       afh_recv
+"
+
+recv_errlist_objs="
+       http_recv
+       recv_common
+       recv
+       time
+       string
+       net
+       dccp_recv
+       fd
+       sched
+       stdout
+       ggo
+       udp_recv
+       buffer_tree
+       afh_recv
+       afh_common
+       wma_afh
+       wma_common
+       mp3_afh
+       version
+"
+NEED_OGG_OBJECTS && recv_errlist_objs="$recv_errlist_objs ogg_afh_common"
+NEED_VORBIS_OBJECTS && recv_errlist_objs="$recv_errlist_objs ogg_afh"
+NEED_SPEEX_OBJECTS && recv_errlist_objs="$recv_errlist_objs spx_afh spx_common"
+NEED_OPUS_OBJECTS && recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
+NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
+
+if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then
+       recv_errlist_objs="$recv_errlist_objs aac_afh aac_common"
 fi
-if test "$have_ao" = "yes"; then
-       AC_CHECK_LIB([pthread], [pthread_create], [], [
-               have_ao="no"
-               AC_MSG_WARN([pthread lib not found or not working, $msg])
-       ])
+recv_objs="add_cmdline($recv_cmdline_objs) $recv_errlist_objs"
+AC_SUBST(receivers, "http dccp udp afh")
+AC_SUBST(recv_objs, add_dot_o($recv_objs))
+AC_DEFINE_UNQUOTED(INIT_RECV_ERRLISTS, objlist_to_errlist($recv_errlist_objs),
+       errors used by para_recv)
+########################################################################### afh
+audio_format_handlers="mp3 wma"
+afh_cmdline_objs="afh"
+afh_errlist_objs="
+       afh
+       string
+       fd
+       mp3_afh
+       afh_common
+       time
+       wma_afh
+       wma_common
+       version
+       ggo
+"
+NEED_OGG_OBJECTS && afh_errlist_objs="$afh_errlist_objs ogg_afh_common"
+NEED_VORBIS_OBJECTS && {
+       afh_errlist_objs="$afh_errlist_objs ogg_afh"
+       audio_format_handlers="$audio_format_handlers ogg"
+}
+NEED_SPEEX_OBJECTS && {
+       afh_errlist_objs="$afh_errlist_objs spx_afh spx_common"
+       audio_format_handlers="$audio_format_handlers spx"
+}
+NEED_OPUS_OBJECTS && {
+       afh_errlist_objs="$afh_errlist_objs opus_afh opus_common"
+       audio_format_handlers="$audio_format_handlers opus"
+}
+NEED_FLAC_OBJECTS && {
+       afh_errlist_objs="$afh_errlist_objs flac_afh"
+       audio_format_handlers="$audio_format_handlers flac"
+}
+if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then
+       afh_errlist_objs="$afh_errlist_objs aac_afh aac_common"
+       audio_format_handlers="$audio_format_handlers aac"
 fi
-if test "$have_ao" = "yes"; then
-       all_errlist_objs="$all_errlist_objs ao_write"
-       audiod_errlist_objs="$audiod_errlist_objs ao_write"
-       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(ao_write)"
-       audiod_ldflags="$audiod_ldflags -lao -lpthread"
 
+afh_objs="add_cmdline($afh_cmdline_objs) $afh_errlist_objs"
+
+AC_SUBST(afh_objs, add_dot_o($afh_objs))
+AC_DEFINE_UNQUOTED(INIT_AFH_ERRLISTS,
+       objlist_to_errlist($afh_errlist_objs), errors used by para_afh)
+########################################################################## play
+play_errlist_objs="
+       play
+       fd
+       sched
+       ggo
+       buffer_tree
+       time
+       string
+       net
+       afh_recv
+       afh_common
+       wma_afh
+       wma_common
+       mp3_afh
+       recv_common
+       udp_recv
+       http_recv
+       dccp_recv
+       filter_common
+       fec
+       bitstream
+       imdct
+       wav_filter
+       compress_filter
+       amp_filter
+       prebuffer_filter
+       fecdec_filter
+       wmadec_filter
+       write_common
+       file_write
+       version
+       sync_filter
+"
+play_cmdline_objs="
+       http_recv
+       dccp_recv
+       udp_recv
+       afh_recv
+       compress_filter
+       amp_filter
+       prebuffer_filter
+       file_write
+       play
+       sync_filter
+"
+if test "$have_core_audio" = "yes"; then
+       play_errlist_objs="$play_errlist_objs osx_write ipc"
+       play_cmdline_objs="$play_cmdline_objs osx_write"
+fi
+NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common"
+NEED_VORBIS_OBJECTS && {
+       play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
+}
+NEED_SPEEX_OBJECTS && {
+       play_errlist_objs="$play_errlist_objs spxdec_filter spx_afh spx_common"
+}
+NEED_OPUS_OBJECTS &&
+       play_errlist_objs="$play_errlist_objs
+               opusdec_filter
+               opus_afh
+               opus_common
+       "
+NEED_FLAC_OBJECTS && {
+       play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
+}
+if test $HAVE_FAAD = yes; then
+       play_errlist_objs="$play_errlist_objs aacdec_filter"
+fi
+if test $HAVE_MP4V2 = yes; then
+       play_errlist_objs="$play_errlist_objs aac_afh"
+fi
+if test $HAVE_MP4V2 = yes || test $HAVE_FAAD = yes; then
+       play_errlist_objs="$play_errlist_objs aac_common"
+fi
+if test $HAVE_MAD = yes; then
+       play_cmdline_objs="$play_cmdline_objs mp3dec_filter"
+       play_errlist_objs="$play_errlist_objs mp3dec_filter"
+fi
+if test $HAVE_OSS = yes; then
+       play_errlist_objs="$play_errlist_objs oss_write"
+       play_cmdline_objs="$play_cmdline_objs oss_write"
+fi
+if test $HAVE_ALSA = yes; then
+       play_errlist_objs="$play_errlist_objs alsa_write"
+       play_cmdline_objs="$play_cmdline_objs alsa_write"
+fi
+NEED_AO_OBJECTS && {
        play_errlist_objs="$play_errlist_objs ao_write"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(ao_write)"
-       play_ldflags="$play_ldflags -lao -lpthread"
+       play_cmdline_objs="$play_cmdline_objs ao_write"
+}
+if test $HAVE_READLINE = yes; then
+       play_errlist_objs="$play_errlist_objs interactive"
+fi
+if test $HAVE_SAMPLERATE = yes; then
+       play_errlist_objs="$play_errlist_objs resample_filter check_wav"
+       play_cmdline_objs="$play_cmdline_objs resample_filter"
+fi
+
+play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs"
+AC_SUBST(play_objs, add_dot_o($play_objs))
+AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS,
+       objlist_to_errlist($play_errlist_objs), errors used by para_play)
+######################################################################### write
+write_cmdline_objs="
+       write
+       file_write
+"
+write_errlist_objs="
+       write
+       write_common
+       file_write
+       time
+       fd
+       string
+       sched
+       stdin
+       buffer_tree
+       ggo
+       check_wav
+       version
+"
+writers="file"
+default_writer="FILE_WRITE"
 
+if test "$have_core_audio" = "yes"; then
+       write_errlist_objs="$write_errlist_objs osx_write ipc"
+       write_cmdline_objs="$write_cmdline_objs osx_write"
+       writers="$writers osx"
+       default_writer="OSX_WRITE"
+fi
+NEED_AO_OBJECTS && {
        write_errlist_objs="$write_errlist_objs ao_write"
-       write_cmdline_objs="$write_cmdline_objs add_cmdline(ao_write)"
-       write_ldflags="$write_ldflags $ao_libs -lao -lpthread"
+       write_cmdline_objs="$write_cmdline_objs ao_write"
        writers="$writers ao"
-       AC_SUBST(ao_cppflags)
+       default_writer="AO_WRITE"
+}
+if test $HAVE_OSS = yes; then
+       write_errlist_objs="$write_errlist_objs oss_write"
+       write_cmdline_objs="$write_cmdline_objs oss_write"
+       writers="$writers oss"
+       default_writer="OSS_WRITE"
 fi
-
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-############################################################# readline
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_readline="yes"
-AC_ARG_WITH(readline_headers, [AS_HELP_STRING(--with-readline-headers=dir,
-       [look for libreadline header files also in dir])])
-if test -n "$with_readline_headers"; then
-       readline_cppflags="-I$with_readline_headers"
-       CPPFLAGS="$CPPFLAGS $readline_cppflags"
+if test $HAVE_ALSA = yes; then
+       write_errlist_objs="$write_errlist_objs alsa_write"
+       write_cmdline_objs="$write_cmdline_objs alsa_write"
+       writers="$writers alsa"
+       default_writer="ALSA_WRITE"
 fi
-
-AC_ARG_WITH(readline_libs, [AS_HELP_STRING(--with-readline-libs=dir,
-       [look for readline library also in dir])])
-if test -n "$with_readline_libs"; then
-       readline_libs="-L$with_readline_libs"
-       LDFLAGS="$LDFLAGS $readline_libs"
+AC_SUBST(writers)
+write_objs="add_cmdline($write_cmdline_objs) $write_errlist_objs"
+AC_SUBST(write_objs, add_dot_o($write_objs))
+AC_DEFINE_UNQUOTED(INIT_WRITE_ERRLISTS,
+       objlist_to_errlist($write_errlist_objs), errors used by para_write)
+enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
+AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
+       enum of supported writers)
+AC_DEFINE_UNQUOTED(DEFAULT_WRITER, $default_writer, use this writer if none was specified)
+names="$(for i in $writers; do printf \"$i\",' ' ; done)"
+AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names)
+inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer *); '; done)"
+AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
+array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
+AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
+######################################################################## audioc
+audioc_cmdline_objs="audioc"
+audioc_errlist_objs="
+       audioc
+       string
+       net
+       fd
+       version
+       ggo
+"
+if test $HAVE_READLINE = yes; then
+       audioc_errlist_objs="$audioc_errlist_objs
+               buffer_tree
+               interactive
+               sched
+               time
+       "
 fi
-msg="no interactive cli support"
-AC_CHECK_HEADERS([readline/readline.h], [
-       ], [
-       have_readline="no"
-       AC_MSG_WARN([readline/readline.h not found, $msg])
-])
+audioc_objs="add_cmdline($audioc_cmdline_objs) $audioc_errlist_objs"
+AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
+AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS,
+       objlist_to_errlist($audioc_errlist_objs), errors used by para_audioc)
+############################################################# error2.h
+# these are always built
+all_errlist_objs="
+       $recv_errlist_objs
+       $filter_errlist_objs
+       $audioc_errlist_objs
+       $write_errlist_objs
+       $afh_errlist_objs
+       $play_errlist_objs
+"
 
-if test "$have_curses" != "yes"; then
-       have_readline="no"
-       AC_MSG_WARN([interactive cli support depends on curses,])
-       AC_MSG_WARN([but no curses lib was detected, $msg])
+# optional executables
+if test "$build_server" = "yes"; then
+       all_errlist_objs="$all_errlist_objs $server_errlist_objs"
 fi
-
-if test "$have_readline" = "yes"; then
-       readline_libs="$readline_libs -lreadline"
-       AC_SEARCH_LIBS([rl_free_keymap], [readline], [], [have_readline="no"])
-       if test "$have_readline" = "no"; then # try with -lcurses
-                # clear cache
-               AC_MSG_NOTICE([trying again with -lcurses])
-                unset ac_cv_search_rl_free_keymap 2> /dev/null
-               AC_SEARCH_LIBS([rl_free_keymap], [readline], [
-                       have_readline=yes
-                       readline_libs="$readline_libs -lcurses"
-               ], [], [-lcurses])
-       fi
-       if test "$have_readline" = "no"; then # try with -ltermcap
-                # clear cache
-               AC_MSG_NOTICE([trying again with -ltermcap])
-                unset ac_cv_search_rl_free_keymap 2> /dev/null
-               AC_SEARCH_LIBS([rl_free_keymap], [readline], [
-                       have_readline=yes
-                       readline_libs="$readline_libs -ltermcap"
-               ], [], [-ltermcap])
-       fi
+if test "$build_gui" = "yes"; then
+       all_errlist_objs="$all_errlist_objs $gui_errlist_objs"
 fi
-
-if test "$have_readline" = "yes"; then
-       all_errlist_objs="$all_errlist_objs interactive"
-       client_errlist_objs="$client_errlist_objs interactive"
-       client_ldflags="$client_ldflags $readline_libs"
-       audioc_errlist_objs="$audioc_errlist_objs buffer_tree interactive sched time"
-       audioc_ldflags="$audioc_ldflags $readline_libs"
-       play_errlist_objs="$play_errlist_objs interactive"
-       play_ldflags="$play_ldflags $readline_libs"
-       AC_SUBST(readline_cppflags)
-       AC_DEFINE(HAVE_READLINE, 1, define to 1 to turn on readline support)
-else
-       AC_MSG_WARN([libreadline not found or unusable])
+if test "$build_fade" = "yes"; then
+       all_errlist_objs="$all_errlist_objs $fade_errlist_objs"
 fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-############################################################# libsamplerate
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-
-have_samplerate="yes"
-AC_ARG_WITH(samplerate_headers, [AS_HELP_STRING(--with-samplerate-headers=dir,
-       [look for samplerate headers also in dir])])
-if test -n "$with_samplerate_headers"; then
-       samplerate_cppflags="-I$with_samplerate_headers"
-       CPPFLAGS="$CPPFLAGS $samplerate_cppflags"
+if test "$build_client" = "yes"; then
+       all_errlist_objs="$all_errlist_objs $client_errlist_objs"
 fi
-AC_ARG_WITH(samplerate_libs, [AS_HELP_STRING(--with-samplerate-libs=dir,
-       [look for samplerate libs also in dir])])
-if test -n "$with_samplerate_libs"; then
-       samplerate_libs="-L$with_samplerate_libs"
-       LDFLAGS="$LDFLAGS $samplerate_libs"
+if test "$build_audiod" = "yes"; then
+       all_errlist_objs="$all_errlist_objs $audiod_errlist_objs"
 fi
 
-AC_CHECK_HEADER(samplerate.h, [], have_samplerate=no)
-AC_CHECK_LIB([samplerate], [src_process], [], have_samplerate=no, [])
+all_errlist_objs="$(echo $all_errlist_objs | tr ' ' '\n' | sort | uniq)"
 
-if test "$have_samplerate" = "yes"; then
-       all_errlist_objs="$all_errlist_objs resample_filter"
-       filter_errlist_objs="$filter_errlist_objs resample_filter check_wav"
-       filter_cmdline_objs="$filter_cmdline_objs add_cmdline(resample_filter)"
-       audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav"
-       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(resample_filter)"
-       play_errlist_objs="$play_errlist_objs resample_filter check_wav"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(resample_filter)"
-       filter_ldflags="$filter_ldflags $samplerate_libs -lsamplerate"
-       audiod_ldflags="$audiod_ldflags $samplerate_libs -lsamplerate"
-       play_ldflags="$play_ldflags $samplerate_libs -lsamplerate"
-       filters="$filters resample"
-       AC_SUBST(samplerate_cppflags)
-else
-       AC_MSG_WARN([no resample support in para_audiod/para_filter])
-fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-############################################################# error2.h
-AC_MSG_NOTICE(creating error2.h)
+object_executable_matrix=
 for i in $executables; do
-       echo "$i: "
-       eval echo \$${i}_errlist_objs
-done | ./error2.pl > error2.h
-for obj in $all_errlist_objs; do
-       SS="$SS SS_$(echo $obj | tr 'a-z' 'A-Z'),"
+       eval objs=\$${i}_errlist_objs
+       object_executable_matrix="$object_executable_matrix $i: $objs"
 done
+# use echo to replace newlines by space
+AC_SUBST(object_executable_matrix, $(echo $object_executable_matrix))
+
+SS=$(for obj in $all_errlist_objs; do
+       printf '%s' " SS_$obj,"; done | tr 'a-z' 'A-Z')
 AC_DEFINE_UNQUOTED(DEFINE_ERRLIST_OBJECT_ENUM,
        [enum {$SS NUM_SS}],
        [list of all objects that use the paraslash error facility]
 )
-
 ################################################################## status items
 
 status_items="basename status num_played mtime bitrate frequency file_size
@@ -1325,104 +1126,27 @@ AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, [$result],
 AC_DEFINE_UNQUOTED(AUDIO_FORMAT_HANDLERS, "$audio_format_handlers",
        [formats supported by para_server and para_afh])
 
-AC_SUBST(executables, add_para($executables))
-
-recv_objs="$recv_cmdline_objs $recv_errlist_objs"
-filter_objs="$filter_cmdline_objs $filter_errlist_objs"
-audiod_objs="$audiod_cmdline_objs $audiod_errlist_objs"
-server_objs="$server_cmdline_objs $server_errlist_objs"
-write_objs="$write_cmdline_objs $write_errlist_objs"
-client_objs="$client_cmdline_objs $client_errlist_objs"
-audioc_objs="$audioc_cmdline_objs $audioc_errlist_objs"
-afh_objs="$afh_cmdline_objs $afh_errlist_objs"
-play_objs="$play_cmdline_objs $play_errlist_objs"
-
-
-AC_SUBST(recv_objs, add_dot_o($recv_objs))
-AC_SUBST(recv_ldflags, $recv_ldflags)
-AC_DEFINE_UNQUOTED(INIT_RECV_ERRLISTS, objlist_to_errlist($recv_errlist_objs),
-       errors used by para_recv)
-
-AC_SUBST(filter_objs, add_dot_o($filter_objs))
-AC_SUBST(filter_ldflags, $filter_ldflags)
-AC_DEFINE_UNQUOTED(INIT_FILTER_ERRLISTS,
-       objlist_to_errlist($filter_errlist_objs), errors used by para_filter)
-
-AC_SUBST(audiod_objs, add_dot_o($audiod_objs))
-AC_SUBST(audiod_ldflags, $audiod_ldflags)
-AC_DEFINE_UNQUOTED(INIT_AUDIOD_ERRLISTS, objlist_to_errlist($audiod_errlist_objs),
-       errors used by para_audiod)
-
-AC_SUBST(server_objs, add_dot_o($server_objs))
-AC_SUBST(server_ldflags, $server_ldflags)
-AC_DEFINE_UNQUOTED(INIT_SERVER_ERRLISTS,
-       objlist_to_errlist($server_errlist_objs), errors used by para_server)
-
-AC_SUBST(afh_objs, add_dot_o($afh_objs))
-AC_SUBST(afh_ldflags, $afh_ldflags)
-AC_DEFINE_UNQUOTED(INIT_AFH_ERRLISTS,
-       objlist_to_errlist($afh_errlist_objs), errors used by para_afh)
-
-AC_SUBST(write_objs, add_dot_o($write_objs))
-AC_SUBST(write_ldflags, $write_ldflags)
-AC_DEFINE_UNQUOTED(INIT_WRITE_ERRLISTS,
-       objlist_to_errlist($write_errlist_objs), errors used by para_write)
-
-AC_SUBST(client_objs, add_dot_o($client_objs))
-AC_SUBST(client_ldflags, $client_ldflags)
-AC_DEFINE_UNQUOTED(INIT_CLIENT_ERRLISTS,
-       objlist_to_errlist($client_errlist_objs), errors used by para_client)
-
-AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
-AC_SUBST(audioc_ldflags, $audioc_ldflags)
-AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS,
-       objlist_to_errlist($audioc_errlist_objs), errors used by para_audioc)
-
-AC_SUBST(gui_objs, add_dot_o($gui_objs))
-AC_SUBST(gui_ldflags, $gui_ldflags)
-AC_DEFINE_UNQUOTED(INIT_GUI_ERRLISTS,
-       objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
-
-AC_SUBST(play_objs, add_dot_o($play_objs))
-AC_SUBST(play_ldflags, $play_ldflags)
-AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS,
-       objlist_to_errlist($play_errlist_objs), errors used by para_play)
-
-enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
-       enum of supported filters)
-inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
-array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
-
-enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
-       enum of supported writers)
-AC_DEFINE_UNQUOTED(DEFAULT_WRITER, $default_writer, use this writer if none was specified)
-names="$(for i in $writers; do printf \"$i\",' ' ; done)"
-AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names)
-inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer *); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
-array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
-
-enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMATS_ENUM, $enum NUM_AUDIO_FORMATS,
-       enum of audio formats supported by audiod)
-names="$(for i in $audiod_audio_formats; do printf \"$i\",' ' ; done)"
-AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMAT_ARRAY, $names, array of audio formats supported by audiod)
+AC_SUBST(executables)
 
 AC_OUTPUT
 AC_MSG_NOTICE([
 paraslash configuration:
 ~~~~~~~~~~~~~~~~~~~~~~~~
+crypto lib: ${CRYPTOLIB:-[none]}
 unix socket credentials: $have_ucred
-readline (interactive CLIs): $have_readline
-audio formats handlers: $audio_format_handlers
-id3 version2 support: $have_libid3tag
-filters: $filters
+readline (interactive CLIs): $HAVE_READLINE
+id3 version 2 support: $HAVE_ID3TAG
+faad: $HAVE_FAAD
+mp4v2: $HAVE_MP4V2
+
+audio format handlers: $audio_format_handlers
+filters: $(echo $filters)
 writers: $writers
-optional executables: $extras
-$mixer_summary
+
+para_fade: $build_fade
+para_server: $build_server
+para_gui: $build_gui
+para_fade: $build_fade
+para_client: $build_client
+para_audiod: $build_audiod
 ])
diff --git a/convert_0.3-0.4.sh b/convert_0.3-0.4.sh
deleted file mode 100755 (executable)
index aa0b239..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/env bash
-
-#-------------------------------------------------------------------------------
-## Script to convert the database of paraslash 0.3.5 to version 0.4.x.
-##
-## Assumptions:
-##     - para_server 0.3.5 is running
-##     - "para_client check" reports no errors
-##     - para_server 0.4.x is running, listens on another port and uses a
-##       different afs database and a different afs socket
-##     - The database of paraslash 0.4.x has been initialized (i.e.
-##       para_client init has successfully been executed)
-##     - All audio files in the 0.3.x database have already been added to
-##       the 0.4.x database (execute para_client add /my/audio/file/dir to
-##       do that)
-##
-#-------------------------------------------------------------------------------
-
-# Call this script without arguments to see usage info
-
-# How to connect to para_server 0.3.x.
-client03=/usr/local/bin/para_client
-port03=2991
-host03=localhost
-database03=$HOME/.paraslash/afs_database
-
-# How to connect to para_server 0.4.x.
-client04=$(pwd)/para_client
-port04=2990
-host04=localhost
-database04=$HOME/.paraslash/afs_database-0.4
-
-# Any character that does not occur in any filename of an audio file
-sep='|'
-
-
-client03_cmd="$client03 -p $port03 -i $host03"
-client04_cmd="$client04 -p $port04 -i $host04"
-
-exec_client03_cmd()
-{
-       result="$($client03_cmd -- "$@")"
-}
-
-exec_client04_cmd()
-{
-       result="$($client04_cmd -- "$@")"
-}
-
-convert_attribute_table()
-{
-       local atts
-
-       echo "converting attribute table"
-       exec_client03_cmd lsatt
-       atts="$result"
-       exec_client04_cmd addatt $atts
-}
-
-convert_attributes()
-{
-       local OIFS="$IFS" a p att atts
-
-       printf "converting attributes: "
-       $client03_cmd -- ls -p -lv \
-               | grep '^path:\|^attributes_txt:' \
-               | sed -e "/^path:/N;s/\n/$sep/1" \
-               | {
-                       IFS="$sep"
-                       while read p a; do
-                               p=${p#path: }
-                               a=${a#attributes_txt:}
-                               IFS=" "
-                               atts=
-                               for att in $a; do
-                                       atts="$atts $att+"
-                               done
-                               IFS="$OIFS"
-                               [[ -n "$atts" ]] && $client04_cmd -- setatt $atts "$p"
-                               IFS="$sep"
-                               printf "."
-                       done
-                       echo done
-               }
-               IFS="$OIFS"
-}
-
-convert_lna()
-{
-       local OIFS="$IFS" p l n a
-
-       printf "converting last_played, num_played, amplification values: "
-       $client03_cmd -- ls -p -d -lv \
-               | grep '^path:\|^last_played: \|^num_played: \|^amplification: ' \
-               | sed -e "/^path:/N;N;N;s/\n/$sep/g" \
-               | {
-                       IFS="$sep"
-                       while read p l n a; do
-                               #echo "p: $p, l:$l, n:$n a:$a"
-                               p=${p#path: }
-                               l=${l#last_played: }
-                               n=${n#num_played: }
-                               a=${a#amplification: }
-                               IFS="$OIFS"
-                               $client04_cmd -- touch "-l$l" "-n$n" "-a$a" "$p"
-                               IFS="$sep"
-                               printf "."
-                       done
-                       echo done
-               }
-               IFS="$OIFS"
-}
-
-convert_blobs()
-{
-       local blob name
-
-       for blob in img lyr mood pl; do
-               printf "converting $blob table: "
-               exec_client03_cmd ls$blob
-               echo "$result" | while read name; do
-                       $client03_cmd -- cat$blob "$name" | $client04_cmd -- add$blob "$name"
-                       printf "."
-               done
-               echo done
-       done
-}
-
-convert_ids()
-{
-       local OIFS="$IFS" p i y iopts yopts
-
-       printf "converting image and lyrics ids: "
-       $client03_cmd -- ls -p -lv \
-               | grep '^path:\|^image_name: \|^lyrics_name: ' \
-               | sed -e "/^path:/N;N;s/\n/$sep/g" \
-               | {
-                       IFS="$sep"
-                       while read p i l; do
-                               IFS="$OIFS"
-                               iopts=
-                               yopts=
-                               p=${p#path: }
-                               i=${i#image_name: }
-                               l=${l#lyrics_name: }
-                               if [[ "$i" != '(none)' ]]; then
-                                       exec_client04_cmd lsimg -l "$i"
-                                       iopts="-i${result%%     *}"
-                               fi
-                               if [[ "$l" != '(none)' ]]; then
-                                       exec_client04_cmd lslyr -l "$l"
-                                       yopts="-y${result%%     *}"
-                               fi
-                               if [[ -n "$iopts" && -n "$yopts" ]]; then
-                                       $client04_cmd -- touch "$iopts" "$yopts" "$p"
-                               elif [[ -n "$iopts" ]]; then
-                                       $client04_cmd -- touch "$iopts" "$p"
-                               elif [[ -n "$yopts" ]]; then
-                                       $client04_cmd -- touch "$yopts" "$p"
-                               fi
-                               printf "."
-                               IFS="$sep"
-                       done
-                       echo done
-               }
-               IFS="$OIFS"
-}
-
-
-usage()
-{
-       grep '^##' $0 | sed -e 's/^## *//'
-       echo '
-Usage: $0 command
-
-command is one of the following:
-
-       attribute_table: create attributes
-       attributes: convert attributes for each audio file
-       lna: convert the last_played/num_played/amplification values
-       blobs: convert the image/lyrics/moods/playlists tables
-       ids: convert image and the lyrics id of each audio file.
-       all: Do all of the above.
-
-Edit the top of the script to customize some options.
-'
-}
-
-if test $# -ne 1; then
-       usage
-       exit 1
-fi
-
-case "$1" in
-attribute_table)
-       convert_attribute_table
-       ;;
-attributes)
-       convert_attributes
-       ;;
-lna)
-       convert_lna
-       ;;
-blobs)
-       convert_blobs
-       ;;
-ids)
-       convert_ids
-       ;;
-all)
-       convert_attribute_table
-       convert_attributes
-       convert_lna
-       convert_blobs
-       convert_ids
-       ;;
-*)
-       usage
-       exit 1
-       ;;
-esac
diff --git a/crypt.c b/crypt.c
index 5c5333a9a15a43d2cecf5d3ed1774141c58b90cf..610d2057947dba6757526127a317f8b87bf7eebc 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -15,6 +15,7 @@
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
+#include <openssl/aes.h>
 
 #include "para.h"
 #include "error.h"
@@ -147,8 +148,7 @@ static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
        *result = rsa;
        return 1;
 fail:
-       if (rsa)
-               RSA_free(rsa);
+       RSA_free(rsa);
        return ret;
 }
 
@@ -260,14 +260,40 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        return ret < 0? -E_ENCRYPT : ret;
 }
 
+struct aes_ctr_128_context {
+       AES_KEY key;
+       unsigned char ivec[AES_CRT128_BLOCK_SIZE];
+       unsigned char ecount[AES_CRT128_BLOCK_SIZE];
+       unsigned int num;
+};
+
 struct stream_cipher {
-       RC4_KEY key;
+       bool use_aes;
+       union {
+               RC4_KEY rc4_key;
+               struct aes_ctr_128_context aes;
+       } context;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len)
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes)
 {
+       int ret;
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
-       RC4_set_key(&sc->key, len, data);
+       struct aes_ctr_128_context *aes;
+
+       sc->use_aes = use_aes;
+       if (!use_aes) {
+               RC4_set_key(&sc->context.rc4_key, len, data);
+               return sc;
+       }
+       assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+       aes = &sc->context.aes;
+       ret = AES_set_encrypt_key(data, AES_CRT128_BLOCK_SIZE * 8 /* bits */,
+               &aes->key);
+       assert(ret == 0);
+       memcpy(aes->ivec, data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
+       aes->num = 0;
        return sc;
 }
 
@@ -283,44 +309,9 @@ void sc_free(struct stream_cipher *sc)
  */
 #define RC4_ALIGN 8
 
-int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t len)
-{
-       int ret;
-       unsigned char *tmp;
-       static unsigned char remainder[RC4_ALIGN];
-       size_t l1 = ROUND_DOWN(len, RC4_ALIGN), l2 = ROUND_UP(len, RC4_ALIGN);
-
-       assert(len);
-       tmp = para_malloc(l2);
-       RC4(&scc->send->key, l1, (const unsigned char *)buf, tmp);
-       if (len > l1) {
-               memcpy(remainder, buf + l1, len - l1);
-               RC4(&scc->send->key, len - l1, remainder, tmp + l1);
-       }
-       ret = xwrite(scc->fd, (char *)tmp, len);
-       free(tmp);
-       return ret;
-}
-
-int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size)
-{
-       unsigned char *tmp = para_malloc(ROUND_UP(size, RC4_ALIGN));
-       ssize_t ret = recv(scc->fd, tmp, size, 0);
-
-       if (ret > 0)
-               RC4(&scc->recv->key, ret, tmp, (unsigned char *)buf);
-       else if (ret < 0)
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-       free(tmp);
-       return ret;
-}
-
-void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst)
 {
        size_t len = src->iov_len, l1, l2;
-       RC4_KEY *key = &sc->key;
 
        assert(len > 0);
        assert(len < ((typeof(src->iov_len))-1) / 2);
@@ -341,6 +332,28 @@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
        ((char *)dst->iov_base)[len] = '\0';
 }
 
+static void aes_ctr128_crypt(struct aes_ctr_128_context *aes, struct iovec *src,
+               struct iovec *dst)
+{
+       size_t len = src->iov_len;
+
+       *dst = (typeof(*dst)) {
+               /* Add one for the terminating zero byte. */
+               .iov_base = para_malloc(len + 1),
+               .iov_len = len
+       };
+       AES_ctr128_encrypt(src->iov_base, dst->iov_base, len,
+               &aes->key, aes->ivec, aes->ecount, &aes->num);
+       ((char *)dst->iov_base)[len] = '\0';
+}
+
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+       if (sc->use_aes)
+               return aes_ctr128_crypt(&sc->context.aes, src, dst);
+       return rc4_crypt(&sc->context.rc4_key, src, dst);
+}
+
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
        SHA_CTX c;
diff --git a/crypt.h b/crypt.h
index 77806af6983895c13f3120f4f65c1c1d46b9b11d..9be7a23e6da4d6d1796d4d98da7d8280152c6c2a 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -123,10 +123,12 @@ struct stream_cipher_context {
  *
  * \param data The key.
  * \param len The size of the key.
+ * \param use_aes True: Use the aes_ctr128 stream cipher, false: Use RC4.
  *
  * \return A new stream cipher structure.
  */
-struct stream_cipher *sc_new(const unsigned char *data, int len);
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes);
 
 /**
  * Encrypt or decrypt a buffer using a stream cipher.
@@ -165,75 +167,9 @@ _static_inline_ void sc_trafo(struct iovec *src, struct iovec *dst,
  */
 void sc_free(struct stream_cipher *sc);
 
-/**
- * Encrypt and send a buffer.
- *
- * \param scc The context.
- * \param buf The buffer to send.
- * \param len The size of \a buf in bytes.
- *
- * \return The return value of the underyling call to write_all().
- *
- * \sa \ref write_all(), RC4(3).
- */
-int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t len);
-
-/**
- * Encrypt and send a \p NULL-terminated buffer.
- *
- * \param scc The context.
- * \param buf The buffer to send.
- *
- * \return The return value of the underyling call to sc_send_bin_buffer().
- */
-int sc_send_buffer(struct stream_cipher_context *scc, char *buf);
-
-/**
- * Format, encrypt and send a buffer.
- *
- * \param scc The context.
- * \param fmt A format string.
- *
- * \return The return value of the underyling call to sc_send_buffer().
- */
-__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
-               const char *fmt, ...);
-
-/**
- * Receive a buffer and decrypt it.
- *
- * \param scc The context.
- * \param buf The buffer to write the decrypted data to.
- * \param size The size of \a buf.
- *
- * \return The number of bytes received on success, negative on errors, zero if
- * the peer has performed an orderly shutdown.
- *
- * \sa recv(2), RC4(3).
- */
-int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size);
-
-/**
- * Receive a buffer, decrypt it and write terminating NULL byte.
- *
- * \param scc The context.
- * \param buf The buffer to write the decrypted data to.
- * \param size The size of \a buf.
- *
- * Read at most \a size - 1 bytes from file descriptor given by \a scc, decrypt
- * the received data and write a NULL byte at the end of the decrypted data.
- *
- * \return The return value of the underlying call to \ref
- * sc_recv_bin_buffer().
- */
-int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size);
-
 /** Size of the hash value in bytes. */
 #define HASH_SIZE 20
 
-
 /**
  * Compute the hash of the given input data.
  *
index 481a215fb24bc4257c1faebde329c07fa1d7ff1e..06c86d74b8c452bc761dafe75ad6ee792a1f15d9 100644 (file)
@@ -1,12 +1,15 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file crypt_backend.h Non-public crypto interface. */
 
-/* This should only be incuded from files which provide crypto functions. */
+/* This should only be included from files which provide crypto functions. */
+
+/** AES block size in bytes. */
+#define AES_CRT128_BLOCK_SIZE 16
 
 size_t is_ssh_rsa_key(char *data, size_t size);
 uint32_t read_ssh_u32(const void *vp);
index cd9500ab39f3a89cd4ee70da64d0d7e72ff7c84f..022692adc52ce1cb8fe4ec925e93362e89b4b754 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -90,41 +90,31 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
 
                switch (state) {
                case 0:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex] = (pos - Base64) << 2;
-                       }
+                       if (tarindex >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] = (pos - Base64) << 2;
                        state = 1;
                        break;
                case 1:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex]   |=  (pos - Base64) >> 4;
-                               target[tarindex+1]  = ((pos - Base64) & 0x0f)
-                                                       << 4 ;
-                       }
+                       if (tarindex + 1 >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= (pos - Base64) >> 4;
+                       target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
                        tarindex++;
                        state = 2;
                        break;
                case 2:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex]   |=  (pos - Base64) >> 2;
-                               target[tarindex+1]  = ((pos - Base64) & 0x03)
-                                                       << 6;
-                       }
+                       if (tarindex + 1 >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= (pos - Base64) >> 2;
+                       target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
                        tarindex++;
                        state = 3;
                        break;
                case 3:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex] |= (pos - Base64);
-                       }
+                       if (tarindex >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= pos - Base64;
                        tarindex++;
                        state = 0;
                        break;
@@ -170,7 +160,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                         * zeros.  If we don't check them, they become a
                         * subliminal channel.
                         */
-                       if (target && target[tarindex] != 0)
+                       if (target[tarindex] != 0)
                                return -E_BASE64;
                }
        } else {
@@ -321,41 +311,3 @@ int hash_compare(unsigned char *h1, unsigned char *h2)
        }
        return 0;
 }
-
-int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size)
-{
-       int n;
-
-       assert(size);
-       n = sc_recv_bin_buffer(scc, buf, size - 1);
-       if (n >= 0)
-               buf[n] = '\0';
-       else
-               *buf = '\0';
-       return n;
-}
-
-int sc_send_buffer(struct stream_cipher_context *scc, char *buf)
-{
-       size_t len = strlen(buf);
-       int ret = sc_send_bin_buffer(scc, buf, len);
-
-       if (ret < 0 || ret == len)
-               return ret;
-       return -E_SHORT_WRITE;
-}
-
-__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
-               const char *fmt, ...)
-{
-       char *msg;
-       int ret;
-       va_list ap;
-
-       va_start(ap, fmt);
-       ret = xvasprintf(&msg, fmt, ap);
-       va_end(ap);
-       ret = sc_send_bin_buffer(scc, msg, ret);
-       free(msg);
-       return ret;
-}
index 18ad1568fbb5074bbddd60bc99835bb49967d24e..478b0f47acc94d3098cc17576599032f007b43ca 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -11,6 +11,7 @@
 #include <sys/types.h> /* getgrnam() */
 #include <grp.h>
 #include <signal.h>
+#include <sys/resource.h>
 
 #include "para.h"
 #include "daemon.h"
@@ -37,12 +38,7 @@ struct daemon {
 
 static struct daemon the_daemon, *me = &the_daemon;
 
-/**
- * Activate default log colors.
- *
- * This should be called early if color support is wanted.
- */
-void daemon_set_default_log_colors(void)
+static void daemon_set_default_log_colors(void)
 {
        int i;
        static const char *default_log_colors[NUM_LOGLEVELS] = {
@@ -58,14 +54,12 @@ void daemon_set_default_log_colors(void)
                color_parse_or_die(default_log_colors[i], me->log_colors[i]);
 }
 
-/**
+/*
  * Set the color for one loglevel.
  *
- * \param arg The loglevel/color specifier.
- *
- * \a arg must be of the form "ll:[fg [bg]] [attr]".
+ * arg must be of the form "ll:[fg [bg]] [attr]".
  */
-void daemon_set_log_color_or_die(char const *arg)
+static void daemon_set_log_color_or_die(char const *arg)
 {
        char *p = strchr(arg, ':');
        int ret, ll;
@@ -84,6 +78,45 @@ err:
        exit(EXIT_FAILURE);
 }
 
+/**
+ * Initialize color mode if necessary.
+ *
+ * \param color_arg The argument given to --color.
+ * \param color_arg_auto The value for automatic color detection.
+ * \param color_arg_no The value to disable colored log messages.
+ * \param logfile_given In auto mode colors are disabled if this value is true.
+ * \param log_color_argv Color specifiers given to --log-color.
+ * \param argc Number of color specifiers in \a log_color_argv.
+ *
+ * If \a color_arg equals \a color_arg_no, color mode is disabled, i.e., calls
+ * to \a para_log() will not produce colored output. If \a color_arg == \a
+ * color_arg_auto, the function autodetects whether to activate colors.
+ * Otherwise color mode is enabled.
+ *
+ * If color mode is to be enabled, the strings in \a log_color_argv are parsed
+ * and the color scheme is updated accordingly. For each loglevel, the default
+ * colors apply if there is no log_color_argv for this loglevel.
+ */
+void daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+               int color_arg_no, bool logfile_given, char **log_color_argv,
+               int argc)
+{
+       int i;
+
+       if (color_arg == color_arg_no)
+               return;
+       if (color_arg == color_arg_auto) {
+               if (logfile_given)
+                       return;
+               if (!isatty(STDERR_FILENO))
+                       return;
+       }
+       daemon_set_flag(DF_COLOR_LOG);
+       daemon_set_default_log_colors();
+       for (i = 0; i < argc; i++)
+               daemon_set_log_color_or_die(log_color_argv[i]);
+}
+
 /**
  * Init or change the name of the log file.
  *
@@ -122,18 +155,6 @@ void daemon_set_flag(unsigned flag)
        me->flags |= flag;
 }
 
-/**
- * Clear one of the daemon config flags.
- *
- * \param flag The flag to clear.
- *
- * \sa \ref daemon_flags.
- */
-void daemon_clear_flag(unsigned flag)
-{
-       me->flags &= ~flag;
-}
-
 static bool daemon_test_flag(unsigned flag)
 {
        return me->flags & flag;
@@ -179,7 +200,7 @@ void daemonize(bool parent_waits)
                goto err;
        if (chdir("/") < 0)
                goto err;
-       null = open("/dev/null", O_RDONLY);
+       null = open("/dev/null", O_RDWR);
        if (null < 0)
                goto err;
        if (dup2(null, STDIN_FILENO) < 0)
@@ -223,16 +244,36 @@ void daemon_open_log_or_die(void)
                        strerror(errno));
                exit(EXIT_FAILURE);
        }
-       setlinebuf(me->logfile);
+       /* equivalent to setlinebuf(), but portable */
+       setvbuf(me->logfile, NULL, _IOLBF, 0);
 }
 
 /**
  * Log the startup message containing the paraslash version.
+ *
+ * \param name The name of the executable.
+ *
+ * First the given \a name is prefixed with the string "para_". Next the git
+ * version is appended. The resulting string is logged with priority "INFO".
  */
-void log_welcome(const char *whoami)
+void daemon_log_welcome(const char *name)
 {
-       PARA_INFO_LOG("welcome to %s " PACKAGE_VERSION " ("BUILD_DATE")\n",
-               whoami);
+       PARA_INFO_LOG("welcome to para_%s-" PACKAGE_VERSION " \n", name);
+}
+
+/**
+ * Renice the calling process.
+ *
+ * \param prio The priority value to set.
+ *
+ * Errors are not considered fatal, but a warning message is logged if the
+ * underlying call to setpriority(2) fails.
+ */
+void daemon_set_priority(int prio)
+{
+       if (setpriority(PRIO_PROCESS, 0, prio) < 0)
+               PARA_WARNING_LOG("could not set priority to %d: %s\n", prio,
+                       strerror(errno));
 }
 
 /**
@@ -249,7 +290,7 @@ void log_welcome(const char *whoami)
  *
  * \sa getpwnam(3), getuid(2), setuid(2), getgrnam(2), setgid(2)
  */
-void drop_privileges_or_die(const char *username, const char *groupname)
+void daemon_drop_privileges_or_die(const char *username, const char *groupname)
 {
        struct passwd *p;
        char *tmp;
@@ -290,27 +331,20 @@ void drop_privileges_or_die(const char *username, const char *groupname)
 }
 
 /**
- * Set the server startup time.
- *
- * \param startuptime The value to store as the server start time.
+ * Set the startup time.
  *
- * This should be called once on startup with \a startuptime either NULL or a
- * pointer to a struct timeval which contains the current time. If \a
- * startuptime is NULL, the server start time is set to the current time.
+ * This should be called once on startup. It sets the start time to the
+ * current time. The stored time is used for retrieving the server uptime.
  *
- * \sa time(2), difftime(3) \ref get_server_uptime(), \ref
- * get_server_uptime_str().
+ * \sa time(2), \ref daemon_get_uptime(), \ref daemon_get_uptime_str().
  */
-void set_server_start_time(const struct timeval *startuptime)
+void daemon_set_start_time(void)
 {
-       if (startuptime)
-               me->startuptime = startuptime->tv_sec;
-       else
-               time(&me->startuptime);
+       time(&me->startuptime);
 }
 
 /**
- * Get the server uptime.
+ * Get the uptime.
  *
  * \param current_time The current time.
  *
@@ -319,9 +353,9 @@ void set_server_start_time(const struct timeval *startuptime)
  *
  * \return This returns the server uptime in seconds, i.e. the difference
  * between the current time and the value stored previously via \ref
- * set_server_start_time().
+ * daemon_set_start_time().
  */
-time_t get_server_uptime(const struct timeval *current_time)
+time_t daemon_get_uptime(const struct timeval *current_time)
 {
        time_t t;
 
@@ -334,15 +368,15 @@ time_t get_server_uptime(const struct timeval *current_time)
 /**
  * Construct a string containing the current uptime.
  *
- * \param current_time See a \ref get_server_uptime().
+ * \param current_time See a \ref daemon_get_uptime().
  *
  * \return A dynamically allocated string of the form "days:hours:minutes".
  *
  * \sa server_uptime.
  */
-__malloc char *get_server_uptime_str(const struct timeval *current_time)
+__malloc char *daemon_get_uptime_str(const struct timeval *current_time)
 {
-       long t = get_server_uptime(current_time);
+       long t = daemon_get_uptime(current_time);
        return make_message("%li:%02li:%02li", t / 86400,
                (t / 3600) % 24, (t / 60) % 60);
 }
index fd435577e6258aa24fe2fe1edc9bde2ce50c7d5f..621420a4340c75fc3eddbe37652c066c0772b8eb 100644 (file)
--- a/daemon.h
+++ b/daemon.h
@@ -4,18 +4,18 @@
 void daemonize(bool parent_waits);
 void daemon_open_log_or_die(void);
 void daemon_close_log(void);
-void log_welcome(const char *whoami);
-void drop_privileges_or_die(const char *username, const char *groupname);
-/** used for server_uptime() */
-void set_server_start_time(const struct timeval *startuptime);
-time_t get_server_uptime(const struct timeval *current_time);
-__malloc char *get_server_uptime_str(const struct timeval *current_time);
+void daemon_log_welcome(const char *whoami);
+void daemon_set_priority(int prio);
+void daemon_drop_privileges_or_die(const char *username, const char *groupname);
+void daemon_set_start_time(void);
+time_t daemon_get_uptime(const struct timeval *current_time);
+__malloc char *daemon_get_uptime_str(const struct timeval *current_time);
 void daemon_set_logfile(char *logfile_name);
 void daemon_set_flag(unsigned flag);
-void daemon_clear_flag(unsigned flag);
 void daemon_set_loglevel(char *loglevel);
-void daemon_set_default_log_colors(void);
-void daemon_set_log_color_or_die(char const *arg);
+void daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+               int color_arg_no, bool logfile_given, char **log_color_argv,
+               int argc);
 __printf_2_3 void daemon_log(int ll, const char* fmt,...);
 
 /** Daemon log configuration flags. */
index c751f2f7ce5d85464d506c8c6582556bb424aa7d..253586e1f7ee4f90867a157757a34a227458047a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  * (C) 2005 Ian McDonald <imcdnzl@gmail.com>
  */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
@@ -53,6 +58,7 @@ static int dccp_recv_open(struct receiver_node *rn)
        }
 
        fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo);
+       flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
                return fd;
@@ -113,25 +119,24 @@ static void *dccp_recv_parse_config(int argc, char **argv)
        return tmp;
 }
 
-static void dccp_recv_pre_select(struct sched *s, struct task *t)
+static void dccp_recv_pre_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
 
-       t->error = 0;
-       if (generic_recv_pre_select(s, t) <= 0)
+       if (generic_recv_pre_select(s, rn) <= 0)
                return;
        para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
 }
 
-static int dccp_recv_post_select(struct sched *s, struct task *t)
+static int dccp_recv_post_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
        struct iovec iov[2];
        int ret, iovcnt;
        size_t num_bytes;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(rn->task);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
index 3979982c725da1b6e83966b6ac6887cb2cfae2a6..92dd933347d2c48d857355da34c13673da54968b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  * (C) 2005 Ian McDonald <imcdnzl@gmail.com>
  */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
-#include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 #include "string.h"
 #include "afh.h"
-#include "afs.h"
 #include "server.h"
 #include "net.h"
 #include "list.h"
@@ -194,13 +197,13 @@ static const char *dccp_list_available_ccids(void)
        return list;
 }
 
-static char *dccp_info(void)
+static char *dccp_status(void)
 {
-       char *info = get_sender_info(dss, "dccp");
-       char *ret  = make_message("%s" "\tsupported ccids: %s\n",
-                                 info, dccp_list_available_ccids());
-       free(info);
-       return ret;
+       char *status = generic_sender_status(dss, "dccp");
+       char *result = make_message("%ssupported ccids: %s\n", status,
+               dccp_list_available_ccids());
+       free(status);
+       return result;
 }
 
 /**
@@ -215,19 +218,19 @@ void dccp_send_init(struct sender *s)
 {
        int ret, k, n;
 
-       s->info = dccp_info;
+       s->status = dccp_status;
        s->send = NULL;
        s->pre_select = dccp_pre_select;
        s->post_select = dccp_post_select;
        s->shutdown_clients = dccp_shutdown_clients;
        s->resolve_target = NULL;
        s->help = generic_sender_help;
-       s->client_cmds[SENDER_ON] = dccp_com_on;
-       s->client_cmds[SENDER_OFF] = dccp_com_off;
-       s->client_cmds[SENDER_DENY] = dccp_com_deny;
-       s->client_cmds[SENDER_ALLOW] = dccp_com_allow;
-       s->client_cmds[SENDER_ADD] = NULL;
-       s->client_cmds[SENDER_DELETE] = NULL;
+       s->client_cmds[SENDER_on] = dccp_com_on;
+       s->client_cmds[SENDER_off] = dccp_com_off;
+       s->client_cmds[SENDER_deny] = dccp_com_deny;
+       s->client_cmds[SENDER_allow] = dccp_com_allow;
+       s->client_cmds[SENDER_add] = NULL;
+       s->client_cmds[SENDER_delete] = NULL;
 
        k = conf.dccp_data_slices_per_group_arg;
        n = conf.dccp_slices_per_group_arg;
diff --git a/depend.sh b/depend.sh
deleted file mode 100755 (executable)
index 746b0d2..0000000
--- a/depend.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-# Call gcc to output a rule suitable for make describing the dependencies of
-# the given input file and parse the output to add a *.d target with the same
-# dependencies.
-
-# The first three arguments to that script are special: $1 is the
-# dependency directory and $2 is the object directory. These are used
-# to prefix the .d and .o targets respectively. $3 is the directory
-# that contains the *.cmdline.h files generated by gengetopt.
-
-# As gcc outputs the dependencies on the *.cmdline.h files either as
-# foo.cmdline.h or as $cmdline_dir/foo.cmdline.h, depending on whether the
-# latter file exists, we prefix the former with $2/
-
-dep_dir="$1"
-object_dir="$2"
-cmdline_dir="$3"
-shift
-shift
-shift
-
-LC_ALL=C gcc -MM -MG "$@" \
-       | sed -e "s@^\(.*\)\.o:@$dep_dir/\1.d $object_dir/\1.o:@" \
-       -e "s@[         ^]\([a-zA-Z0-9_]\{1,\}\.cmdline.h\)@ $cmdline_dir/\1@g"
diff --git a/error.h b/error.h
index ef54ef27707e262a09c7b638c8264cec549b2c1e..b735233339f1b64a89697e318db36436a907e091 100644 (file)
--- a/error.h
+++ b/error.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -16,11 +16,9 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define TIME_ERRORS
 #define CLOSE_ON_FORK_ERRORS
 #define DAEMON_ERRORS
-#define GUI_ERRORS
 #define GUI_THEME_ERRORS
 #define RINGBUFFER_ERRORS
 #define SCORE_ERRORS
-#define RBTREE_ERRORS
 #define RECV_ERRORS
 #define IPC_ERRORS
 #define DCCP_SEND_ERRORS
@@ -28,7 +26,7 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define GGO_ERRORS
 #define COLOR_ERRORS
 #define SIGNAL_ERRORS
-#define FADE_ERRORS
+#define OSS_MIX_ERRORS
 #define STDOUT_ERRORS
 #define FILE_WRITE_ERRORS
 #define STDIN_ERRORS
@@ -37,17 +35,18 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define VERSION_ERRORS
 #define SCHED_ERRORS
 
-extern const char **para_errlist[];
 
-#define OSS_MIX_ERRORS \
-       PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \
+extern const char **para_errlist[];
 
+#define SYNC_FILTER_ERRORS\
+       PARA_ERROR(SYNC_COMPLETE, "all buddies in sync"), \
+       PARA_ERROR(SYNC_LISTEN_FD, "no fd to listen on"), \
 
 #define ALSA_MIX_ERRORS \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
-       PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \
        PARA_ERROR(ALSA_MIX_GET_VAL, "could not read control element state"), \
        PARA_ERROR(ALSA_MIX_SET_VAL, "could not set control element state"), \
+       PARA_ERROR(ALSA_MIX_RANGE, "value control element out of range"), \
 
 
 #define RESAMPLE_FILTER_ERRORS \
@@ -78,12 +77,16 @@ extern const char **para_errlist[];
        PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
        PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
 
+#define FADE_ERRORS \
+       PARA_ERROR(BAD_CHANNEL, "invalid channel"), \
 
 #define FLACDEC_FILTER_ERRORS \
        PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
        PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
        PARA_ERROR(FLACDEC_EOF, "flacdec encountered end of file condition"), \
 
+#define GUI_ERRORS \
+       PARA_ERROR(GUI_SIGCHLD, "received SIGCHLD"), \
 
 #define FLAC_AFH_ERRORS \
        PARA_ERROR(FLAC_CHAIN_ALLOC, "could not create metadata chain"), \
@@ -95,6 +98,8 @@ extern const char **para_errlist[];
        PARA_ERROR(FLAC_SKIP_META, "could not skip metadata"), \
        PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \
        PARA_ERROR(FLAC_STREAMINFO, "could not read stream info meta block"), \
+       PARA_ERROR(FLAC_REPLACE_COMMENT, "could not replace vorbis comment"), \
+       PARA_ERROR(FLAC_WRITE_CHAIN, "failed to write metadata chain"), \
 
 
 #define AFH_RECV_ERRORS \
@@ -102,9 +107,11 @@ extern const char **para_errlist[];
 
 
 #define OGG_AFH_COMMON_ERRORS \
-       PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error (first packet)"), \
+       PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error"), \
+       PARA_ERROR(STREAM_PACKETIN, "ogg stream packet-in error"), \
        PARA_ERROR(SYNC_PAGEOUT, "ogg sync page-out error (no ogg file?)"), \
-       PARA_ERROR(STREAM_PAGEIN, "ogg stream page-in error (first page)"), \
+       PARA_ERROR(STREAM_PAGEIN, "ogg stream page-in error"), \
+       PARA_ERROR(STREAM_PAGEOUT, "ogg stream page-out error"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
        PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
 
@@ -158,7 +165,6 @@ extern const char **para_errlist[];
 
 
 #define PREBUFFER_FILTER_ERRORS \
-       PARA_ERROR(PREBUFFER_SYNTAX, "syntax error in prebuffer filter config"), \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
 
 
@@ -178,6 +184,7 @@ extern const char **para_errlist[];
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
        PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
        PARA_ERROR(AO_PTHREAD, "pthread error"), \
+       PARA_ERROR(AO_EOF, "ao: end of file"), \
 
 
 #define COMPRESS_FILTER_ERRORS \
@@ -206,7 +213,6 @@ extern const char **para_errlist[];
 
 
 #define AMP_FILTER_ERRORS \
-       PARA_ERROR(AMP_SYNTAX, "syntax error in amp filter config"), \
        PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \
        PARA_ERROR(AMP_EOF, "amp: end of file"), \
 
@@ -255,7 +261,6 @@ extern const char **para_errlist[];
 
 #define BLOB_ERRORS \
        PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
-       PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \
 
 
 #define PLAYLIST_ERRORS \
@@ -265,7 +270,7 @@ extern const char **para_errlist[];
 
 
 #define AFT_ERRORS \
-       PARA_ERROR(BAD_AFSI, "invaiid afs info"), \
+       PARA_ERROR(BAD_AFSI, "invalid afs info"), \
        PARA_ERROR(LOCALTIME, "localtime() failed"), \
        PARA_ERROR(STRFTIME, "strftime() failed"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
@@ -274,6 +279,7 @@ extern const char **para_errlist[];
        PARA_ERROR(NO_AFHI, "audio format handler info required"), \
        PARA_ERROR(AFT_SYNTAX, "audio file table syntax error"), \
        PARA_ERROR(HASH_MISMATCH, "hash mismatch, consider re-add"), \
+       PARA_ERROR(NO_MATCH, "no matches"), \
 
 
 #define USER_LIST_ERRORS \
@@ -297,10 +303,10 @@ extern const char **para_errlist[];
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(NO_CONFIG, "config file not found"), \
        PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
-       PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
        PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
        PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
+       PARA_ERROR(INCOMPAT_FEAT, "client/server incompatibility"), \
 
 
 #define NET_ERRORS \
@@ -337,6 +343,9 @@ extern const char **para_errlist[];
        PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
        PARA_ERROR(NOT_PLAYING, "not playing"), \
        PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
+       PARA_ERROR(STATUS_TIMEOUT, "status item timeout"), \
+       PARA_ERROR(AUDIOD_SIGNAL, "caught deadly signal"), \
+       PARA_ERROR(AUDIOD_TERM, "terminating on user request"), \
 
 
 #define AUDIOD_COMMAND_ERRORS \
@@ -382,12 +391,12 @@ extern const char **para_errlist[];
 
 #define STRING_ERRORS \
        PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
-       PARA_ERROR(STRTOLL, "unknown strtoll error"), \
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \
        PARA_ERROR(REGEX, "regular expression error"), \
        PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
+       PARA_ERROR(BAD_LL, "invalid loglevel"), \
 
 
 #define EXEC_ERRORS \
@@ -400,13 +409,16 @@ extern const char **para_errlist[];
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
        PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
        PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
-
+       PARA_ERROR(ID3_DETACH, "could not detach id3 frame"), \
+       PARA_ERROR(ID3_ATTACH, "could not atttach id3 frame"), \
+       PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
+       PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
 
 #define AAC_AFH_ERRORS \
        PARA_ERROR(STSZ, "did not find stcz atom"), \
        PARA_ERROR(MP4ASC, "audio spec config error"), \
        PARA_ERROR(AAC_AFH_INIT, "failed to init aac decoder"), \
-
+       PARA_ERROR(MP4V2, "mp4v2 library error"), \
 
 #define AAC_COMMON_ERRORS \
        PARA_ERROR(ESDS, "did not find esds atom"), \
@@ -415,6 +427,7 @@ extern const char **para_errlist[];
 
 #define OGG_AFH_ERRORS \
        PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
+       PARA_ERROR(VORBIS_COMMENTHEADER, "could not create vorbis comment header"), \
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
        PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
 
@@ -464,7 +477,7 @@ extern const char **para_errlist[];
        PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
        PARA_ERROR(BAD_USER, "auth request for invalid user"), \
-       PARA_ERROR(BAD_FEATURE, "request for unknown or invalid feature"), \
+       PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_AUTH, "authentication failure"), \
 
 
@@ -492,7 +505,6 @@ extern const char **para_errlist[];
        PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
        PARA_ERROR(AAC_DECODE, "aac decode error"), \
 
-
 #define CHUNK_QUEUE_ERRORS \
        PARA_ERROR(QUEUE, "packet queue overrun"), \
 
@@ -567,20 +579,8 @@ extern const char **para_errlist[];
 /** Set the osl error bit for the given number. */
 #define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
 
-/** Check whether a given number is a system error number.
- *
- * \param num The value to be checked.
- * \param _errno The system error number.
- *
- * \return True if \a num is paraslash's representation of the system
- * error identified by \a _errno.
- */
-_static_inline_ int is_errno(int num, int _errno)
-{
-       assert(num > 0 && _errno > 0);
-       return ERRNO_TO_PARA_ERROR(_errno) == num;
-}
 
+static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
 /**
  * Paraslash's version of strerror(3).
  *
@@ -591,12 +591,12 @@ _static_inline_ int is_errno(int num, int _errno)
 _static_inline_ const char *para_strerror(int num)
 {
        assert(num > 0);
-#ifdef _OSL_H
-       if (IS_OSL_ERROR(num))
-               return osl_strerror(num & ((1 << OSL_ERROR_BIT) - 1));
-#endif
+       if (IS_OSL_ERROR(num)) {
+               assert(weak_osl_strerror);
+               return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
+       }
        if (IS_SYSTEM_ERROR(num))
-               return strerror(num & ((1 << SYSTEM_ERROR_BIT) - 1));
+               return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
        return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)];
 }
 
@@ -607,7 +607,7 @@ _static_inline_ const char *para_strerror(int num)
  *
  * This should be used for all calls to osl functions that return an osl error
  * code. It changes the return value appropriately so that it can be used for
- * printing the correct error message vi para_strerror().
+ * printing the correct error message with para_strerror().
  *
  * \return \a ret if \a ret >= 0, a paraslash error code otherwise.
  */
@@ -620,9 +620,9 @@ _static_inline_ int osl(int ret)
 
 /**
  * Define the error list for one subsystem.
- #
- * The first entry (index 0) is always the name of the subsystem
- * Used by macros in config.h (generated by configure).
+ *
+ * The first entry (index 0) is always the name of the subsystem. Used by
+ * macros in config.h (generated by configure).
  */
 #define DEFINE_ERRLIST(ss) const char * ss ## _ERRLIST[] = {#ss, ss ## _ERRORS}
 
diff --git a/error2.c b/error2.c
new file mode 100644 (file)
index 0000000..85eb41c
--- /dev/null
+++ b/error2.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file error2.c Simple program to create error2.h. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <ctype.h>
+
+// #define DEBUG
+#ifdef DEBUG
+       #define log(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)
+#else
+       #define log(...) do {;} while (0)
+#endif /* DEBUG*/
+
+#define HASH_TABLE_BITS 8
+#define HASH_TABLE_SIZE (1 << HASH_TABLE_BITS)
+
+/* number of executables seen so far */
+static int num_exe;
+
+struct hash_table_entry {
+       char *key;
+       /* only used for objecs, not for executables */
+       unsigned exe_bitmask;
+};
+
+static struct hash_table_entry exe_table[HASH_TABLE_SIZE];
+static struct hash_table_entry obj_table[HASH_TABLE_SIZE];
+
+/* no need for anything sophisticated here */
+static int hash_token(const char *tok)
+{
+       uint32_t tmp = 31415927;
+       const uint8_t *src = (typeof(src))tok;
+
+       for (; *src; src++) {
+               tmp *= 27182817;
+               tmp += *tok;
+       }
+       return tmp % HASH_TABLE_SIZE;
+}
+
+static inline bool slot_empty(int idx, struct hash_table_entry *table)
+{
+       return table[idx].key == NULL;
+}
+
+static char *safe_strdup(const char *str)
+{
+       char *result = strdup(str);
+       if (result)
+               return result;
+       errno = ENOMEM;
+       perror("strdup");
+       exit(EXIT_FAILURE);
+}
+
+static bool lookup(const char *tok, struct hash_table_entry *table, int *idx)
+{
+       int i, h = hash_token(tok);
+
+       for (i = 0; i < HASH_TABLE_SIZE; i++) {
+               *idx = (h + i) % HASH_TABLE_SIZE;
+               if (slot_empty(*idx, table))
+                       return false;
+               if (!strcmp(table[*idx].key, tok))
+                       return true;
+       }
+       log ("hash table full !?\n");
+       exit(EXIT_FAILURE);
+}
+
+static bool insert(const char *tok, struct hash_table_entry *table, int *idx)
+{
+       if (lookup(tok, table, idx))
+               return false; /* not inserted */
+       table[*idx].key = safe_strdup(tok);
+       return true;
+}
+
+static void process_token(char *tok)
+{
+       int idx;
+       size_t len = strlen(tok);
+
+       assert(len > 0);
+       if (tok[len - 1] == ':') {
+               tok[len - 1] = '\0';
+               if (insert(tok, exe_table, &idx)) { /* new exe */
+                       log("exe #%d: '%s', idx: %d\n", num_exe, tok, idx);
+                       num_exe++;
+               }
+       } else {
+               if (num_exe == 0) {
+                       log("invalid input\n");
+                       exit(EXIT_FAILURE);
+               }
+               insert(tok, obj_table, &idx);
+               obj_table[idx].exe_bitmask |= (1 << (num_exe - 1));
+       }
+}
+
+static void print_ss_enum(int idx)
+{
+       char *s = obj_table[idx].key;
+
+       printf("SS_ENUM(");
+       for (; *s; s++)
+               printf("%c", toupper(*s));
+       printf(");\n");
+}
+
+static void dump_bipolar(void)
+{
+       int i, j;
+
+       for (i = 0; i < HASH_TABLE_SIZE; i++) {
+               if (slot_empty(i, obj_table))
+                       continue;
+               printf("#ifdef MAIN_INPUT_FILE_IS_%s\n", obj_table[i].key);
+               for (j = 0; j < HASH_TABLE_SIZE; j++) {
+                       unsigned mi, mj;
+                       if (slot_empty(j, obj_table))
+                               continue;
+                       mi = obj_table[i].exe_bitmask;
+                       mj = obj_table[j].exe_bitmask;
+                       if ((mi & mj) == mi)
+                               print_ss_enum(j);
+               }
+               printf("#endif\n");
+       }
+}
+
+/**
+ * The main function of error2.c.
+ *
+ * The purpose of this program is to create the error2.h file which defines the
+ * enumerations of all error codes which may be used by any given .c file. This
+ * header is included by most .c files of the paraslash suite.
+ *
+ * Since this program is executed on the build system, it must be compiled with
+ * the host compiler.
+ *
+ * \return \p EXIT_SUCCESS or \p EXIT_FAILURE.
+ */
+int main(void)
+{
+       int ret;
+
+       for (;;) {
+               char tok[100];
+               ret = scanf("%96s", tok);
+               if (ret != 1)
+                       break;
+               process_token(tok);
+       }
+       dump_bipolar();
+       return 0;
+}
diff --git a/error2.pl b/error2.pl
deleted file mode 100755 (executable)
index 78ff2c5..0000000
--- a/error2.pl
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env perl
-
-use warnings;
-use strict;
-
-my %matrix;
-my @executables;
-my %objects;
-
-sub make_matrix
-{
-       my ($line, $e, @fields, $field);
-
-       while (defined($line = <>)) {
-               chomp($line);
-               if ($line =~ "^ *\$") {
-                       next;
-               }
-               @fields = split(" ", $line);
-               while (defined(($field = shift(@fields)))) {
-                       if ($field =~ ":\$") {
-                               $field =~ s/://;
-                               $e = $field;
-                               push(@executables, $e);
-                               next;
-                       }
-                       $matrix{$e . ">" . $field} = 1;
-                       $objects{$field} = 1;
-               }
-       }
-}
-
-sub print_safe_objects
-{
-       my @objs = keys(%objects);
-       my ($o1, $o2, $e);
-
-       foreach $o1 (@objs) {
-               print("#ifdef MAIN_INPUT_FILE_IS_$o1\n");
-               O2: foreach $o2 (@objs) {
-                       foreach $e (@executables) {
-                               if (!defined($matrix{$e . ">" . $o1})) {
-                                       next;
-                               }
-                               if (defined($matrix{$e . ">" . $o2})) {
-                                       next;
-                               }
-                               next O2;
-                       }
-                       $_ = $o2;
-                       tr/a-z/A-Z/;
-                       printf("SS_ENUM(%s);\n", $_);
-               }
-               print("#endif\n");
-       }
-}
-make_matrix;
-print_safe_objects;
diff --git a/exec.c b/exec.c
index 66065d374e4e0a19b9410e00c05ec1c0a35225cf..a2348ba392ea684ec02e9add6266f6189fa09267 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2003 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -117,7 +117,7 @@ err_out:
  * Exec the given command.
  *
  * \param pid Will hold the pid of the created process upon return.
- * \param cmdline Holds the command and its arguments, seperated by spaces.
+ * \param cmdline Holds the command and its arguments, separated by spaces.
  * \param fds A pointer to a value-result array.
  *
  * This function uses fork/exec to create a new process. \a fds must be a
@@ -130,7 +130,7 @@ err_out:
  *     - fd[i] > 0: create a pipe and dup i to one end of that pipe.
  *     Upon return, fd[i] contains the file descriptor of the pipe.
  *
- *     In any case, all unneeded filedescriptors are closed.
+ *     In any case, all unneeded file descriptors are closed.
  *
  * \return Standard.
  */
diff --git a/fade.c b/fade.c
index 878b83a17e26f40da9c33c6035dba345353257d4..e05e31733623dacae4beddad69580d077ad23a0d 100644 (file)
--- a/fade.c
+++ b/fade.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1998-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -27,7 +27,7 @@ static struct mixer supported_mixer[] = {MIXER_ARRAY};
 #define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
 
 static int loglevel;
-__printf_2_3 void date_log(int ll, const char *fmt, ...)
+static __printf_2_3 void date_log(int ll, const char *fmt, ...)
 {
        va_list argp;
        time_t t1;
@@ -37,13 +37,21 @@ __printf_2_3 void date_log(int ll, const char *fmt, ...)
                return;
        time(&t1);
        tm = localtime(&t1);
-       printf("%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
+       fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
        va_start(argp, fmt);
        vprintf(fmt, argp);
        va_end(argp);
 }
 __printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
 
+static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel)
+{
+
+       PARA_NOTICE_LOG("using %s mixer channel\n", channel?
+               channel : "default");
+       return m->set_channel(h, channel);
+}
+
 /* Fade to new volume in fade_time seconds. */
 static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time)
 {
@@ -55,11 +63,12 @@ static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_t
        if (fade_time <= 0)
                return m->set(h, new_vol);
        secs = fade_time;
-       PARA_NOTICE_LOG("fading to %d in %d seconds\n", new_vol, secs);
        ret = m->get(h);
        if (ret < 0)
                goto out;
        vol = ret;
+       PARA_NOTICE_LOG("fading %s from %d to %d in %d seconds\n",
+               conf.mixer_channel_arg, vol, new_vol, secs);
        diff = new_vol - vol;
        if (!diff) {
                sleep(secs);
@@ -112,7 +121,7 @@ fail:
        exit(EXIT_FAILURE);
 }
 
-static void change_afs_mode_and_play(char *afs_mode)
+static void change_afs_mode(char *afs_mode)
 {
        char *cmd;
 
@@ -122,7 +131,44 @@ static void change_afs_mode_and_play(char *afs_mode)
        cmd = make_message("select %s", afs_mode);
        client_cmd(cmd);
        free(cmd);
-       client_cmd("play");
+}
+
+static int set_initial_volume(struct mixer *m, struct mixer_handle *h)
+{
+       int i, ret;
+
+       for (i = 0; i < conf.ivol_given; i++) {
+               char *p, *ch, *arg = para_strdup(conf.ivol_arg[i]);
+               int32_t iv;
+               p = strchr(arg, ':');
+               if (p) {
+                       *p = '\0';
+                       p++;
+                       ch = arg;
+               } else {
+                       p = arg;
+                       ch = NULL;
+               }
+               ret = para_atoi32(p, &iv);
+               if (ret < 0) {
+                       free(arg);
+                       return ret;
+               }
+               ret = set_channel(m, h, ch);
+               if (!ch)
+                       ch = "default";
+               if (ret < 0) {
+                       PARA_WARNING_LOG("ignoring channel %s\n", ch);
+                       ret = 0;
+               } else {
+                       PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
+                       ret = m->set(h, iv);
+               }
+               free(arg);
+               if (ret < 0)
+                       return ret;
+       }
+       return 1;
 }
 
 static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
@@ -138,7 +184,6 @@ static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
        int fot = conf.fo_time_arg;
        int fiv = conf.fi_vol_arg;
        int fov = conf.fo_vol_arg;
-       int iv = conf.ivol_arg;
 
        /* calculate wake time */
        time(&t1);
@@ -158,15 +203,18 @@ static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
                tm = localtime(&t1);
        }
        wake_time_epoch = mktime(tm);
-       PARA_INFO_LOG("waketime: %s", asctime(tm));
+       PARA_INFO_LOG("waketime: %u:%02u\n", tm->tm_hour, tm->tm_min);
        client_cmd("stop");
        sleep(1);
        if (fot) {
-               PARA_INFO_LOG("initial volume: %d\n", iv);
-               ret = m->set(h, iv);
+               ret = set_initial_volume(m, h);
+               if (ret < 0)
+                       return ret;
+               change_afs_mode(fo_mood);
+               client_cmd("play");
+               ret = set_channel(m, h, conf.mixer_channel_arg);
                if (ret < 0)
                        return ret;
-               change_afs_mode_and_play(fo_mood);
                ret = fade(m, h, fov, fot);
                if (ret < 0)
                        return ret;
@@ -175,12 +223,14 @@ static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
                if (ret < 0)
                        return ret;
        }
-       if (conf.sleep_mood_given)
-               change_afs_mode_and_play(sleep_mood);
-       else
+       if (conf.sleep_mood_given) {
+               change_afs_mode(sleep_mood);
+               client_cmd("play");
+       } else
                client_cmd("stop");
        if (!fit)
                return 1;
+       change_afs_mode(fi_mood);
        for (;;) {
                time(&t1);
                if (wake_time_epoch <= t1 + fit)
@@ -191,7 +241,7 @@ static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
                        (delay % 3600) / 60);
                sleep(delay);
        }
-       change_afs_mode_and_play(fi_mood);
+       client_cmd("play");
        ret = fade(m, h, fiv, fit);
        PARA_INFO_LOG("fade complete, returning\n");
        return ret;
@@ -246,22 +296,9 @@ static void init_mixers(void)
        }
 }
 
-static int set_channel(struct mixer *m, struct mixer_handle *h)
+static int set_val(struct mixer *m, struct mixer_handle *h)
 {
-       char *channels;
-       int ret;
-
-       ret = m->set_channel(h, conf.mixer_channel_arg);
-       if (ret >= 0) {
-               PARA_NOTICE_LOG("using %s mixer channel\n",
-                       conf.mixer_channel_arg?  conf.mixer_channel_arg
-                               : "default");
-               return ret;
-       }
-       channels = m->get_channels(h);
-       printf("Available channels: %s\n", channels);
-       free(channels);
-       return ret;
+       return m->set(h, conf.val_arg);
 }
 
 static struct mixer *get_mixer_or_die(void)
@@ -330,7 +367,12 @@ int main(int argc, char *argv[])
        ret = m->open(conf.mixer_device_arg, &h);
        if (ret < 0)
                goto out;
-       ret = set_channel(m, h);
+       ret = set_channel(m, h, conf.mixer_channel_arg);
+       if (ret == -E_BAD_CHANNEL) {
+               char *channels = m->get_channels(h);
+               printf("Available channels: %s\n", channels);
+               free(channels);
+       }
        if (ret < 0)
                goto out;
        switch (conf.mode_arg) {
@@ -340,6 +382,9 @@ int main(int argc, char *argv[])
        case mode_arg_snooze:
                ret = snooze(m, h);
                break;
+       case mode_arg_set:
+               ret = set_val(m, h);
+               break;
        default: /* sleep mode */
                ret = sweet_dreams(m, h);
                break;
diff --git a/fd.c b/fd.c
index 20dc07da16e49bcfc13df2a649f0c616755f889b..6a26ce5e3d4d5f2993affc76a544e96db1a5738c 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/mman.h>
-#include <fcntl.h>
-#include <sys/uio.h>
 
 #include "para.h"
 #include "error.h"
 #include "string.h"
 #include "fd.h"
 
+/**
+ * Change the name or location of a file.
+ *
+ * \param oldpath File to be moved.
+ * \param newpath Destination.
+ *
+ * This is just a simple wrapper for the rename(2) system call which returns a
+ * paraslash error code and prints an error message on failure.
+ *
+ * \return Standard.
+ *
+ * \sa rename(2).
+ */
+int xrename(const char *oldpath, const char *newpath)
+{
+       int ret = rename(oldpath, newpath);
+
+       if (ret >= 0)
+               return 1;
+       ret = -ERRNO_TO_PARA_ERROR(errno);
+       PARA_ERROR_LOG("failed to rename %s -> %s\n", oldpath, newpath);
+       return ret;
+}
+
 /**
  * Write an array of buffers to a file descriptor.
  *
@@ -169,7 +191,7 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  *
  * \return Zero or a negative error code. If the underlying call to readv(2)
  * returned zero (indicating an end of file condition) or failed for some
- * reason other than \p EAGAIN, a negative return value is returned.
+ * reason other than \p EAGAIN, a negative error code is returned.
  *
  * In any case, \a num_bytes contains the number of bytes that have been
  * successfully read from \a fd (zero if the first readv() call failed with
@@ -527,6 +549,7 @@ static int para_opendir(const char *dirname, DIR **dir, int *cwd)
 {
        int ret;
 
+       *dir = NULL;
        if (cwd) {
                ret = para_open(".", O_RDONLY, 0);
                if (ret < 0)
diff --git a/fd.h b/fd.h
index 3cd2638bbef079f2546aeb13c6dbc0ec4fa89634..29f387984c70455fec1f2ec2f27f844f88746c02 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file fd.h exported symbols from fd.c */
 
+int xrename(const char *oldpath, const char *newpath);
 int write_all(int fd, const char *buf, size_t len);
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
 int file_exists(const char *);
index 691d696af6e3972ac9f2becf68c0712873fe9624..26ea24a6375db1749f7f9e7b2cd3ebe05196aca8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -47,7 +47,7 @@ struct fec_header {
        uint16_t slice_bytes;
        /** Non-zero if this group is the beginning of the stream. */
        uint8_t bos;
-       /** Non-zero if this stream embedds audio headers into fec groups. */
+       /** Non-zero if this stream embeds audio headers into fec groups. */
        uint8_t header_stream;
 };
 
@@ -436,9 +436,9 @@ static void fecdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static int fecdec_post_select(__a_unused struct sched *s, struct task *t)
+static int fecdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
        int ret;
        struct fec_header h;
index f7b2b30792881e6146a4e2af1d12373f14b8a53f..ef25b0f5695d09c539635cdffe32a3b4cc6dbe53 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -74,9 +74,9 @@ out:
        return ret;
 }
 
-static void file_write_pre_select(struct sched *s, struct task *t)
+static void file_write_pre_select(struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
        int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
 
@@ -97,17 +97,16 @@ static void file_write_close(struct writer_node *wn)
        free(pfwd);
 }
 
-static int file_write_post_select(__a_unused struct sched *s,
-               struct task *t)
+static int file_write_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        int ret;
        char *buf;
        size_t bytes;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(wn->task);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
index 3b68857328b230877e2f8cd9061fb07ee789902c..1ba12689b45d7b41d3e2a8b1a8c06464114211a6 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
 
+/**
+ * Dummy version which only contains NULL pointers.
+ *
+ * This is used by the amp filter which first tries to obtain the amplification
+ * value from an element in this array.
+ */
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
 /** Initialize the array of errors for para_filter. */
@@ -100,7 +106,7 @@ int main(int argc, char *argv[])
 {
        static struct sched s;
        int i, ret;
-       struct filter *f;
+       const struct filter *f;
        struct btr_node *parent;
        struct filter_node **fns;
 
@@ -112,13 +118,13 @@ int main(int argc, char *argv[])
                goto out;
        sit->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdin"));
-       stdin_set_defaults(sit);
-       register_task(&s, &sit->task);
+       stdin_task_register(sit, &s);
 
        fns = para_malloc(conf.filter_given * sizeof(*fns));
        for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) {
                char *fa = conf.filter_arg[i];
                struct filter_node *fn;
+               struct task_info ti;
 
                fn = fns[i] = para_calloc(sizeof(*fn));
                ret = check_filter_arg(fa, &fn->conf);
@@ -127,32 +133,33 @@ int main(int argc, char *argv[])
                        goto out_cleanup;
                }
                fn->filter_num = ret;
-               f = filters + fn->filter_num;
-               sprintf(fn->task.status, "%s", f->name);
+               f = filter_get(fn->filter_num);
                PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name);
                fn->btrn = btr_new_node(&(struct btr_node_description)
                        EMBRACE(.name = f->name, .parent = parent,
                        .handler = f->execute, .context = fn));
-               fn->task.pre_select = f->pre_select;
-               fn->task.post_select = f->post_select;
+               ti.name = f->name;
+               ti.pre_select = f->pre_select;
+               ti.post_select = f->post_select;
+               ti.context = fn;
                f->open(fn);
-               register_task(&s, &fn->task);
+               fn->task = task_register(&ti, &s);
                parent = fn->btrn;
        }
        sot->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdout", .parent = parent));
-       stdout_set_defaults(sot);
-       register_task(&s, &sot->task);
+       stdout_task_register(sot, &s);
 
        s.default_timeout.tv_sec = 1;
        s.default_timeout.tv_usec = 0;
        btr_log_tree(sit->btrn, LL_INFO);
        ret = schedule(&s);
+       sched_shutdown(&s);
 out_cleanup:
        for (i--; i >= 0; i--) {
                struct filter_node *fn = fns[i];
 
-               f = filters + fn->filter_num;
+               f = filter_get(fn->filter_num);
                if (f->close)
                        f->close(fn);
                btr_remove_node(&fn->btrn);
index 80bc896be87612d869da1610366333fced5660a3..31eedcc044d5c1a022b8a6bb6bdd37be1c0eb116 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -27,7 +27,7 @@ struct filter_node {
        /** The buffer tree node. */
        struct btr_node *btrn;
        /** The task corresponding to this filter node. */
-       struct task task;
+       struct task *task;
        /** The minimal input queue size, see \ref btr_node_status(). */
        size_t min_iqs;
 };
@@ -96,19 +96,21 @@ struct filter {
        /**
         * Set scheduler timeout and add file descriptors to fd sets.
         *
-        * This function is used to control the timeout value for select. It
-        * only allowed to decrease the current value. The second purpose of
-        * this function is to set file descriptors to be watched by the
-        * subsequent select call to the two fd sets.
+        * This function controls the timeout value for the next call to
+        * select(2). It may decrease the current timeout but shall never
+        * increase it. The second purpose of this function is to add file
+        * descriptors to the two fd sets of the sched structure. The
+        * descriptors in these sets will be watched by the subsequent
+        * select(2) call.
         */
-       void (*pre_select)(struct sched *s, struct task *t);
+       void (*pre_select)(struct sched *s, void *context);
        /**
         * Convert (filter) the given data.
         *
         * Pointer to the converting function of the filter. On errors, the
         * post_select function is supposed to return a negative error code.
         */
-       int (*post_select)(struct sched *s, struct task *t);
+       int (*post_select)(struct sched *s, void *context);
        /**
         * Answer a buffer tree query.
         *
@@ -121,7 +123,7 @@ struct filter {
 void filter_init(void);
 int check_filter_arg(char *filter_arg, void **conf);
 void print_filter_helps(unsigned flags);
-void generic_filter_pre_select(struct sched *s, struct task *t);
+void generic_filter_pre_select(struct sched *s, void *context);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
 
@@ -138,8 +140,5 @@ static inline void write_int16_host_endian(char *buf, int val)
 
 DECLARE_FILTER_INITS
 
-/** Iterate over the array of supported filters. */
-#define FOR_EACH_SUPPORTED_FILTER(j)  for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
-
 /** The filter array, one structure for each supported filter. */
-extern struct filter filters[NUM_SUPPORTED_FILTERS];
+const struct filter *filter_get(int filter_num);
index 2616c9bdfc940a3415ef6e5abd3a446d4e859b7e..099d056ef1514e2f606adf30d779308b58bead95 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #include "error.h"
 #include "string.h"
 
+/** Iterate over the array of supported filters. */
+#define FOR_EACH_SUPPORTED_FILTER(j)  for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
+
 /** The array of supported filters. */
-struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+
+const struct filter *filter_get(int filter_num)
+{
+       assert(filter_num >= 0);
+       assert(filter_num < NUM_SUPPORTED_FILTERS);
+       return filters + filter_num;
+}
 
 /**
  * Call the init function of each supported filter.
@@ -31,7 +41,7 @@ void filter_init(void)
        int i;
 
        FOR_EACH_SUPPORTED_FILTER(i)
-               filters[i].init(filters + i);
+               filter_get(i)->init((struct filter *)filter_get(i));
 }
 
 /*
@@ -40,7 +50,7 @@ void filter_init(void)
  */
 static int parse_filter_args(int filter_num, char *options, void **conf)
 {
-       struct filter *f = &filters[filter_num];
+       const struct filter *f = filter_get(filter_num);
        int ret, argc;
        char **argv;
 
@@ -80,7 +90,7 @@ int check_filter_arg(char *fa, void **conf)
        *conf = NULL;
 //     PARA_DEBUG_LOG("arg: %s\n", fa);
        FOR_EACH_SUPPORTED_FILTER(j) {
-               const char *name = filters[j].name;
+               const char *name = filter_get(j)->name;
                size_t len = strlen(name);
                char c;
                if (strlen(fa) < len)
@@ -90,7 +100,7 @@ int check_filter_arg(char *fa, void **conf)
                c = fa[len];
                if (c && c != ' ')
                        continue;
-               if (c && !filters[j].parse_config)
+               if (c && !filter_get(j)->parse_config)
                        return -E_BAD_FILTER_OPTIONS;
                return parse_filter_args(j, c? fa + len + 1 :
                        fa + strlen(fa), conf);
@@ -113,12 +123,12 @@ void print_filter_helps(unsigned flags)
                        printf_or_die("\n                  ");
                        num = 0;
                }
-               num += printf_or_die("%s%s", i? " " : "", filters[i].name);
+               num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name);
        }
        printf_or_die("\n");
 
        FOR_EACH_SUPPORTED_FILTER(i) {
-               struct filter *f = filters + i;
+               struct filter *f = (struct filter *)filter_get(i);
 
                if (!f->help.short_help)
                        continue;
@@ -132,18 +142,17 @@ void print_filter_helps(unsigned flags)
  * Set select timeout of the scheduler.
  *
  * \param s The scheduler.
- * \param t The task struct of this filter.
+ * \param context Pointer to the filter node (task context).
  *
  * This looks at the status of the btr node of the filter. If data is available
  * in the input queue of the filter, or if an error occurred, a minimal timeout
  * for the next select call is requested from the scheduler. Otherwise the
  * scheduler timeout is left unchanged.
  */
-void generic_filter_pre_select(struct sched *s, struct task *t)
+void generic_filter_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
 
-       t->error = 0;
        if (btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL) != 0)
                sched_min_delay(s);
 }
index d2f256dc152a8e9547968b5f30ece5cff381754c..51c3c314bf9e265110e82eabae9e94918e1bc7e3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #include "error.h"
 #include "afh.h"
 #include "string.h"
+#include "fd.h"
 
 struct private_flac_afh_data {
-       char *map;
+       const char *map;
        size_t map_bytes;
        size_t fpos;
        struct afh_info *afhi;
@@ -80,7 +81,7 @@ static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
 static int meta_eof_cb(FLAC__IOHandle handle)
 {
        struct private_flac_afh_data *pfad = handle;
-       return pfad->fpos == pfad->map_bytes - 1;
+       return pfad->fpos == pfad->map_bytes;
 }
 
 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
@@ -88,6 +89,15 @@ static int meta_close_cb(FLAC__IOHandle __a_unused handle)
        return 0;
 }
 
+static const FLAC__IOCallbacks meta_callbacks = {
+       .read = meta_read_cb,
+       .write = NULL,
+       .seek = meta_seek_cb,
+       .tell = meta_tell_cb,
+       .eof = meta_eof_cb,
+       .close = meta_close_cb
+};
+
 static void free_tags(struct taginfo *tags)
 {
        freep(&tags->artist);
@@ -131,22 +141,95 @@ static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc,
        }
 }
 
-static int flac_read_meta(struct private_flac_afh_data *pfad)
+/*
+ * FLAC__metadata_object_vorbiscomment_replace_comment() is buggy in some
+ * libFLAC versions (see commit e95399c1 in the flac git repository). Hence we
+ * use delete and add as a workaround.
+ */
+static int flac_replace_vorbis_comment(FLAC__StreamMetadata *b,
+               const char *tag, const char* val)
 {
+       FLAC__bool ok;
+       FLAC__StreamMetadata_VorbisComment_Entry entry;
        int ret;
-       FLAC__IOCallbacks meta_callbacks = {
-               .read = meta_read_cb,
-               .write = NULL,
-               .seek = meta_seek_cb,
-               .tell = meta_tell_cb,
-               .eof = meta_eof_cb,
-               .close = meta_close_cb
-       };
+
+       PARA_INFO_LOG("replacing %s\n", tag);
+       ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
+               b, tag);
+       if (ret < 0)
+               return -E_FLAC_REPLACE_COMMENT;
+       ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(
+               &entry, tag, val? val : "");
+       if (!ok)
+               return -E_FLAC_REPLACE_COMMENT;
+       ok = FLAC__metadata_object_vorbiscomment_append_comment(b, entry,
+               false /* no copy */);
+       if (!ok) {
+               free(entry.entry);
+               return -E_FLAC_REPLACE_COMMENT;
+       }
+       return 1;
+}
+
+static int flac_replace_vorbis_comments(FLAC__Metadata_Chain *chain,
+               FLAC__StreamMetadata *b, struct taginfo *tags)
+{
+       FLAC__bool ok;
+       int ret;
+
+       ret = flac_replace_vorbis_comment(b, "artist", tags->artist);
+       if (ret < 0)
+               return ret;
+       ret = flac_replace_vorbis_comment(b, "title", tags->title);
+       if (ret < 0)
+               return ret;
+       ret = flac_replace_vorbis_comment(b, "album", tags->album);
+       if (ret < 0)
+               return ret;
+       ret = flac_replace_vorbis_comment(b, "year", tags->year);
+       if (ret < 0)
+               return ret;
+       ret = flac_replace_vorbis_comment(b, "comment", tags->comment);
+       if (ret < 0)
+               return ret;
+       /*
+        * Even if padding is disabled, libflac will try to modify the original
+        * file inplace if the metadata size has not changed. This won't work
+        * here though, because the original file is mapped read-only. Since
+        * there is no option to force the use of a temp file we work around
+        * this shortcoming by adding a dummy entry which increases the size of
+        * the meta data. If the entry already exists, we simply remove it.
+        */
+       ok = FLAC__metadata_chain_check_if_tempfile_needed(chain,
+               false /* no padding */);
+       if (!ok) {
+               PARA_INFO_LOG("adding/removing dummy comment\n");
+               ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
+                       b, "comment2");
+               if (ret < 0)
+                       return -E_FLAC_REPLACE_COMMENT;
+               if (ret == 0) { /* nothing was removed */
+                       ret = flac_replace_vorbis_comment(b, "comment2",
+                               "avoid inplace write");
+                       if (ret < 0)
+                               return ret;
+               }
+               assert(FLAC__metadata_chain_check_if_tempfile_needed(chain,
+                       false /* no padding */));
+       }
+       return 1;
+}
+
+static int flac_init_meta(struct private_flac_afh_data *pfad,
+               FLAC__Metadata_Chain **chainp, FLAC__Metadata_Iterator **iterp)
+{
+       int ret;
+       FLAC__bool ok;
        FLAC__Metadata_Chain *chain;
        FLAC__Metadata_Iterator *iter;
-       FLAC__StreamMetadata_StreamInfo *info = NULL;
-       FLAC__bool ok;
 
+       *chainp = NULL;
+       *iterp = NULL;
        chain = FLAC__metadata_chain_new();
        if (!chain)
                return -E_FLAC_CHAIN_ALLOC;
@@ -160,6 +243,25 @@ static int flac_read_meta(struct private_flac_afh_data *pfad)
        if (!iter)
                goto free_chain;
        FLAC__metadata_iterator_init(iter, chain);
+       *iterp = iter;
+       *chainp = chain;
+       return 1;
+free_chain:
+       FLAC__metadata_chain_delete(chain);
+       return ret;
+}
+
+static int flac_read_meta(struct private_flac_afh_data *pfad)
+{
+       int ret;
+       FLAC__Metadata_Chain *chain;
+       FLAC__Metadata_Iterator *iter;
+       FLAC__StreamMetadata_StreamInfo *info = NULL;
+       FLAC__bool ok;
+
+       ret = flac_init_meta(pfad, &chain, &iter);
+       if (ret < 0)
+               return ret;
        for (;;) {
                FLAC__StreamMetadata *b;
                b = FLAC__metadata_iterator_get_block(iter);
@@ -184,7 +286,6 @@ static int flac_read_meta(struct private_flac_afh_data *pfad)
        ret = info? 0: -E_FLAC_STREAMINFO;
 free_iter:
        FLAC__metadata_iterator_delete(iter);
-free_chain:
        FLAC__metadata_chain_delete(chain);
        if (ret < 0)
                free_tags(&pfad->afhi->tags);
@@ -215,7 +316,7 @@ static FLAC__StreamDecoderTellStatus tell_cb(
        return FLAC__STREAM_DECODER_TELL_STATUS_OK;
 }
 
-/* libflac insits on this callback being present. */
+/* libflac insists on this callback being present. */
 static FLAC__StreamDecoderWriteStatus write_cb(
                __a_unused const FLAC__StreamDecoder *decoder,
                __a_unused const FLAC__Frame *frame,
@@ -332,6 +433,88 @@ static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
        return 1;
 }
 
+static size_t temp_write_cb(const void *ptr, size_t size, size_t nmemb,
+       FLAC__IOHandle handle)
+{
+       int ret, fd = *(int *)handle;
+       size_t n = size * nmemb; /* FIXME: possible overflow */
+
+       ret = write_all(fd, ptr, n);
+
+       /*
+        * libflac expects POSIX semantics: If an error occurs, or the end of
+        * the file is reached, the return value is a short item count or zero.
+        */
+       if (ret < 0) {
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               return 0;
+       }
+       return nmemb;
+}
+
+/* only the write callback needs to be supplied for writing the temp file. */
+static const FLAC__IOCallbacks temp_callbacks = {
+       .write = temp_write_cb,
+};
+
+static int flac_write_chain(FLAC__Metadata_Chain *chain,
+               struct private_flac_afh_data *pfad, int fd)
+{
+       FLAC__bool ok;
+
+       ok = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain,
+               false /* no padding*/, pfad,
+               meta_callbacks, &fd, temp_callbacks);
+       if (!ok) {
+               FLAC__Metadata_ChainStatus st;
+               st = FLAC__metadata_chain_status(chain);
+               PARA_ERROR_LOG("chain status: %d\n", st);
+               if (st == FLAC__METADATA_CHAIN_STATUS_READ_ERROR)
+                       PARA_ERROR_LOG("read error\n");
+               return -E_FLAC_WRITE_CHAIN;
+       }
+       return 1;
+}
+
+static int flac_rewrite_tags(const char *map, size_t map_bytes,
+               struct taginfo *tags, int fd, __a_unused const char *filename)
+{
+       int ret;
+       FLAC__Metadata_Chain *chain;
+       FLAC__Metadata_Iterator *iter;
+       FLAC__StreamMetadata *b = NULL;
+       FLAC__bool ok;
+       struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad));
+
+       pfad->map = map;
+       pfad->map_bytes = map_bytes;
+       pfad->fpos = 0;
+
+       ret = flac_init_meta(pfad, &chain, &iter);
+       if (ret < 0)
+               goto free_pfad;
+       for (ok = true; ok; ok = FLAC__metadata_iterator_next(iter)) {
+               b = FLAC__metadata_iterator_get_block(iter);
+               assert(b);
+               if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+                       break;
+               b = NULL;
+       }
+       ret = -E_FLAC_REPLACE_COMMENT;
+       if (!b)
+               goto out;
+       ret = flac_replace_vorbis_comments(chain, b, tags);
+       if (ret < 0)
+               goto out;
+       ret = flac_write_chain(chain, pfad, fd);
+out:
+       FLAC__metadata_iterator_delete(iter);
+       FLAC__metadata_chain_delete(chain);
+free_pfad:
+       free(pfad);
+       return ret;
+}
+
 static const char* flac_suffixes[] = {"flac", NULL};
 
 /**
@@ -343,4 +526,5 @@ void flac_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = flac_get_file_info,
        afh->suffixes = flac_suffixes;
+       afh->rewrite_tags = flac_rewrite_tags;
 }
index 09b319a029a5c392a2d1616eb4fc5602be97d1cc..bbacb3dad87025073b5bf8e99dc1ea8fa6bf5800 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -25,7 +25,7 @@ struct private_flacdec_data {
         * We can not consume directly what was copied by the read callback
         * because we might need to feed unconsumend bytes to the decoder again
         * after the read callback ran out of data and returned ABORT. So we
-        * track how many bytes are unconsumed so far.
+        * track how many bytes have been fed to libflac but are unconsumed so far.
         */
        size_t unconsumed;
 };
@@ -63,6 +63,16 @@ static FLAC__StreamDecoderReadStatus read_cb(
        }
        if (*bytes > 0)
                return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+       /**
+       * Nothing was copied. If the input queue of the btrn is smaller than
+       * the minimal input queue size, our parent must have been gone, so
+       * we're not going to get more input. Since our remaining data is not
+       * sufficient do decode a single frame, we have an EOF condition.
+       */
+       if (btr_get_input_queue_size(btrn) < fn->min_iqs) {
+               assert(btr_no_parent(btrn));
+               return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+       }
        /*
         * We are kind of screwed here. Returning CONTINUE with a byte count of
         * zero leads to an endless loop, so we must return either EOF or
@@ -144,7 +154,7 @@ static FLAC__StreamDecoderWriteStatus write_cb(
                        write_int16_host_endian(outbuffer + 4 * k + 2, right);
                }
        }
-       btr_add_output(outbuffer, n * 4, btrn);
+       btr_add_output(outbuffer, n * channels * 2, btrn);
        flac_consume(fn);
        return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
 }
@@ -200,9 +210,9 @@ static bool output_queue_full(struct btr_node *btrn)
        return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
 }
 
-static void flacdec_pre_select(struct sched *s, struct task *t)
+static void flacdec_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret;
@@ -216,12 +226,13 @@ static void flacdec_pre_select(struct sched *s, struct task *t)
                return sched_min_delay(s);
 }
 
-static int flacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int flacdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret;
+       FLAC__StreamDecoderState state;
 
        if (output_queue_full(btrn))
                return 0;
@@ -240,7 +251,6 @@ static int flacdec_post_select(__a_unused struct sched *s, struct task *t)
                goto out;
        }
        pfd->have_more = false;
-       FLAC__StreamDecoderState state;
        FLAC__stream_decoder_process_single(pfd->decoder);
        state = FLAC__stream_decoder_get_state(pfd->decoder);
        ret = -E_FLACDEC_EOF;
@@ -248,10 +258,12 @@ static int flacdec_post_select(__a_unused struct sched *s, struct task *t)
                goto out;
        if (state == FLAC__STREAM_DECODER_ABORTED) {
                FLAC__stream_decoder_flush(pfd->decoder);
-               fn->min_iqs = pfd->unconsumed + 1;
+               pfd->unconsumed = 0; /* feed unconsumed bytes again */
+               fn->min_iqs = btr_get_input_queue_size(btrn) + 1;
                ret = 1;
                goto out;
        }
+       pfd->have_more = true;
        fn->min_iqs = 0;
        ret = 1;
 out:
index 6bb7452c254f550499945226ab96e4bc44c81c66..45a1c67d20da0b6d229180de92be10644a63d082 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -26,7 +26,7 @@ static void dump_buffer(const char *msg, unsigned char *buf, int len)
 {
        int i;
 
-       fprintf(stderr, "%s (%u bytes): ", msg, len);
+       fprintf(stderr, "%s (%d bytes): ", msg, len);
        for (i = 0; i < len; i++)
                fprintf(stderr, "%02x ", buf[i]);
        fprintf(stderr, "\n");
@@ -87,7 +87,7 @@ void init_random_seed_or_die(void)
 }
 
 /** S-expression for the public part of an RSA key. */
-#define RSA_PUBKEY_SEXP  "(public-key (rsa (n %m) (e %m)))"
+#define RSA_PUBKEY_SEXP "(public-key (rsa (n %m) (e %m)))"
 /** S-expression for a private RSA key. */
 #define RSA_PRIVKEY_SEXP "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))"
 
@@ -97,7 +97,7 @@ static void mgf1(unsigned char *seed, size_t seed_len, unsigned result_len,
 {
        gcry_error_t gret;
        gcry_md_hd_t handle;
-       size_t n;;
+       size_t n;
        unsigned char *md;
        unsigned char octet_string[4], *rp = result, *end = rp + result_len;
 
@@ -239,7 +239,6 @@ static int decode_key(const char *key_file, const char *header_str,
                key[j++] = begin[i];
        }
        key[j] = '\0';
-       //PARA_CRIT_LOG("key: %s\n", key);
        blob_size = key_size * 2;
        blob = para_malloc(blob_size);
        ret = base64_decode(key, blob, blob_size);
@@ -275,14 +274,14 @@ enum asn1_types {
 /* bit 6 has value 0 */
 static inline bool is_primitive(unsigned char c)
 {
-       return ((c & (1<<6)) == 0);
+       return (c & (1<<6)) == 0;
 }
 
 static inline bool is_primitive_integer(unsigned char c)
 {
        if (!is_primitive(c))
                return false;
-       return ((c & 0x1f) == ASN1_TYPE_INTEGER);
+       return (c & 0x1f) == ASN1_TYPE_INTEGER;
 }
 
 /* Bit 8 is zero (and bits 7-1 give the length) */
@@ -305,7 +304,7 @@ static int find_pubkey_bignum_offset(const unsigned char *data, int len)
 {
        const unsigned char *p = data, *end = data + len;
 
-       /* the whole thing istarts with one sequence */
+       /* the whole thing starts with one sequence */
        if (*p != ASN1_TYPE_SEQUENCE)
                return -E_ASN1_PARSE;
        p++;
@@ -429,7 +428,7 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len)
        if (p >= end)
                return -E_ASN1_PARSE;
 
-       /* Skip next integer  */
+       /* skip next integer */
        if (*p != ASN1_TYPE_INTEGER)
                return -E_ASN1_PARSE;
        p++;
@@ -605,7 +604,7 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
        size_t nr_scanned, erroff, decoded_size;
        gcry_mpi_t e = NULL, n = NULL;
 
-       PARA_DEBUG_LOG("decoding %d byte  public rsa-ssh key\n", size);
+       PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
        if (size > INT_MAX / 4)
                return -ERRNO_TO_PARA_ERROR(EOVERFLOW);
        blob = para_malloc(2 * size);
@@ -913,11 +912,25 @@ struct stream_cipher {
        gcry_cipher_hd_t handle;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len)
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes)
 {
        gcry_error_t gret;
-
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
+
+       if (use_aes) {
+               assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+               gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
+                       GCRY_CIPHER_MODE_CTR, 0);
+               assert(gret == 0);
+               gret = gcry_cipher_setkey(sc->handle, data,
+                       AES_CRT128_BLOCK_SIZE);
+               assert(gret == 0);
+               gret = gcry_cipher_setctr(sc->handle,
+                       data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
+               assert(gret == 0);
+               return sc;
+       }
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR,
                GCRY_CIPHER_MODE_STREAM, 0);
        if (gret) {
@@ -938,39 +951,6 @@ void sc_free(struct stream_cipher *sc)
        free(sc);
 }
 
-int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size)
-{
-       gcry_error_t gret;
-       int ret;
-       unsigned char *tmp = para_malloc(size);
-
-       assert(size);
-       gret = gcry_cipher_encrypt(scc->send->handle, tmp, size,
-               (unsigned char *)buf, size);
-       assert(gret == 0);
-       ret = xwrite(scc->fd, (char *)tmp, size);
-       free(tmp);
-       return ret;
-}
-
-int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size)
-{
-       gcry_error_t gret;
-       ssize_t ret = recv(scc->fd, buf, size, 0);
-
-       if (ret < 0)
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-       if (ret <= 0)
-               return ret;
-       /* perform in-place encryption */
-       gret = gcry_cipher_encrypt(scc->recv->handle, (unsigned char *)buf, ret,
-               NULL, 0);
-       assert(gret == 0);
-       return ret;
-}
-
 void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
 {
        gcry_cipher_hd_t handle = sc->handle;
diff --git a/ggo.c b/ggo.c
index 99171c73457d150cb2f4b193f5fe20df0025e662..81ab464491070f8b43fb7cfb018eef8e40a75495 100644 (file)
--- a/ggo.c
+++ b/ggo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/ggo.h b/ggo.h
index 81565e7c24b50d130afd36560c4f5973973f1090..7e5240311dc109f999f1e617b7b860e492dff410 100644 (file)
--- a/ggo.h
+++ b/ggo.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index ecc16fc89bd66d6a71b351b9e468d125bc8a1775..0ef1c15f33593b9067e385a5c1aab65dcf66f0cd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -54,7 +54,7 @@ struct grab_client {
        /** The point of the grab client's node in the buffer tree. */
        struct btr_node *btrn;
        /* The task of this grab client. */
-       struct task task;
+       struct task *task;
        /** Belongs to either the active or the inactive list. */
        struct list_head node;
 };
@@ -92,9 +92,9 @@ err:
        return -E_GC_WRITE;
 }
 
-static void gc_pre_select(struct sched *s, struct task *t)
+static void gc_pre_select(struct sched *s, void *context)
 {
-       struct grab_client *gc = container_of(t, struct grab_client, task);
+       struct grab_client *gc = context;
        int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
 
        if (ret == 0)
@@ -108,7 +108,7 @@ static void gc_pre_select(struct sched *s, struct task *t)
  * We need this forward declaration as post_select() needs
  * activate_grab_client and vice versa.
  */
-static int gc_post_select(struct sched *s, struct task *t);
+static int gc_post_select(struct sched *s, void *context);
 
 /**
  * Move a grab client to the active list and start it.
@@ -129,12 +129,13 @@ static void gc_activate(struct grab_client *gc, struct sched *s)
        list_move(&gc->node, &active_grab_client_list);
        gc->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = name, .parent = parent));
-       gc->task.pre_select = gc_pre_select;
-       gc->task.post_select = gc_post_select;
-       snprintf(gc->task.status, sizeof(gc->task.status) - 1, "%s", name);
-       gc->task.status[sizeof(gc->task.status) - 1] = '\0';
-       gc->task.error = 0;
-       register_task(s, &gc->task);
+
+       gc->task = task_register(&(struct task_info) {
+               .name = name,
+               .pre_select = gc_pre_select,
+               .post_select = gc_post_select,
+               .context = gc,
+       }, s);
 }
 
 /**
@@ -184,9 +185,9 @@ static int gc_close(struct grab_client *gc, int err)
        return 0;
 }
 
-static int gc_post_select(__a_unused struct sched *s, struct task *t)
+static int gc_post_select(__a_unused struct sched *s, void *context)
 {
-       struct grab_client *gc = container_of(t, struct grab_client, task);
+       struct grab_client *gc = context;
        struct btr_node *btrn = gc->btrn;
        int ret;
        size_t sz;
@@ -283,7 +284,12 @@ int grab_client_new(int fd, int argc, char **argv, struct sched *s)
        ret = gc_check_args(argc, argv, gc);
        if (ret < 0)
                goto err_out;
-       gc->fd = fd;
+       ret = dup(fd);
+       if (ret < 0) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               goto err_out;
+       }
+       gc->fd = ret;
        para_list_add(&gc->node, &inactive_grab_client_list);
        gc_activate(gc, s);
        return 1;
index a4e81ced7283144e65db596898b2637196ded577..7a752cee96052fcc7516f9e4e58a1abe252e3e9b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/gui.c b/gui.c
index c20bb7ac98c5d05a9c63c9103a69873b257f0dad..9d96e70f6949362b39b50fa1dc23e85327a9316a 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1998-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 INIT_GUI_ERRLISTS;
 static char *stat_content[NUM_STAT_ITEMS];
 
-#define STANDARD_STATUS_BAR "para_gui " PACKAGE_VERSION " (hit ? for help)"
-
-static int signal_pipe;
-
-static struct win_data {
+static struct gui_window {
        WINDOW *win;
-       size_t begx;
-       size_t begy;
-       size_t cols;
-       size_t lines;
+       bool needs_update;
 } top, bot, sb, in, sep;
 
+/** How many lines of output to remember. */
 #define RINGBUFFER_SIZE 512
+
 struct rb_entry {
        char *msg;
        size_t len;
        int color;
 };
 static struct ringbuffer *bot_win_rb;
-#define NUM_LINES(len) (1 + (len) / bot.cols)
 
 static unsigned scroll_position;
 
-static int curses_active;
-static pid_t cmd_pid;
+static pid_t exec_pid;
 
-static int command_fds[2];
-static int stat_pipe = -1;
+static int exec_fds[2] = {-1, -1};
 static struct gui_args_info conf;
+static int loglevel;
 
-enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
-
+/** Type of the process currently being executed. */
+enum exec_status {
+       EXEC_IDLE, /**< No process running. */
+       EXEC_DCMD, /**< para or display process running. */
+       EXEC_XCMD, /**< External process running. */
+};
 
-#define COLOR_STATUSBAR 52
-#define COLOR_COMMAND 53
-#define COLOR_OUTPUT 54
-#define COLOR_MSG 55
-#define COLOR_ERRMSG 56
-#define COLOR_WELCOME 57
-#define COLOR_SEPARATOR 58
-#define COLOR_TOP 59
-#define COLOR_BOT 60
+/**
+ * Codes for various colors.
+ *
+ * Each status item has its own color pair. The ones defined here start at a
+ * higher number so that they do not overlap with these.
+ */
+enum gui_color_pair {
+       COLOR_STATUSBAR = NUM_STAT_ITEMS + 1,
+       COLOR_COMMAND,
+       COLOR_OUTPUT,
+       COLOR_MSG,
+       COLOR_ERRMSG,
+       COLOR_SEPARATOR,
+       COLOR_TOP,
+       COLOR_BOT,
+};
 
 struct gui_command {
        const char *key;
@@ -80,130 +84,66 @@ struct gui_command {
        void (*handler)(void);
 };
 
-struct stat_item {
-       char name[MAXLINE];
-       char prefix[MAXLINE];
-       char postfix[MAXLINE];
-       unsigned y;
-       unsigned x;
-       unsigned len;
-       int fg, bg;
-       int align;
-       char content[MAXLINE];
+static struct gui_theme theme;
+
+#define GUI_COMMANDS \
+       GUI_COMMAND(help, "?", "print help") \
+       GUI_COMMAND(enlarge_top_win, "+", "enlarge the top window") \
+       GUI_COMMAND(shrink_top_win, "-", "shrink the top window") \
+       GUI_COMMAND(reread_conf, "r", "reread configuration file") \
+       GUI_COMMAND(quit, "q", "exit para_gui") \
+       GUI_COMMAND(refresh, "^L", "redraw the screen") \
+       GUI_COMMAND(next_theme, ".", "switch to next theme") \
+       GUI_COMMAND(prev_theme, ",", "switch to previous theme") \
+       GUI_COMMAND(ll_incr, ">", "increase loglevel (decreases verbosity)") \
+       GUI_COMMAND(ll_decr, "<", "decrease loglevel (increases verbosity)") \
+       GUI_COMMAND(version, "V", "show the para_gui version") \
+       GUI_COMMAND(scroll_up, "<up>", "scroll up one line") \
+       GUI_COMMAND(scroll_down, "<down>", "scroll_down") \
+       GUI_COMMAND(page_up, "<ppage>", "scroll up one page") \
+       GUI_COMMAND(page_down, "<npage>", "scroll down one page") \
+       GUI_COMMAND(scroll_top, "<home>", "scroll to top of buffer") \
+       GUI_COMMAND(cancel_scroll, "<end>", "deactivate scroll mode") \
+
+/* declare command handlers */
+#define GUI_COMMAND(_c, _k, _d) \
+       static void com_ ## _c(void);
+GUI_COMMANDS
+
+#undef GUI_COMMAND
+
+/* define command array */
+#define GUI_COMMAND(_c, _k, _d) \
+       { \
+               .key = _k, \
+               .name = #_c, \
+               .description = _d, \
+               .handler = com_ ## _c \
+       },
+
+static struct gui_command command_list[] = {GUI_COMMANDS {.name = NULL}};
+
+struct input_task {
+       struct task *task;
 };
 
-static struct gui_theme theme;
+struct status_task {
+       struct task *task;
+       pid_t pid;
+       char *buf;
+       int bufsize, loaded;
+       struct timeval next_exec;
+       int fd;
+};
 
-static int _argc;
-static char **_argv;
-
-static void com_help(void);
-static void com_reread_conf(void);
-static void com_enlarge_top_win(void);
-static void com_shrink_top_win(void);
-static void com_version(void);
-__noreturn static void com_quit(void);
-static void com_refresh(void);
-static void com_ll_incr(void);
-static void com_ll_decr(void);
-static void com_prev_theme(void);
-static void com_next_theme(void);
-static void com_scroll_up(void);
-static void com_scroll_down(void);
-static void com_page_up(void);
-static void com_page_down(void);
-static void com_cancel_scrolling(void);
-static void com_scroll_top(void);
-
-static struct gui_command command_list[] = {
-       {
-               .key = "?",
-               .name = "help",
-               .description = "print help",
-               .handler = com_help
-       }, {
-               .key = "+",
-               .name = "enlarge_win",
-               .description = "enlarge the top window",
-               .handler = com_enlarge_top_win
-       }, {
-               .key = "-",
-               .name = "shrink_win",
-               .description = "shrink the top window",
-               .handler = com_shrink_top_win
-       }, {
-               .key = "r",
-               .name = "reread_conf",
-               .description = "reread configuration file",
-               .handler = com_reread_conf
-       }, {
-               .key = "q",
-               .name = "quit",
-               .description = "exit para_gui",
-               .handler = com_quit
-       }, {
-               .key = "^L",
-               .name = "refresh",
-               .description = "redraw the screen",
-               .handler = com_refresh
-       }, {
-               .key = ".",
-               .name = "next_theme",
-               .description = "switch to next theme",
-               .handler = com_next_theme
-       }, {
-               .key = ",",
-               .name = "prev_theme",
-               .description = "switch to previous stream",
-               .handler = com_prev_theme
-       }, {
-               .key = ">",
-               .name = "ll_incr",
-               .description = "increase loglevel (decreases verbosity)",
-               .handler = com_ll_incr
-       }, {
-               .key = "<",
-               .name = "ll_decr",
-               .description = "decrease loglevel (increases verbosity)",
-               .handler = com_ll_decr
-       }, {
-               .key = "V",
-               .name = "version",
-               .description = "show the para_gui version",
-               .handler = com_version
-       }, {
-               .key = "<up>",
-               .name = "scroll_up",
-               .description = "scroll up one line",
-               .handler = com_scroll_up
-       }, {
-               .key = "<down>",
-               .name = "scroll_down",
-               .description = "scroll down one line",
-               .handler = com_scroll_down
-       }, {
-               .key = "<ppage>",
-               .name = "page_up",
-               .description = "scroll up one page",
-               .handler = com_page_up
-       }, {
-               .key = "<npage>",
-               .name = "page_down",
-               .description = "scroll down one page",
-               .handler = com_page_down
-       }, {
-               .key = "<home>",
-               .name = "scroll_top",
-               .description = "scroll to top of buffer",
-               .handler = com_scroll_top
-       }, {
-               .key = "<end>",
-               .name = "cancel_scroll",
-               .description = "deactivate scroll mode",
-               .handler = com_cancel_scrolling
-       }, {
-               .handler = NULL
-       }
+/** Stdout/stderr of the executing process is read in chunks of this size. */
+#define COMMAND_BUF_SIZE 32768
+
+struct exec_task {
+       struct task *task;
+       char command_buf[2][COMMAND_BUF_SIZE]; /* stdout/stderr of command */
+       int cbo[2]; /* command buf offsets */
+       unsigned flags[2]; /* passed to for_each_line() */
 };
 
 static int find_cmd_byname(char *name)
@@ -216,6 +156,37 @@ static int find_cmd_byname(char *name)
        return -1;
 }
 
+/*
+ * Even though ncurses provides getmaxx and getmaxy, these functions/macros are
+ * not described in the XSI Curses standard.
+ */
+static int get_num_lines(struct gui_window *w)
+{
+       int lines;
+       __a_unused int cols; /* avoid "set but not used" warnings */
+
+       getmaxyx(w->win, lines, cols);
+       return lines;
+}
+
+static int get_num_cols(struct gui_window *w)
+{
+       __a_unused int lines; /* avoid "set but not used" warnings */
+       int cols;
+
+       getmaxyx(w->win, lines, cols);
+       return cols;
+}
+
+/** Number of lines of the window are occupied by an output line. */
+#define NUM_LINES(len) (1 + (len) / get_num_cols(&bot))
+
+/* isendwin() returns false before initscr() was called */
+static bool curses_active(void)
+{
+       return top.win && !isendwin();
+}
+
 /* taken from mutt */
 static char *km_keyname(int c)
 {
@@ -272,24 +243,6 @@ static char *km_keyname(int c)
        return buf;
 }
 
-static char *configfile_exists(void)
-{
-       static char *config_file;
-       char *tmp;
-
-       if (!conf.config_file_given) {
-               if (!config_file) {
-                       char *home = para_homedir();
-                       config_file = make_message("%s/.paraslash/gui.conf",
-                               home);
-                       free(home);
-               }
-               tmp = config_file;
-       } else
-               tmp = conf.config_file_arg;
-       return file_exists(tmp)? tmp: NULL;
-}
-
 /* Print given number of spaces to curses window. */
 static void add_spaces(WINDOW* win, unsigned int num)
 {
@@ -343,65 +296,70 @@ static int align_str(WINDOW* win, char *str, unsigned int len,
                waddstr(win, str);
        } else {
                add_spaces(win, num / 2);
-               waddstr(win, str[0]? str: "");
+               waddstr(win, str);
                add_spaces(win, num - num / 2);
        }
        return 1;
 }
 
+static void refresh_window(struct gui_window *gw)
+{
+       gw->needs_update = true;
+}
+
+static bool window_update_needed(void)
+{
+       return top.needs_update || bot.needs_update || sb.needs_update ||
+               in.needs_update || sep.needs_update;
+}
+
 __printf_2_3 static void print_in_bar(int color, const char *fmt,...)
 {
        char *msg;
        va_list ap;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        wattron(in.win, COLOR_PAIR(color));
        va_start(ap, fmt);
        xvasprintf(&msg, fmt, ap);
        va_end(ap);
        wmove(in.win, 0, 0);
-       align_str(in.win, msg, sb.cols, LEFT);
+       align_str(in.win, msg, get_num_cols(&in), LEFT);
        free(msg);
-       wrefresh(in.win);
+       refresh_window(&in);
 }
 
-/*
- * update the status bar
- */
 static void print_status_bar(void)
 {
        char *tmp;
 
-       if (!curses_active)
-               return;
-       tmp = para_strdup(STANDARD_STATUS_BAR);
+       tmp = para_strdup("para_gui " PACKAGE_VERSION " (hit ? for help)");
        wmove(sb.win, 0, 0);
-       align_str(sb.win, tmp, sb.cols, CENTER);
+       align_str(sb.win, tmp, get_num_cols(&sb), CENTER);
        free(tmp);
-       wrefresh(sb.win);
 }
 
 /*
  * get the number of the oldest rbe that is (partially) visible. On return,
- * lines contains the sum of the number of lines of all visable entries. If the
+ * lines contains the sum of the number of lines of all visible entries. If the
  * first one is only partially visible, lines is greater than bot.lines.
  */
 static int first_visible_rbe(unsigned *lines)
 {
-       int i;
+       int i, bot_lines = get_num_lines(&bot);
+
        *lines = 0;
        for (i = scroll_position; i < RINGBUFFER_SIZE; i++) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                int rbe_lines;
                if (!rbe)
                        return i - 1;
-//             fprintf(stderr, "found: %s\n", rbe->msg);
                rbe_lines = NUM_LINES(rbe->len);
-               if (rbe_lines > bot.lines)
+               if (rbe_lines > bot_lines)
                        return -1;
                *lines += rbe_lines;
-               if (*lines >= bot.lines)
+               if (*lines >= bot_lines)
                        return i;
        }
        return RINGBUFFER_SIZE - 1;
@@ -412,7 +370,7 @@ returns number of first visible rbe, *lines is the number of lines drawn.
  */
 static int draw_top_rbe(unsigned *lines)
 {
-       int ret, fvr = first_visible_rbe(lines);
+       int bot_cols, bot_lines, ret, fvr = first_visible_rbe(lines);
        struct rb_entry *rbe;
        size_t bytes_to_skip, cells_to_skip, width;
 
@@ -422,9 +380,10 @@ static int draw_top_rbe(unsigned *lines)
        rbe = ringbuffer_get(bot_win_rb, fvr);
        if (!rbe)
                return -1;
-       if (*lines > bot.lines) {
+       getmaxyx(bot.win, bot_lines, bot_cols);
+       if (*lines > bot_lines) {
                /* rbe is partially visible multi-line */
-               cells_to_skip = (*lines - bot.lines) * bot.cols;
+               cells_to_skip = (*lines - bot_lines) * bot_cols;
                ret = skip_cells(rbe->msg, cells_to_skip, &bytes_to_skip);
                if (ret < 0)
                        return ret;
@@ -444,14 +403,14 @@ static int draw_top_rbe(unsigned *lines)
 static void redraw_bot_win(void)
 {
        unsigned lines;
-       int i;
+       int i, bot_lines = get_num_lines(&bot);
 
        wmove(bot.win, 0, 0);
        wclear(bot.win);
        i = draw_top_rbe(&lines);
        if (i <= 0)
                goto out;
-       while (i > 0 && lines < bot.lines) {
+       while (i > 0 && lines < bot_lines) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, --i);
                if (!rbe) {
                        lines++;
@@ -464,7 +423,7 @@ static void redraw_bot_win(void)
                waddstr(bot.win, rbe->msg);
        }
 out:
-       wrefresh(bot.win);
+       refresh_window(&bot);
 }
 
 static void rb_add_entry(int color, char *msg)
@@ -480,7 +439,6 @@ static void rb_add_entry(int color, char *msg)
        new->len = len;
        new->msg = msg;
        old = ringbuffer_add(bot_win_rb, new);
-//     fprintf(stderr, "added: %s\n", new->msg);
        if (old) {
                free(old->msg);
                free(old);
@@ -506,196 +464,66 @@ __printf_2_3 static void outputf(int color, const char* fmt,...)
        char *msg;
        va_list ap;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        va_start(ap, fmt);
        xvasprintf(&msg, fmt, ap);
        va_end(ap);
        rb_add_entry(color, msg);
-       wrefresh(bot.win);
+       refresh_window(&bot);
 }
 
 static int add_output_line(char *line, void *data)
 {
        int color = *(int *)data? COLOR_ERRMSG : COLOR_OUTPUT;
-       if (!curses_active)
+
+       if (!curses_active())
                return 1;
        rb_add_entry(color, para_strdup(line));
        return 1;
 }
 
-static int loglevel;
-
-__printf_2_3 void curses_log(int ll, const char *fmt,...)
+static __printf_2_3 void curses_log(int ll, const char *fmt,...)
 {
-       int color;
-       char *msg;
        va_list ap;
 
-       if (ll < loglevel || !curses_active)
+       if (ll < loglevel)
                return;
-       switch (ll) {
-               case LL_DEBUG:
-               case LL_INFO:
-               case LL_NOTICE:
-                       color = COLOR_MSG;
-                       break;
-               default:
-                       color = COLOR_ERRMSG;
-       }
        va_start(ap, fmt);
-       xvasprintf(&msg, fmt, ap);
+       if (curses_active()) {
+               int color = ll <= LL_NOTICE? COLOR_MSG : COLOR_ERRMSG;
+               char *msg;
+               unsigned bytes = xvasprintf(&msg, fmt, ap);
+               if (bytes > 0 && msg[bytes - 1] == '\n')
+                       msg[bytes - 1] = '\0'; /* cut trailing newline */
+               rb_add_entry(color, msg);
+               refresh_window(&bot);
+       } else if (exec_pid <= 0) /* no external command running */
+               vfprintf(stderr, fmt, ap);
        va_end(ap);
-       chop(msg);
-       rb_add_entry(color, msg);
-       wrefresh(bot.win);
 }
+/** The log function of para_gui, always set to curses_log(). */
 __printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
 
-static void setup_signal_handling(void)
-{
-       signal_pipe = para_signal_init();
-       para_install_sighandler(SIGINT);
-       para_install_sighandler(SIGTERM);
-       para_install_sighandler(SIGCHLD);
-       para_install_sighandler(SIGWINCH);
-       para_install_sighandler(SIGUSR1);
-}
-
-/* kill every process in the process group and exit */
-__noreturn static void kill_pg_and_die(int ret)
-{
-       para_sigaction(SIGTERM, SIG_IGN);
-       kill(0, SIGTERM);
-       exit(ret);
-}
-
 static void shutdown_curses(void)
 {
-       if (!curses_active)
-               return;
        def_prog_mode();
-       curses_active = 0;
        endwin();
 }
 
-__noreturn static void finish(int ret)
-{
-       shutdown_curses();
-       kill_pg_and_die(ret);
-}
-
-/*
- * exit curses and print given message to stdout/stderr
- */
-__noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
+/* disable curses, print a message, kill running processes and exit */
+__noreturn __printf_2_3 static void die(int exit_code, const char* fmt, ...)
 {
        va_list argp;
-       FILE *outfd = ret? stderr: stdout;
 
        shutdown_curses();
        va_start(argp, fmt);
-       vfprintf(outfd, fmt, argp);
+       vfprintf(stderr, fmt, argp);
        va_end(argp);
-       kill_pg_and_die(ret);
-}
-
-static void print_welcome(void)
-{
-       if (loglevel > LL_NOTICE)
-               return;
-       outputf(COLOR_WELCOME, "Welcome to %s. Theme: %s",
-               version_single_line("gui"), theme.name);
-       wclrtoeol(bot.win);
-}
-
-/*
- * init all windows
- */
-static void init_wins(int top_lines)
-{
-       int i;
-
-       top.lines = top_lines;
-       top.cols = COLS;
-       top.begy = 0;
-       top.begx = 0;
-
-       bot.lines = LINES - top.lines - 3;
-       bot.cols = COLS;
-       bot.begy = top.lines + 1;
-       bot.begx = 0;
-
-       sb.lines = 1;
-       sb.cols = COLS;
-       sb.begy = LINES - 2;
-       sb.begx = 0;
-
-       in.lines = 1;
-       in.cols = COLS;
-       in.begy = LINES - 1;
-       in.begx = 0;
-
-       sep.lines = 1;
-       sep.cols = COLS;
-       sep.begy = top.lines;
-       sep.begx = 0;
-
-       assume_default_colors(theme.default_fg, theme.default_bg);
-       if (top.win) {
-               mvwin(top.win, top.begy, top.begx);
-               wresize(top.win, top.lines, top.cols);
-
-               mvwin(sb.win, sb.begy, sb.begx);
-               wresize(sb.win, sb.lines, sb.cols);
-
-               mvwin(sep.win, sep.begy, sep.begx);
-               wresize(sep.win, sep.lines, sep.cols);
-
-               mvwin(bot.win, bot.begy, bot.begx);
-               wresize(bot.win, bot.lines, bot.cols);
-
-               mvwin(in.win, in.begy, in.begx);
-               wresize(in.win, in.lines, in.cols);
-       } else {
-               sep.win = newwin(sep.lines, sep.cols, sep.begy, sep.begx);
-               top.win = newwin(top.lines, top.cols, top.begy, top.begx);
-               bot.win = newwin(bot.lines, bot.cols, bot.begy, bot.begx);
-               sb.win = newwin(sb.lines, sb.cols, sb.begy, sb.begx);
-               in.win = newwin(in.lines, in.cols, in.begy, in.begx);
-               if (!top.win || !bot.win || !sb.win || !in.win || !sep.win)
-                       msg_n_exit(1, "Error: Cannot create curses windows\n");
-               wclear(bot.win);
-               wclear(sb.win);
-               wclear(in.win);
-               scrollok(bot.win, 1);
-               wattron(sb.win, COLOR_PAIR(COLOR_STATUSBAR));
-               wattron(sep.win, COLOR_PAIR(COLOR_SEPARATOR));
-               wattron(bot.win, COLOR_PAIR(COLOR_BOT));
-               wattron(top.win, COLOR_PAIR(COLOR_TOP));
-               nodelay(top.win, 1);
-               nodelay(bot.win, 1);
-               nodelay(sb.win, 1);
-               nodelay(in.win, 0);
-
-               keypad(top.win, 1);
-               keypad(bot.win, 1);
-               keypad(sb.win, 1);
-               keypad(in.win, 1);
-               print_status_bar();
-       }
-       wmove(sep.win, 0, 0);
-       for (i = 1; i <= COLS; i++)
-               waddstr(sep.win, theme.sep_str);
-       wclear(top.win);
-       //wclear(bot.win);
-       wnoutrefresh(top.win);
-       wnoutrefresh(bot.win);
-       //wnoutrefresh(sb.win);
-       print_status_bar();
-       wnoutrefresh(in.win);
-       wnoutrefresh(sep.win);
-       doupdate();
+       /* kill every process in the process group and exit */
+       para_sigaction(SIGTERM, SIG_IGN);
+       kill(0, SIGTERM);
+       exit(exit_code);
 }
 
 /*
@@ -706,16 +534,16 @@ static void print_stat_item(int i)
        char *tmp;
        struct stat_item_data d = theme.data[i];
        char *c = stat_content[i];
+       int top_lines = get_num_lines(&top);
 
-       if (!curses_active || !d.len || !c)
+       if (!curses_active() || !d.len || !c)
                return;
        tmp = make_message("%s%s%s", d.prefix, c, d.postfix);
-       wmove(top.win, d.y * top.lines / 100, d.x * COLS / 100);
-       wrefresh(top.win);
+       wmove(top.win, d.y * top_lines / 100, d.x * COLS / 100);
        wattron(top.win, COLOR_PAIR(i + 1));
        align_str(top.win, tmp, d.len * COLS / 100, d.align);
        free(tmp);
-       wrefresh(top.win);
+       refresh_window(&top);
 }
 
 static int update_item(int item_num, char *buf)
@@ -749,44 +577,11 @@ print:
        return 1;
 }
 
-static int read_stat_pipe(fd_set *rfds)
-{
-       static char *buf;
-       static int bufsize, loaded;
-       int ret, ret2;
-       size_t sz;
-
-       if (stat_pipe < 0)
-               return 0;
-       if (loaded >= bufsize) {
-               if (bufsize > 1000 * 1000) {
-                       loaded = 0;
-                       return 0;
-               }
-               bufsize += bufsize + 1000;
-               buf = para_realloc(buf, bufsize);
-       }
-       assert(loaded < bufsize);
-       ret = read_nonblock(stat_pipe, buf + loaded, bufsize - loaded,
-               rfds, &sz);
-       loaded += sz;
-       ret2 = for_each_stat_item(buf, loaded, update_item);
-       if (ret < 0 || ret2 < 0) {
-               loaded = 0;
-               return ret2 < 0? ret2 : ret;
-       }
-       sz = ret2; /* what is left */
-       if (sz > 0 && sz < loaded)
-               memmove(buf, buf + loaded - sz, sz);
-       loaded = sz;
-       return 1;
-}
-
 static void print_all_items(void)
 {
        int i;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        FOR_EACH_STATUS_ITEM(i)
                print_stat_item(i);
@@ -802,10 +597,156 @@ static void clear_all_items(void)
        }
 }
 
+static void status_pre_select(struct sched *s, void *context)
+{
+       struct status_task *st = context;
+
+       if (st->fd >= 0)
+               para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+       if (task_get_notification(st->task) < 0)
+               return sched_min_delay(s);
+       if (st->fd < 0)
+               sched_request_barrier_or_min_delay(&st->next_exec, s);
+}
+
+static int status_post_select(struct sched *s, void *context)
+{
+       struct status_task *st = context;
+       size_t sz;
+       int ret, ret2;
+
+       ret = task_get_notification(st->task);
+       if (ret == -E_GUI_SIGCHLD && st->pid > 0) {
+               int exit_status;
+               if (waitpid(st->pid, &exit_status, WNOHANG) == st->pid) {
+                       st->pid = 0;
+                       PARA_ERROR_LOG("stat command exit status: %d",
+                               exit_status);
+               }
+       }
+       if (st->fd < 0) {
+               int fds[3] = {0, 1, 0};
+               if (st->pid > 0)
+                       return 0;
+               /* Avoid busy loop */
+               if (tv_diff(&st->next_exec, now, NULL) > 0)
+                       return 0;
+               st->next_exec.tv_sec = now->tv_sec + 2;
+               ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
+               if (ret < 0)
+                       return 0;
+               ret = mark_fd_nonblocking(fds[1]);
+               if (ret < 0) {
+                       close(fds[1]);
+                       return 0;
+               }
+               st->fd = fds[1];
+               return 0;
+       }
+
+       if (st->loaded >= st->bufsize) {
+               if (st->bufsize > 1000 * 1000) {
+                       st->loaded = 0;
+                       return 0;
+               }
+               st->bufsize += st->bufsize + 1000;
+               st->buf = para_realloc(st->buf, st->bufsize);
+       }
+       assert(st->loaded < st->bufsize);
+       ret = read_nonblock(st->fd, st->buf + st->loaded,
+               st->bufsize - st->loaded, &s->rfds, &sz);
+       st->loaded += sz;
+       ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
+       if (ret < 0 || ret2 < 0) {
+               st->loaded = 0;
+               PARA_NOTICE_LOG("closing stat pipe: %s\n",
+                       para_strerror(ret < 0? -ret : -ret2));
+               close(st->fd);
+               st->fd = -1;
+               clear_all_items();
+               free(stat_content[SI_BASENAME]);
+               stat_content[SI_BASENAME] =
+                       para_strdup("stat command terminated!?");
+               print_all_items();
+               return 0;
+       }
+       sz = ret2; /* what is left */
+       if (sz > 0 && sz < st->loaded)
+               memmove(st->buf, st->buf + st->loaded - sz, sz);
+       st->loaded = sz;
+       return 0;
+}
+
+/*
+ * init all windows
+ */
+static void init_wins(int top_lines)
+{
+       int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
+               in_y = LINES - 1, sep_y = top_lines;
+       int bot_lines = LINES - top_lines - 3, sb_lines = 1, in_lines = 1,
+               sep_lines = 1;
+
+       assume_default_colors(theme.dflt.fg, theme.dflt.bg);
+       if (top.win) {
+               wresize(top.win, top_lines, COLS);
+               mvwin(top.win, top_y, 0);
+
+               wresize(sb.win, sb_lines, COLS);
+               mvwin(sb.win, sb_y, 0);
+
+               wresize(sep.win, sep_lines, COLS);
+               mvwin(sep.win, sep_y, 0);
+
+               wresize(bot.win, bot_lines, COLS);
+               mvwin(bot.win, bot_y, 0);
+
+               wresize(in.win, in_lines, COLS);
+               mvwin(in.win, in_y, 0);
+       } else {
+               sep.win = newwin(sep_lines, COLS, sep_y, 0);
+               top.win = newwin(top_lines, COLS, top_y, 0);
+               bot.win = newwin(bot_lines, COLS, bot_y, 0);
+               sb.win = newwin(sb_lines, COLS, sb_y, 0);
+               in.win = newwin(in_lines, COLS, in_y, 0);
+               if (!top.win || !bot.win || !sb.win || !in.win || !sep.win)
+                       die(EXIT_FAILURE, "Error: Cannot create curses windows\n");
+               wclear(bot.win);
+               wclear(sb.win);
+               wclear(in.win);
+               scrollok(bot.win, 1);
+               wattron(sb.win, COLOR_PAIR(COLOR_STATUSBAR));
+               wattron(sep.win, COLOR_PAIR(COLOR_SEPARATOR));
+               wattron(bot.win, COLOR_PAIR(COLOR_BOT));
+               wattron(top.win, COLOR_PAIR(COLOR_TOP));
+               nodelay(top.win, 1);
+               nodelay(bot.win, 1);
+               nodelay(sb.win, 1);
+               nodelay(in.win, 0);
+
+               keypad(top.win, 1);
+               keypad(bot.win, 1);
+               keypad(sb.win, 1);
+               keypad(in.win, 1);
+       }
+       wmove(sep.win, 0, 0);
+       whline(sep.win, theme.sep_char, COLS);
+       wclear(top.win);
+       print_all_items();
+       //wclear(bot.win);
+       wnoutrefresh(top.win);
+       wnoutrefresh(bot.win);
+       print_status_bar();
+       wnoutrefresh(sb.win);
+       wnoutrefresh(in.win);
+       wnoutrefresh(sep.win);
+       doupdate();
+}
+
 static void init_pair_or_die(short pair, short f, short b)
 {
        if (init_pair(pair, f, b) == ERR)
-               msg_n_exit(EXIT_FAILURE, "fatal: init_pair() failed\n");
+               die(EXIT_FAILURE, "fatal: init_pair() failed\n");
 }
 
 static void init_colors_or_die(void)
@@ -813,62 +754,48 @@ static void init_colors_or_die(void)
        int i;
 
        if (!has_colors())
-               msg_n_exit(EXIT_FAILURE, "fatal: No color term\n");
+               die(EXIT_FAILURE, "fatal: No color term\n");
        if (start_color() == ERR)
-               msg_n_exit(EXIT_FAILURE, "fatal: failed to start colors\n");
+               die(EXIT_FAILURE, "fatal: failed to start colors\n");
        FOR_EACH_STATUS_ITEM(i)
                if (theme.data[i].len)
-                       init_pair_or_die(i + 1, theme.data[i].fg,
-                               theme.data[i].bg);
-       init_pair_or_die(COLOR_STATUSBAR, theme.sb_fg, theme.sb_bg);
-       init_pair_or_die(COLOR_COMMAND, theme.cmd_fg, theme.cmd_bg);
-       init_pair_or_die(COLOR_OUTPUT, theme.output_fg, theme.output_bg);
-       init_pair_or_die(COLOR_MSG, theme.msg_fg, theme.msg_bg);
-       init_pair_or_die(COLOR_ERRMSG, theme.err_msg_fg, theme.err_msg_bg);
-       init_pair_or_die(COLOR_WELCOME, theme.welcome_fg, theme.welcome_bg);
-       init_pair_or_die(COLOR_SEPARATOR, theme.sep_fg, theme.sep_bg);
-       init_pair_or_die(COLOR_TOP, theme.default_fg, theme.default_bg);
-       init_pair_or_die(COLOR_BOT, theme.default_fg, theme.default_bg);
+                       init_pair_or_die(i + 1, theme.data[i].color.fg,
+                               theme.data[i].color.bg);
+       init_pair_or_die(COLOR_STATUSBAR, theme.sb.fg, theme.sb.bg);
+       init_pair_or_die(COLOR_COMMAND, theme.cmd.fg, theme.cmd.bg);
+       init_pair_or_die(COLOR_OUTPUT, theme.output.fg, theme.output.bg);
+       init_pair_or_die(COLOR_MSG, theme.msg.fg, theme.msg.bg);
+       init_pair_or_die(COLOR_ERRMSG, theme.err_msg.fg, theme.err_msg.bg);
+       init_pair_or_die(COLOR_SEPARATOR, theme.sep.fg, theme.sep.bg);
+       init_pair_or_die(COLOR_TOP, theme.dflt.fg, theme.dflt.bg);
+       init_pair_or_die(COLOR_BOT, theme.dflt.fg, theme.dflt.bg);
 }
 
 /* (Re-)initialize the curses library. */
 static void init_curses(void)
 {
-       curses_active = 1;
-       if (top.win && refresh() == ERR) /* refesh is really needed */
-               msg_n_exit(EXIT_FAILURE, "refresh() failed\n");
+       if (curses_active())
+               return;
+       if (refresh() == ERR) /* refresh is really needed */
+               die(EXIT_FAILURE, "refresh() failed\n");
        if (LINES < theme.lines_min || COLS < theme.cols_min)
-               msg_n_exit(EXIT_FAILURE, "Error: Terminal (%dx%d) too small"
+               die(EXIT_FAILURE, "Terminal (%dx%d) too small"
                        " (need at least %dx%d)\n", COLS, LINES,
                        theme.cols_min, theme.lines_min);
        curs_set(0); /* make cursor invisible, ignore errors */
        nonl(); /* do not NL->CR/NL on output, always returns OK */
        /* don't echo input */
        if (noecho() == ERR)
-               msg_n_exit(EXIT_FAILURE, "fatal: noecho() failed\n");
+               die(EXIT_FAILURE, "fatal: noecho() failed\n");
        /* take input chars one at a time, no wait for \n */
        if (cbreak() == ERR)
-               msg_n_exit(EXIT_FAILURE, "fatal: cbreak() failed\n");
+               die(EXIT_FAILURE, "fatal: cbreak() failed\n");
        init_colors_or_die();
        clear(); /* ignore non-fatal errors */
        init_wins(theme.top_lines_default);
-       print_all_items();
        // noecho(); /* don't echo input */
 }
 
-static void check_sigchld(void)
-{
-       int ret;
-       pid_t pid;
-reap_next_child:
-       ret = para_reap_child(&pid);
-       if (ret <= 0)
-               return;
-       if (pid == cmd_pid)
-               cmd_pid = 0;
-       goto reap_next_child;
-}
-
 /*
  * This sucker modifies its first argument. *handler and *arg are
  * pointers to 0-terminated strings (inside line). Crap.
@@ -888,313 +815,358 @@ err_out:
        return 0;
 }
 
-static int check_key_map_args(void)
+static void check_key_map_args_or_die(void)
 {
-       char *s;
-       int i, ret = -1;
-       char *tmp = NULL, *handler, *arg;
+       int i;
+       char *tmp = NULL;
 
        for (i = 0; i < conf.key_map_given; ++i) {
-               s = conf.key_map_arg[i];
-               if (!(*s))
-                       goto err_out;
+               char *handler, *arg;
+
                free(tmp);
-               tmp = para_strdup(s);
+               tmp = para_strdup(conf.key_map_arg[i]);
                if (!split_key_map(tmp, &handler, &arg))
-                       goto err_out;
+                       break;
                if (strlen(handler) != 1)
-                       goto err_out;
-               if (*handler != 'x'
-                       && *handler != 'd'
-                       && *handler != 'i'
-                       && *handler != 'p')
-                       goto err_out;
+                       break;
+               if (*handler != 'x' && *handler != 'd' && *handler != 'i'
+                               && *handler != 'p')
+                       break;
                if (*handler != 'i')
                        continue;
                if (find_cmd_byname(arg) < 0)
-                       goto err_out;
+                       break;
        }
-       ret = 0;
-err_out:
+       if (i != conf.key_map_given)
+               die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
        free(tmp);
-       return ret;
+}
+
+static void parse_config_file_or_die(bool override)
+{
+       bool err;
+       char *config_file;
+       struct gui_cmdline_parser_params params = {
+               .override = override,
+               .initialize = 0,
+               .check_required = !override,
+               .check_ambiguity = 0,
+               .print_errors = 1,
+       };
+
+       if (conf.config_file_given)
+               config_file = para_strdup(conf.config_file_arg);
+       else {
+               char *home = para_homedir();
+               config_file = make_message("%s/.paraslash/gui.conf", home);
+               free(home);
+       }
+       if (!file_exists(config_file)) {
+               if (!conf.config_file_given)
+                       err = false;
+               else {
+                       PARA_EMERG_LOG("config file %s does not exist\n",
+                               config_file);
+                       err = true;
+               }
+               goto out;
+       }
+       /*
+        * When the gengetopt config file parser is called more than once, any
+        * key map arguments found in the config file are _appended_ to the old
+        * values, even though we turn on ->override. We want the new arguments
+        * to replace the old ones, so we must empty the key_map_arg array
+        * first. Unfortunately, this also clears any key map arguments given
+        * at the command line.
+        */
+       if (override) {
+               int i;
+               for (i = 0; i < conf.key_map_given; i++) {
+                       free(conf.key_map_arg[i]);
+                       conf.key_map_arg[i] = NULL;
+               }
+               conf.key_map_given = 0;
+       }
+
+       gui_cmdline_parser_config_file(config_file, &conf, &params);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       check_key_map_args_or_die();
+       err = false;
+out:
+       free(config_file);
+       if (err)
+               exit(EXIT_FAILURE);
+       theme_init(conf.theme_arg, &theme);
+}
+
+/* reread configuration, terminate on errors */
+static void reread_conf(void)
+{
+       /*
+        * gengetopt might print to stderr and exit on errors. So we have to
+        * shutdown curses first.
+        */
+       shutdown_curses();
+       parse_config_file_or_die(true /* override */);
+       init_curses();
+       print_in_bar(COLOR_MSG, "config file reloaded\n");
 }
 
 /*
  * React to various signal-related events
  */
-static void handle_signal(int sig)
+static int signal_post_select(struct sched *s, __a_unused void *context)
 {
-       switch (sig) {
+       int ret = para_next_signal(&s->rfds);
+
+       if (ret <= 0)
+               return 0;
+       switch (ret) {
        case SIGTERM:
-               msg_n_exit(EXIT_FAILURE,
-                       "only the good die young (caught SIGTERM))\n");
-               return;
-       case SIGWINCH:
-               if (curses_active) {
-                       shutdown_curses();
-                       init_curses();
-                       redraw_bot_win();
-               }
-               return;
+               die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
+               return 1;
        case SIGINT:
-               PARA_WARNING_LOG("caught SIGINT, reset");
+               PARA_WARNING_LOG("caught SIGINT, reset\n");
                /* Nothing to do. SIGINT killed our child which gets noticed
                 * by do_select and resets everything.
                 */
-               return;
+               return 1;
        case SIGUSR1:
-               PARA_NOTICE_LOG("got SIGUSR1, rereading configuration");
-               com_reread_conf();
-               return;
+               PARA_NOTICE_LOG("got SIGUSR1, rereading configuration\n");
+               reread_conf();
+               return 1;
        case SIGCHLD:
-               check_sigchld();
-               return;
+               task_notify_all(s, E_GUI_SIGCHLD);
+               return 1;
        }
+       return 1;
 }
 
-static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv)
+static enum exec_status exec_status(void)
 {
-       static struct timeval next_exec, atm, diff;
-       int ret, fds[3] = {0, 1, 0};
-       pid_t pid;
-
-       if (stat_pipe >= 0)
-               goto success;
-       /* Avoid busy loop */
-       gettimeofday(&atm, NULL);
-       if (tv_diff(&next_exec, &atm, &diff) > 0) {
-               if (tv_diff(&diff, tv, NULL) < 0)
-                       *tv = diff;
-               return;
-       }
-       next_exec.tv_sec = atm.tv_sec + 2;
-       ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
-       if (ret < 0)
-               return;
-       ret = mark_fd_nonblocking(fds[1]);
-       if (ret < 0) {
-               close(fds[1]);
-               return;
-       }
-       stat_pipe = fds[1];
-success:
-       para_fd_set(stat_pipe, rfds, max_fileno);
+       if (exec_fds[0] >= 0 || exec_fds[1] >= 0)
+               return EXEC_DCMD;
+       if (exec_pid > 0)
+               return EXEC_XCMD;
+       return EXEC_IDLE;
 }
 
-#define COMMAND_BUF_SIZE 32768
+static void exec_pre_select(struct sched *s, void *context)
+{
+       struct exec_task *et = context;
+       if (exec_fds[0] >= 0)
+               para_fd_set(exec_fds[0], &s->rfds, &s->max_fileno);
+       if (exec_fds[1] >= 0)
+               para_fd_set(exec_fds[1], &s->rfds, &s->max_fileno);
+       if (task_get_notification(et->task) < 0)
+               sched_min_delay(s);
+}
 
-/*
- * This is the core select loop. Besides the (internal) signal
- * pipe, the following other fds are checked according to the mode:
- *
- * GETCH_MODE: check stdin, return when key is pressed
- *
- * COMMAND_MODE: check command fds and stdin. Return when peer has closed both
- * stdout and stderr or when any key is pressed.
- *
- * EXTERNAL_MODE: Check only signal pipe. Used when an external command
- * is running. During that time curses is disabled.  Returns when
- * cmd_pid == 0.
- */
-static int do_select(int mode)
-{
-       fd_set rfds;
-       int ret, i, max_fileno;
-       char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
-       int cbo[2] = {0, 0}; /* command buf offsets */
-       struct timeval tv;
-       unsigned flags[2] = {0, 0}; /* for for_each_line() */
-
-repeat:
-       tv.tv_sec = conf.timeout_arg  / 1000;
-       tv.tv_usec = (conf.timeout_arg % 1000) * 1000;
-//     ret = refresh_status();
-       FD_ZERO(&rfds);
-       max_fileno = 0;
-       status_pre_select(&rfds, &max_fileno, &tv);
-       /* signal pipe */
-       para_fd_set(signal_pipe, &rfds, &max_fileno);
-       /* command pipe only for COMMAND_MODE */
-       if (mode == COMMAND_MODE) {
-               if (command_fds[0] >= 0)
-                       para_fd_set(command_fds[0], &rfds, &max_fileno);
-               if (command_fds[1] >= 0)
-                       para_fd_set(command_fds[1], &rfds, &max_fileno);
+static int exec_post_select(struct sched *s, void *context)
+{
+       struct exec_task *ct = context;
+       int i, ret;
+
+       ret = task_get_notification(ct->task);
+       if (ret == -E_GUI_SIGCHLD && exec_pid > 0) {
+               int exit_status;
+               if (waitpid(exec_pid, &exit_status, WNOHANG) == exec_pid) {
+                       exec_pid = 0;
+                       init_curses();
+                       PARA_INFO_LOG("command exit status: %d", exit_status);
+                       print_in_bar(COLOR_MSG, " ");
+               }
        }
-       if (mode == GETCH_MODE || mode == COMMAND_MODE)
-               para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
-       ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
-       if (ret <= 0)
-               goto check_return; /* skip fd checks */
-       /* signals */
-       ret = para_next_signal(&rfds);
-       if (ret > 0)
-               handle_signal(ret);
-       /* read command pipe if ready */
-       if (mode == COMMAND_MODE) {
-               for (i = 0; i < 2; i++) {
-                       size_t sz;
-                       if (command_fds[i] < 0)
-                               continue;
-                       ret = read_nonblock(command_fds[i],
-                               command_buf[i] + cbo[i],
-                               COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
-                       cbo[i] += sz;
-                       sz = cbo[i];
-                       cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
-                               add_output_line, &i);
-                       if (sz != cbo[i]) { /* at least one line found */
-                               wrefresh(bot.win);
-                               flags[i] = 0;
-                       }
-                       if (ret < 0) {
+       for (i = 0; i < 2; i++) {
+               size_t sz;
+               if (exec_fds[i] < 0)
+                       continue;
+               ret = read_nonblock(exec_fds[i],
+                       ct->command_buf[i] + ct->cbo[i],
+                       COMMAND_BUF_SIZE - 1 - ct->cbo[i], &s->rfds, &sz);
+               ct->cbo[i] += sz;
+               sz = ct->cbo[i];
+               ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
+                       ct->cbo[i], add_output_line, &i);
+               if (sz != ct->cbo[i]) { /* at least one line found */
+                       refresh_window(&bot);
+                       ct->flags[i] = 0;
+               }
+               if (ret < 0 || exec_pid == 0) {
+                       if (ret < 0)
                                PARA_NOTICE_LOG("closing command fd %d: %s",
                                        i, para_strerror(-ret));
-                               close(command_fds[i]);
-                               command_fds[i] = -1;
-                               flags[i] = 0;
-                               if (command_fds[!i] < 0) /* both fds closed */
-                                       return 0;
-                       }
-                       if (cbo[i] == COMMAND_BUF_SIZE - 1) {
-                               PARA_NOTICE_LOG("discarding overlong line");
-                               cbo[i] = 0;
-                               flags[i] = FELF_DISCARD_FIRST;
-                       }
+                       close(exec_fds[i]);
+                       exec_fds[i] = -1;
+                       ct->flags[i] = 0;
+                       ct->cbo[i] = 0;
+                       if (exec_fds[!i] < 0) /* both fds closed */
+                               return 1;
                }
-       }
-       ret = read_stat_pipe(&rfds);
-       if (ret < 0) {
-               PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
-               close(stat_pipe);
-               stat_pipe = -1;
-               clear_all_items();
-               free(stat_content[SI_BASENAME]);
-               stat_content[SI_BASENAME] =
-                       para_strdup("stat command terminated!?");
-               print_all_items();
-       }
-check_return:
-       switch (mode) {
-       case COMMAND_MODE:
-               ret = wgetch(top.win);
-               if (ret != ERR && ret != KEY_RESIZE) {
-                       if (command_fds[0] >= 0) {
-                               close(command_fds[0]);
-                               command_fds[0] = -1;
-                       }
-                       if (command_fds[1] >= 0) {
-                               close(command_fds[1]);
-                               command_fds[1] = -1;
-                       }
-                       if (cmd_pid)
-                               kill(cmd_pid, SIGTERM);
-                       return -1;
+               if (ct->cbo[i] == COMMAND_BUF_SIZE - 1) {
+                       PARA_NOTICE_LOG("discarding overlong line");
+                       ct->cbo[i] = 0;
+                       ct->flags[i] = FELF_DISCARD_FIRST;
                }
-               break;
-       case GETCH_MODE:
-               ret = wgetch(top.win);
-               if (ret != ERR && ret != KEY_RESIZE)
-                       return ret;
-               break;
-       case EXTERNAL_MODE:
-               if (cmd_pid == 0)
-                       return 0;
        }
-       goto repeat;
+       return 0;
 }
 
-/*
- * read from command pipe and print data to bot window
- */
-static void send_output(void)
+static void input_pre_select(struct sched *s, __a_unused void *context)
 {
-       int ret;
+       if (exec_status() != EXEC_XCMD)
+               para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+       if (window_update_needed())
+               sched_min_delay(s);
+}
+
+/* read from command pipe and print data to bot window */
+static void exec_and_display(const char *file_and_args)
+{
+       int ret, fds[3] = {0, 1, 1};
 
-       ret = mark_fd_nonblocking(command_fds[0]);
+       outputf(COLOR_COMMAND, "%s", file_and_args);
+       ret = para_exec_cmdline_pid(&exec_pid, file_and_args, fds);
+       if (ret < 0)
+               return;
+       ret = mark_fd_nonblocking(fds[1]);
        if (ret < 0)
                goto fail;
-       ret = mark_fd_nonblocking(command_fds[1]);
+       ret = mark_fd_nonblocking(fds[2]);
        if (ret < 0)
                goto fail;
-       if (do_select(COMMAND_MODE) >= 0)
-               PARA_INFO_LOG("command complete");
-       else
-               PARA_NOTICE_LOG("command aborted");
-       print_in_bar(COLOR_MSG, " ");
+       exec_fds[0] = fds[1];
+       exec_fds[1] = fds[2];
+       print_in_bar(COLOR_MSG, "hit any key to abort\n");
        return;
 fail:
        PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-       close(command_fds[0]);
-       close(command_fds[1]);
+       close(exec_fds[0]);
+       close(exec_fds[1]);
 }
 
-static void para_cmd(char *cmd)
+static void exec_para(const char *args)
 {
-       int ret, fds[3] = {0, 1, 1};
-       char *c = make_message(BINDIR "/para_client -- %s", cmd);
+       char *file_and_args;
 
-       outputf(COLOR_COMMAND, "%s", c);
-       print_in_bar(COLOR_MSG, "executing client command, hit any key to abort\n");
-       ret = para_exec_cmdline_pid(&cmd_pid, c, fds);
-       free(c);
-       if (ret < 0)
-               return;
-       command_fds[0] = fds[1];
-       command_fds[1] = fds[2];
-       send_output();
+       file_and_args = make_message(BINDIR "/para_client -- %s", args);
+       exec_and_display(file_and_args);
+       free(file_and_args);
 }
 
 /*
- * exec command and print output to bot win
+ * shutdown curses and stat pipe before executing external commands
  */
-static void display_cmd(char *cmd)
+static void exec_external(char *file_and_args)
 {
-       int fds[3] = {0, 1, 1};
+       int fds[3] = {-1, -1, -1};
 
-       print_in_bar(COLOR_MSG, "executing display command, hit any key to abort");
-       outputf(COLOR_COMMAND, "%s", cmd);
-       if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
+       if (exec_pid)
                return;
-       command_fds[0] = fds[1];
-       command_fds[1] = fds[2];
-       send_output();
+       shutdown_curses();
+       para_exec_cmdline_pid(&exec_pid, file_and_args, fds);
 }
 
-/*
- * shutdown curses and stat pipe before executing external commands
- */
-static void external_cmd(char *cmd)
+static void handle_command(int c)
 {
-       int fds[3] = {-1, -1, -1};
+       int i;
 
-       if (cmd_pid)
-               return;
-       shutdown_curses();
-       if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
+       /* first check user-defined key bindings */
+       for (i = 0; i < conf.key_map_given; ++i) {
+               char *tmp, *handler, *arg;
+
+               tmp = para_strdup(conf.key_map_arg[i]);
+               if (!split_key_map(tmp, &handler, &arg)) {
+                       free(tmp);
+                       return;
+               }
+               if (strcmp(tmp, km_keyname(c))) {
+                       free(tmp);
+                       continue;
+               }
+               if (*handler == 'd')
+                       exec_and_display(arg);
+               else if (*handler == 'x')
+                       exec_external(arg);
+               else if (*handler == 'p')
+                       exec_para(arg);
+               else if (*handler == 'i') {
+                       int num = find_cmd_byname(arg);
+                       if (num >= 0)
+                               command_list[num].handler();
+               }
+               free(tmp);
                return;
-       do_select(EXTERNAL_MODE);
-       init_curses();
+       }
+       /* not found, check internal key bindings */
+       for (i = 0; command_list[i].handler; i++) {
+               if (!strcmp(km_keyname(c), command_list[i].key)) {
+                       command_list[i].handler();
+                       return;
+               }
+       }
+       print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
+               km_keyname(c));
+}
+
+static int input_post_select(__a_unused struct sched *s, __a_unused void *context)
+{
+       int ret;
+       enum exec_status exs = exec_status();
+
+       if (exs == EXEC_XCMD)
+               return 0;
+       if (window_update_needed()) {
+               if (top.needs_update)
+                       assert(wnoutrefresh(top.win) == OK);
+               if (bot.needs_update)
+                       assert(wnoutrefresh(bot.win) == OK);
+               if (sep.needs_update)
+                       assert(wnoutrefresh(sep.win) == OK);
+               if (sb.needs_update)
+                       assert(wnoutrefresh(sb.win) == OK);
+               if (in.needs_update)
+                       assert(wnoutrefresh(in.win) == OK);
+               doupdate();
+               top.needs_update = bot.needs_update = sb.needs_update =
+                       in.needs_update = sep.needs_update = false;
+       }
+       ret = wgetch(top.win);
+       if (ret == ERR)
+               return 0;
+       if (ret == KEY_RESIZE) {
+               if (curses_active()) {
+                       shutdown_curses();
+                       init_curses();
+                       redraw_bot_win();
+               }
+               return 0;
+       }
+       if (exs == EXEC_IDLE)
+               handle_command(ret);
+       else if (exec_pid > 0)
+               kill(exec_pid, SIGTERM);
+       return 0;
 }
 
 static void print_scroll_msg(void)
 {
        unsigned lines_total, filled = ringbuffer_filled(bot_win_rb);
        int first_rbe = first_visible_rbe(&lines_total);
+
        print_in_bar(COLOR_MSG, "scrolled view: %d-%d/%d\n", filled - first_rbe,
                filled - scroll_position, ringbuffer_filled(bot_win_rb));
 }
 
 static void com_scroll_top(void)
 {
-       int i = RINGBUFFER_SIZE - 1;
+       int i = RINGBUFFER_SIZE - 1, bot_lines = get_num_lines(&bot);
        unsigned lines = 0;
 
        while (i > 0 && !ringbuffer_get(bot_win_rb, i))
                i--;
        /* i is oldest entry */
-       for (; lines < bot.lines && i >= 0; i--) {
+       for (; lines < bot_lines && i >= 0; i--) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
@@ -1210,7 +1182,7 @@ static void com_scroll_top(void)
        print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
 }
 
-static void com_cancel_scrolling(void)
+static void com_cancel_scroll(void)
 {
 
        if (scroll_position == 0) {
@@ -1224,8 +1196,9 @@ static void com_cancel_scrolling(void)
 static void com_page_down(void)
 {
        unsigned lines = 0;
-       int i = scroll_position;
-       while (lines < bot.lines && --i > 0) {
+       int i = scroll_position, bot_lines = get_num_lines(&bot);
+
+       while (lines < bot_lines && --i > 0) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
@@ -1243,7 +1216,7 @@ static void com_page_down(void)
 static void com_page_up(void)
 {
        unsigned lines;
-       int fvr = first_visible_rbe(&lines);
+       int fvr = first_visible_rbe(&lines), bot_lines = get_num_lines(&bot);
 
        if (fvr < 0 || fvr + 1 >= ringbuffer_filled(bot_win_rb)) {
                print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
@@ -1252,7 +1225,7 @@ static void com_page_up(void)
        scroll_position = fvr + 1;
        for (; scroll_position > 0; scroll_position--) {
                first_visible_rbe(&lines);
-               if (lines == bot.lines)
+               if (lines == bot_lines)
                        break;
        }
        redraw_bot_win();
@@ -1262,7 +1235,7 @@ static void com_page_up(void)
 static void com_scroll_down(void)
 {
        struct rb_entry *rbe;
-       int rbe_lines;
+       int rbe_lines, bot_lines = get_num_lines(&bot);
 
        if (!scroll_position) {
                print_in_bar(COLOR_ERRMSG, "bottom of buffer is shown\n");
@@ -1272,10 +1245,10 @@ static void com_scroll_down(void)
        rbe = ringbuffer_get(bot_win_rb, scroll_position);
        rbe_lines = NUM_LINES(rbe->len);
        wscrl(bot.win, rbe_lines);
-       wmove(bot.win, bot.lines - rbe_lines, 0);
+       wmove(bot.win, bot_lines - rbe_lines, 0);
        wattron(bot.win, COLOR_PAIR(rbe->color));
        waddstr(bot.win, rbe->msg);
-       wrefresh(bot.win);
+       refresh_window(&bot);
        print_scroll_msg();
 }
 
@@ -1305,7 +1278,6 @@ static void com_scroll_up(void)
                        break;
                rbe_lines = NUM_LINES(rbe->len);
                lines += rbe_lines;
-//             fprintf(stderr, "msg: %s\n", rbe->msg);
                wattron(bot.win, COLOR_PAIR(rbe->color));
                waddstr(bot.win, "\n");
                waddstr(bot.win, rbe->msg);
@@ -1313,7 +1285,7 @@ static void com_scroll_up(void)
                        break;
                i--;
        }
-       wrefresh(bot.win);
+       refresh_window(&bot);
        print_scroll_msg();
        return;
 err_out:
@@ -1342,30 +1314,9 @@ static void com_ll_incr(void)
        print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
 }
 
-/*
- * reread configuration, terminate on errors
- */
 static void com_reread_conf(void)
 {
-       char *cf =configfile_exists();
-       struct gui_cmdline_parser_params params = {
-               .override = 1,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 0,
-       };
-
-       if (!cf) {
-               PARA_WARNING_LOG("there is no configuration to read");
-               return;
-       }
-       PARA_INFO_LOG("rereading command line options and config file");
-       gui_cmdline_parser_ext(_argc, _argv, &conf, &params);
-       gui_cmdline_parser_config_file(cf, &conf, &params);
-       PARA_NOTICE_LOG("config file reloaded");
-       if (check_key_map_args() < 0)
-               finish(EXIT_FAILURE);
+       reread_conf();
 }
 
 static void com_help(void)
@@ -1407,25 +1358,25 @@ static void com_help(void)
 
 static void com_shrink_top_win(void)
 {
-       if (top.lines <= theme.top_lines_min) {
-               PARA_WARNING_LOG("can not decrease top window");
+       int top_lines = get_num_lines(&top);
+
+       if (top_lines <= theme.top_lines_min) {
+               PARA_WARNING_LOG("can not decrease top window\n");
                return;
        }
-       init_wins(top.lines - 1);
-       wclear(top.win);
-       print_all_items();
+       init_wins(top_lines - 1);
        print_in_bar(COLOR_MSG, "%s", "decreased top window");
 }
 
 static void com_enlarge_top_win(void)
 {
-       if (bot.lines < 3) {
-               PARA_WARNING_LOG("can not increase top window");
+       int top_lines = get_num_lines(&top), bot_lines = get_num_lines(&bot);
+
+       if (bot_lines < 3) {
+               PARA_WARNING_LOG("can not increase top window\n");
                return;
        }
-       init_wins(top.lines + 1);
-       wclear(top.win);
-       print_all_items();
+       init_wins(top_lines + 1);
        print_in_bar(COLOR_MSG, "increased top window");
 }
 
@@ -1436,7 +1387,7 @@ static void com_version(void)
 
 __noreturn static void com_quit(void)
 {
-       finish(0);
+       die(EXIT_SUCCESS, "%s", "");
 }
 
 static void com_refresh(void)
@@ -1445,69 +1396,16 @@ static void com_refresh(void)
        init_curses();
 }
 
-static void change_theme(int next)
-{
-       if (next)
-               next_theme(&theme);
-       else
-               prev_theme(&theme);
-       /* This seems to be needed twice, why? */
-       com_refresh();
-       com_refresh();
-       PARA_NOTICE_LOG("new theme: %s", theme.name);
-}
-
 static void com_next_theme(void)
 {
-       change_theme(1);
+       theme_next(&theme);
+       com_refresh();
 }
 
 static void com_prev_theme(void)
 {
-       change_theme(0);
-}
-
-
-static void handle_command(int c)
-{
-       int i;
-
-       /* first check user's key bindings */
-       for (i = 0; i < conf.key_map_given; ++i) {
-               char *tmp, *handler, *arg;
-
-               tmp = para_strdup(conf.key_map_arg[i]);
-               if (!split_key_map(tmp, &handler, &arg)) {
-                       free(tmp);
-                       return;
-               }
-               if (strcmp(tmp, km_keyname(c))) {
-                       free(tmp);
-                       continue;
-               }
-               if (*handler == 'd')
-                       display_cmd(arg);
-               else if (*handler == 'x')
-                       external_cmd(arg);
-               else if (*handler == 'p')
-                       para_cmd(arg);
-               else if (*handler == 'i') {
-                       int num = find_cmd_byname(arg);
-                       if (num >= 0)
-                               command_list[num].handler();
-               }
-               free(tmp);
-               return;
-       }
-       /* not found, check internal key bindings */
-       for (i = 0; command_list[i].handler; i++) {
-               if (!strcmp(km_keyname(c), command_list[i].key)) {
-                       command_list[i].handler();
-                       return;
-               }
-       }
-       print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
-               km_keyname(c));
+       theme_prev(&theme);
+       com_refresh();
 }
 
 __noreturn static void print_help_and_die(void)
@@ -1519,54 +1417,94 @@ __noreturn static void print_help_and_die(void)
        exit(0);
 }
 
-int main(int argc, char *argv[])
+static int setup_tasks_and_schedule(void)
 {
        int ret;
-       char *cf;
+       struct exec_task exec_task = {.task = NULL};
+       struct status_task status_task = {.fd = -1};
+       struct input_task input_task = {.task = NULL};
+       struct signal_task *signal_task;
+       struct sched sched = {
+               .default_timeout = {
+                       .tv_sec = conf.timeout_arg  / 1000,
+                       .tv_usec = (conf.timeout_arg % 1000) * 1000,
+               },
+       };
 
-       _argc = argc;
-       _argv = argv;
+       exec_task.task = task_register(&(struct task_info) {
+               .name = "exec",
+               .pre_select = exec_pre_select,
+               .post_select = exec_post_select,
+               .context = &exec_task,
+       }, &sched);
+
+       status_task.task = task_register(&(struct task_info) {
+               .name = "status",
+               .pre_select = status_pre_select,
+               .post_select = status_post_select,
+               .context = &status_task,
+       }, &sched);
+
+       input_task.task = task_register(&(struct task_info) {
+               .name = "input",
+               .pre_select = input_pre_select,
+               .post_select = input_post_select,
+               .context = &input_task,
+       }, &sched);
+
+       signal_task = signal_init_or_die();
+       para_install_sighandler(SIGINT);
+       para_install_sighandler(SIGTERM);
+       para_install_sighandler(SIGCHLD);
+       para_install_sighandler(SIGUSR1);
+       signal_task->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = signal_post_select,
+               .context = signal_task,
+       }, &sched);
+       ret = schedule(&sched);
+       sched_shutdown(&sched);
+       signal_shutdown(signal_task);
+       return ret;
+}
 
+/**
+ * The main function of para_gui.
+ *
+ * \param argc Usual argument count.
+ * \param argv Usual argument vector.
+ *
+ * After initialization para_gui registers the following tasks to the paraslash
+ * scheduler: status, exec, signal, input.
+ *
+ * The status task executes the para_audioc stat command to obtain the status
+ * of para_server and para_audiod, and displays this information in the top
+ * window of para_gui.
+ *
+ * The exec task is responsible for printing the output of the currently
+ * running executable to the bottom window.
+ *
+ * The signal task performs suitable actions according to any signals received.
+ * For example it refreshes all windows on terminal size changes and resets the
+ * terminal on \p SIGTERM.
+ *
+ * The input task reads single key strokes from stdin. For each key pressed, it
+ * executes the command handler associated with this key.
+ *
+ * \return \p EXIT_SUCCESS or \p EXIT_FAILURE.
+ */
+int main(int argc, char *argv[])
+{
        gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        version_handle_flag("gui", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
-       cf = configfile_exists();
-       if (!cf && conf.config_file_given) {
-               fprintf(stderr, "can not read config file %s\n",
-                       conf.config_file_arg);
-               exit(EXIT_FAILURE);
-       }
-       if (cf) {
-               struct gui_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1,
-               };
-               gui_cmdline_parser_config_file(cf, &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       }
-       if (check_key_map_args() < 0) {
-               fprintf(stderr, "invalid key map\n");
-               exit(EXIT_FAILURE);
-       }
-       init_theme_or_die(conf.theme_arg, &theme);
-       top.lines = theme.top_lines_default;
-       setup_signal_handling();
+       parse_config_file_or_die(false /* override */);
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
        setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
-       print_welcome();
-       for (;;) {
-               print_status_bar();
-               ret = do_select(GETCH_MODE);
-               if (!ret)
-                       continue;
-               print_in_bar(COLOR_MSG, " ");
-               handle_command(ret);
-       }
+       return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/gui.h b/gui.h
index 2a3214d0e11acb64d8064ecbfd335a1e06898203..1cfd39c476f82d52aa13fcd33173a94cdbd848cd 100644 (file)
--- a/gui.h
+++ b/gui.h
@@ -1,38 +1,72 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file gui.h symbols used by gui and gui_theme */
 
+/**
+ * The foreground and background color of each status item, the decorations and
+ * all messages can be customized through an instance of this structure.
+ */
+struct gui_color_spec {
+       int fg; /**< Foreground color. */
+       int bg; /**< Background color. */
+};
+
+/** How to display one status item. */
 struct stat_item_data {
-       const char *prefix, *postfix;
-       unsigned x, y, len;
-       int fg, bg, align;
+       const char *prefix; /**< Text to print before the item content. */
+       const char *postfix; /**< Text to print after item content. */
+       unsigned x; /**< Horizontal start coordinate for this item. */
+       unsigned y; /**< Vertical start coordinate for this item. */
+       unsigned len; /**< Item width, including \a prefix and \a postfix. */
+       struct gui_color_spec color; /**< Foreground and background color. */
+       int align; /**< How to align this item. */
 };
 
+/** Theme definition. */
 struct gui_theme {
+       /** Printed at startup. */
        const char *name;
+       /** Also printed at startup. */
        const char *author;
-       int sb_fg, sb_bg;
-       int cmd_fg, cmd_bg;
-       int output_fg, output_bg;
-       int msg_fg, msg_bg;
-       int err_msg_fg, err_msg_bg;
-       int welcome_fg, welcome_bg;
-       int sep_fg, sep_bg;
-       const char *sep_str;
-       int default_fg, default_bg;
-
-       int top_lines_default, top_lines_min;
-       int lines_min, cols_min;
+       /** The character for the separator line. */
+       char sep_char;
+       /** Default color, see assume_default_colors(3). */
+       struct gui_color_spec dflt;
+       /** Default number of lines of the top window. */
+       int top_lines_default;
+       /** Minimal admissible number of lines to display the top window. */
+       int top_lines_min;
+       /** Minimal admissible number of lines to display this theme. */
+       int lines_min;
+       /** Minimal admissible number of columns to display this theme. */
+       int cols_min;
+       /** Individual status item properties. */
        struct stat_item_data data[NUM_STAT_ITEMS];
+       /** Color of the status bar. */
+       struct gui_color_spec sb;
+       /** Color of the name and args of the executing process. */
+       struct gui_color_spec cmd;
+       /** Color for stdout of the executing process. */
+       struct gui_color_spec output;
+       /** Color for log messages of moderate severity. */
+       struct gui_color_spec msg;
+       /** Color for severe log messages. */
+       struct gui_color_spec err_msg;
+       /** Color for the separator line. */
+       struct gui_color_spec sep;
 };
 
-void init_theme_or_die(const char *name, struct gui_theme *t);
-void next_theme(struct gui_theme *);
-void prev_theme(struct gui_theme *);
+void theme_init(const char *name, struct gui_theme *t);
+void theme_prev(struct gui_theme *t);
+void theme_next(struct gui_theme *t);
+
+/** Status item text should be left-aligned. */
 #define LEFT 1
+/** Status item text should be right-aligned. */
 #define RIGHT 2
+/** Status item text should be displayed centered. */
 #define CENTER 3
index a0c47fb0da23f0fb5ceb5243dff1f3bb80e81238..fb8f2f9c367101eb3b4111fe133df9c8806b72c0 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
+/** \file gui_theme.c Theme definitions. */
+
 #include "para.h"
 #include "gui.h"
 #include <curses.h>
@@ -16,28 +18,26 @@ static void init_theme_simple(struct gui_theme *t)
        t->top_lines_min = 2;
        t->cols_min = 40;
        t->top_lines_default = 2;
-       t->sb_bg = COLOR_CYAN;
-       t->sb_fg = COLOR_BLACK;
-       t->cmd_bg = COLOR_WHITE;
-       t->cmd_fg = COLOR_BLACK;
-       t->output_bg = COLOR_BLUE;
-       t->output_fg = COLOR_WHITE;
-       t->msg_bg = COLOR_BLUE;
-       t->msg_fg = COLOR_YELLOW;
-       t->err_msg_bg = COLOR_RED;
-       t->err_msg_fg = COLOR_WHITE;
-       t->welcome_bg = COLOR_BLUE;
-       t->welcome_fg = COLOR_WHITE;
-       t->sep_bg = COLOR_BLUE;
-       t->sep_fg = COLOR_CYAN;
-       t->default_fg = COLOR_WHITE;
-       t->default_bg = COLOR_BLUE;
-       t->sep_str = "*";
+       t->sb.bg = COLOR_CYAN;
+       t->sb.fg = COLOR_BLACK;
+       t->cmd.bg = COLOR_WHITE;
+       t->cmd.fg = COLOR_BLACK;
+       t->output.bg = COLOR_BLUE;
+       t->output.fg = COLOR_WHITE;
+       t->msg.bg = COLOR_BLUE;
+       t->msg.fg = COLOR_YELLOW;
+       t->err_msg.bg = COLOR_RED;
+       t->err_msg.fg = COLOR_WHITE;
+       t->sep.bg = COLOR_BLUE;
+       t->sep.fg = COLOR_CYAN;
+       t->dflt.fg = COLOR_WHITE;
+       t->dflt.bg = COLOR_BLUE;
+       t->sep_char = '*';
 
        d[SI_BASENAME].prefix = "";
        d[SI_BASENAME].postfix = "";
-       d[SI_BASENAME].fg = COLOR_WHITE;
-       d[SI_BASENAME].bg = COLOR_BLUE;
+       d[SI_BASENAME].color.fg = COLOR_WHITE;
+       d[SI_BASENAME].color.bg = COLOR_BLUE;
        d[SI_BASENAME].align = CENTER;
        d[SI_BASENAME].x = 0;
        d[SI_BASENAME].y = 7;
@@ -45,8 +45,8 @@ static void init_theme_simple(struct gui_theme *t)
 
        d[SI_STATUS].prefix = "para_server: ";
        d[SI_STATUS].postfix = "";
-       d[SI_STATUS].fg = COLOR_WHITE;
-       d[SI_STATUS].bg = COLOR_BLUE;
+       d[SI_STATUS].color.fg = COLOR_WHITE;
+       d[SI_STATUS].color.bg = COLOR_BLUE;
        d[SI_STATUS].align = CENTER;
        d[SI_STATUS].x = 0;
        d[SI_STATUS].y = 60;
@@ -54,8 +54,8 @@ static void init_theme_simple(struct gui_theme *t)
 
        d[SI_AUDIOD_STATUS].prefix = "para_audiod: ";
        d[SI_AUDIOD_STATUS].postfix = "";
-       d[SI_AUDIOD_STATUS].fg = COLOR_WHITE;
-       d[SI_AUDIOD_STATUS].bg = COLOR_BLUE;
+       d[SI_AUDIOD_STATUS].color.fg = COLOR_WHITE;
+       d[SI_AUDIOD_STATUS].color.bg = COLOR_BLUE;
        d[SI_AUDIOD_STATUS].align = CENTER;
        d[SI_AUDIOD_STATUS].x = 50;
        d[SI_AUDIOD_STATUS].y = 60;
@@ -75,29 +75,27 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        t->top_lines_min = 9;
        t->top_lines_default = 11; /* default number of lines */
 
-       t->sb_bg = COLOR_GREEN; /* status bar background */
-       t->sb_fg = COLOR_BLACK; /* status bar foreground */
-       t->cmd_bg = COLOR_BLACK;
-       t->cmd_fg = COLOR_YELLOW;
-       t->output_bg = COLOR_BLACK;
-       t->output_fg = COLOR_CYAN;
-       t->msg_bg = COLOR_BLACK;
-       t->msg_fg = COLOR_WHITE;
-       t->err_msg_bg = COLOR_RED;
-       t->err_msg_fg = COLOR_WHITE;
-       t->welcome_bg = COLOR_BLUE;
-       t->welcome_fg = COLOR_WHITE;
-       t->sep_bg = COLOR_BLACK; /* color of the separator */
-       t->sep_fg = COLOR_BLUE;
-       t->sep_str = "-";
-       t->default_bg = COLOR_BLACK;
-       t->default_fg = COLOR_MAGENTA;
+       t->sb.bg = COLOR_GREEN; /* status bar background */
+       t->sb.fg = COLOR_BLACK; /* status bar foreground */
+       t->cmd.bg = COLOR_BLACK;
+       t->cmd.fg = COLOR_YELLOW;
+       t->output.bg = COLOR_BLACK;
+       t->output.fg = COLOR_CYAN;
+       t->msg.bg = COLOR_BLACK;
+       t->msg.fg = COLOR_WHITE;
+       t->err_msg.bg = COLOR_RED;
+       t->err_msg.fg = COLOR_WHITE;
+       t->sep.bg = COLOR_BLACK; /* color of the separator */
+       t->sep.fg = COLOR_BLUE;
+       t->sep_char = 0; /* default (ACS_HLINE) */
+       t->dflt.bg = COLOR_BLACK;
+       t->dflt.fg = COLOR_MAGENTA;
 
 
        d[SI_PLAY_TIME].prefix = "";
        d[SI_PLAY_TIME].postfix = "";
-       d[SI_PLAY_TIME].fg = COLOR_CYAN;
-       d[SI_PLAY_TIME].bg = COLOR_BLACK;
+       d[SI_PLAY_TIME].color.fg = COLOR_CYAN;
+       d[SI_PLAY_TIME].color.bg = COLOR_BLACK;
        d[SI_PLAY_TIME].align = CENTER;
        d[SI_PLAY_TIME].x = 0;
        d[SI_PLAY_TIME].y = 7;
@@ -105,8 +103,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_BASENAME].prefix = "";
        d[SI_BASENAME].postfix = "";
-       d[SI_BASENAME].fg = COLOR_CYAN;
-       d[SI_BASENAME].bg = COLOR_BLACK;
+       d[SI_BASENAME].color.fg = COLOR_CYAN;
+       d[SI_BASENAME].color.bg = COLOR_BLACK;
        d[SI_BASENAME].align = LEFT;
        d[SI_BASENAME].x = 35;
        d[SI_BASENAME].y = 7;
@@ -114,8 +112,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_STATUS].prefix = "";
        d[SI_STATUS].postfix = " ";
-       d[SI_STATUS].fg = COLOR_RED;
-       d[SI_STATUS].bg = COLOR_BLACK;
+       d[SI_STATUS].color.fg = COLOR_RED;
+       d[SI_STATUS].color.bg = COLOR_BLACK;
        d[SI_STATUS].align = RIGHT;
        d[SI_STATUS].x = 0;
        d[SI_STATUS].y = 17;
@@ -123,8 +121,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_STATUS_FLAGS].prefix = "(";
        d[SI_STATUS_FLAGS].postfix = ")";
-       d[SI_STATUS_FLAGS].fg = COLOR_RED;
-       d[SI_STATUS_FLAGS].bg = COLOR_BLACK;
+       d[SI_STATUS_FLAGS].color.fg = COLOR_RED;
+       d[SI_STATUS_FLAGS].color.bg = COLOR_BLACK;
        d[SI_STATUS_FLAGS].align = LEFT;
        d[SI_STATUS_FLAGS].x = 11;
        d[SI_STATUS_FLAGS].y = 17;
@@ -132,8 +130,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_IMAGE_ID].prefix = "img: ";
        d[SI_IMAGE_ID].postfix = "";
-       d[SI_IMAGE_ID].fg = COLOR_RED;
-       d[SI_IMAGE_ID].bg = COLOR_BLACK;
+       d[SI_IMAGE_ID].color.fg = COLOR_RED;
+       d[SI_IMAGE_ID].color.bg = COLOR_BLACK;
        d[SI_IMAGE_ID].align = CENTER;
        d[SI_IMAGE_ID].x = 21;
        d[SI_IMAGE_ID].y = 17;
@@ -141,8 +139,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_LYRICS_ID].prefix = "lyr: ";
        d[SI_LYRICS_ID].postfix = "";
-       d[SI_LYRICS_ID].fg = COLOR_RED;
-       d[SI_LYRICS_ID].bg = COLOR_BLACK;
+       d[SI_LYRICS_ID].color.fg = COLOR_RED;
+       d[SI_LYRICS_ID].color.bg = COLOR_BLACK;
        d[SI_LYRICS_ID].align = CENTER;
        d[SI_LYRICS_ID].x = 31;
        d[SI_LYRICS_ID].y = 17;
@@ -150,8 +148,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_FORMAT].prefix = "format: ";
        d[SI_FORMAT].postfix = "";
-       d[SI_FORMAT].fg = COLOR_RED;
-       d[SI_FORMAT].bg = COLOR_BLACK;
+       d[SI_FORMAT].color.fg = COLOR_RED;
+       d[SI_FORMAT].color.bg = COLOR_BLACK;
        d[SI_FORMAT].align = CENTER;
        d[SI_FORMAT].x = 42;
        d[SI_FORMAT].y = 17;
@@ -159,8 +157,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_NUM_PLAYED].prefix = "#";
        d[SI_NUM_PLAYED].postfix = "";
-       d[SI_NUM_PLAYED].fg = COLOR_RED;
-       d[SI_NUM_PLAYED].bg = COLOR_BLACK;
+       d[SI_NUM_PLAYED].color.fg = COLOR_RED;
+       d[SI_NUM_PLAYED].color.bg = COLOR_BLACK;
        d[SI_NUM_PLAYED].align = LEFT;
        d[SI_NUM_PLAYED].x = 60;
        d[SI_NUM_PLAYED].y = 17;
@@ -168,8 +166,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_BITRATE].prefix = "";
        d[SI_BITRATE].postfix = "";
-       d[SI_BITRATE].fg = COLOR_RED;
-       d[SI_BITRATE].bg = COLOR_BLACK;
+       d[SI_BITRATE].color.fg = COLOR_RED;
+       d[SI_BITRATE].color.bg = COLOR_BLACK;
        d[SI_BITRATE].align = CENTER;
        d[SI_BITRATE].x = 65;
        d[SI_BITRATE].y = 17;
@@ -177,8 +175,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_FREQUENCY].prefix = "";
        d[SI_FREQUENCY].postfix = "";
-       d[SI_FREQUENCY].fg = COLOR_RED;
-       d[SI_FREQUENCY].bg = COLOR_BLACK;
+       d[SI_FREQUENCY].color.fg = COLOR_RED;
+       d[SI_FREQUENCY].color.bg = COLOR_BLACK;
        d[SI_FREQUENCY].align = CENTER;
        d[SI_FREQUENCY].x = 78;
        d[SI_FREQUENCY].y = 17;
@@ -186,8 +184,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_SCORE].prefix = "sc: ";
        d[SI_SCORE].postfix = "";
-       d[SI_SCORE].fg = COLOR_RED;
-       d[SI_SCORE].bg = COLOR_BLACK;
+       d[SI_SCORE].color.fg = COLOR_RED;
+       d[SI_SCORE].color.bg = COLOR_BLACK;
        d[SI_SCORE].align = CENTER;
        d[SI_SCORE].x = 88;
        d[SI_SCORE].y = 17;
@@ -195,8 +193,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_AUDIOD_STATUS].prefix = "";
        d[SI_AUDIOD_STATUS].postfix = "";
-       d[SI_AUDIOD_STATUS].fg = COLOR_MAGENTA;
-       d[SI_AUDIOD_STATUS].bg = COLOR_BLACK;
+       d[SI_AUDIOD_STATUS].color.fg = COLOR_MAGENTA;
+       d[SI_AUDIOD_STATUS].color.bg = COLOR_BLACK;
        d[SI_AUDIOD_STATUS].align = CENTER;
        d[SI_AUDIOD_STATUS].x = 0;
        d[SI_AUDIOD_STATUS].y = 27;
@@ -204,8 +202,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_DECODER_FLAGS].prefix = "[";
        d[SI_DECODER_FLAGS].postfix = "]";
-       d[SI_DECODER_FLAGS].fg = COLOR_MAGENTA;
-       d[SI_DECODER_FLAGS].bg = COLOR_BLACK;
+       d[SI_DECODER_FLAGS].color.fg = COLOR_MAGENTA;
+       d[SI_DECODER_FLAGS].color.bg = COLOR_BLACK;
        d[SI_DECODER_FLAGS].align = CENTER;
        d[SI_DECODER_FLAGS].x = 5;
        d[SI_DECODER_FLAGS].y = 27;
@@ -213,8 +211,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_MTIME].prefix = "mod: ";
        d[SI_MTIME].postfix = "";
-       d[SI_MTIME].fg = COLOR_MAGENTA;
-       d[SI_MTIME].bg = COLOR_BLACK;
+       d[SI_MTIME].color.fg = COLOR_MAGENTA;
+       d[SI_MTIME].color.bg = COLOR_BLACK;
        d[SI_MTIME].align = CENTER;
        d[SI_MTIME].x = 15;
        d[SI_MTIME].y = 27;
@@ -222,8 +220,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_FILE_SIZE].prefix = "";
        d[SI_FILE_SIZE].postfix = "kb";
-       d[SI_FILE_SIZE].fg = COLOR_MAGENTA;
-       d[SI_FILE_SIZE].bg = COLOR_BLACK;
+       d[SI_FILE_SIZE].color.fg = COLOR_MAGENTA;
+       d[SI_FILE_SIZE].color.bg = COLOR_BLACK;
        d[SI_FILE_SIZE].align = CENTER;
        d[SI_FILE_SIZE].x = 37;
        d[SI_FILE_SIZE].y = 27;
@@ -231,8 +229,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_CHANNELS].prefix = "";
        d[SI_CHANNELS].postfix = "ch";
-       d[SI_CHANNELS].fg = COLOR_MAGENTA;
-       d[SI_CHANNELS].bg = COLOR_BLACK;
+       d[SI_CHANNELS].color.fg = COLOR_MAGENTA;
+       d[SI_CHANNELS].color.bg = COLOR_BLACK;
        d[SI_CHANNELS].align = CENTER;
        d[SI_CHANNELS].x = 47;
        d[SI_CHANNELS].y = 27;
@@ -240,8 +238,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_LAST_PLAYED].prefix = "lp: ";
        d[SI_LAST_PLAYED].postfix = "";
-       d[SI_LAST_PLAYED].fg = COLOR_MAGENTA;
-       d[SI_LAST_PLAYED].bg = COLOR_BLACK;
+       d[SI_LAST_PLAYED].color.fg = COLOR_MAGENTA;
+       d[SI_LAST_PLAYED].color.bg = COLOR_BLACK;
        d[SI_LAST_PLAYED].align = CENTER;
        d[SI_LAST_PLAYED].x = 52;
        d[SI_LAST_PLAYED].y = 27;
@@ -249,8 +247,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_NUM_CHUNKS].prefix = "";
        d[SI_NUM_CHUNKS].postfix = "x";
-       d[SI_NUM_CHUNKS].fg = COLOR_MAGENTA;
-       d[SI_NUM_CHUNKS].bg = COLOR_BLACK;
+       d[SI_NUM_CHUNKS].color.fg = COLOR_MAGENTA;
+       d[SI_NUM_CHUNKS].color.bg = COLOR_BLACK;
        d[SI_NUM_CHUNKS].align = RIGHT;
        d[SI_NUM_CHUNKS].x = 73;
        d[SI_NUM_CHUNKS].y = 27;
@@ -258,8 +256,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_CHUNK_TIME].prefix = "";
        d[SI_CHUNK_TIME].postfix = "ms";
-       d[SI_CHUNK_TIME].fg = COLOR_MAGENTA;
-       d[SI_CHUNK_TIME].bg = COLOR_BLACK;
+       d[SI_CHUNK_TIME].color.fg = COLOR_MAGENTA;
+       d[SI_CHUNK_TIME].color.bg = COLOR_BLACK;
        d[SI_CHUNK_TIME].align = LEFT;
        d[SI_CHUNK_TIME].x = 84;
        d[SI_CHUNK_TIME].y = 27;
@@ -267,8 +265,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_AMPLIFICATION].prefix = "amp:";
        d[SI_AMPLIFICATION].postfix = "";
-       d[SI_AMPLIFICATION].fg = COLOR_MAGENTA;
-       d[SI_AMPLIFICATION].bg = COLOR_BLACK;
+       d[SI_AMPLIFICATION].color.fg = COLOR_MAGENTA;
+       d[SI_AMPLIFICATION].color.bg = COLOR_BLACK;
        d[SI_AMPLIFICATION].align = RIGHT;
        d[SI_AMPLIFICATION].x = 92;
        d[SI_AMPLIFICATION].y = 27;
@@ -276,8 +274,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_TECHINFO].prefix = "";
        d[SI_TECHINFO].postfix = "";
-       d[SI_TECHINFO].fg = COLOR_GREEN;
-       d[SI_TECHINFO].bg = COLOR_BLACK;
+       d[SI_TECHINFO].color.fg = COLOR_GREEN;
+       d[SI_TECHINFO].color.bg = COLOR_BLACK;
        d[SI_TECHINFO].align = CENTER;
        d[SI_TECHINFO].x = 0;
        d[SI_TECHINFO].y = 43;
@@ -285,8 +283,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_TITLE].prefix = "";
        d[SI_TITLE].postfix = ",";
-       d[SI_TITLE].fg = COLOR_GREEN;
-       d[SI_TITLE].bg = COLOR_BLACK;
+       d[SI_TITLE].color.fg = COLOR_GREEN;
+       d[SI_TITLE].color.bg = COLOR_BLACK;
        d[SI_TITLE].align = RIGHT;
        d[SI_TITLE].x = 0;
        d[SI_TITLE].y = 53;
@@ -294,8 +292,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_ARTIST].prefix = " by ";
        d[SI_ARTIST].postfix = "";
-       d[SI_ARTIST].fg = COLOR_GREEN;
-       d[SI_ARTIST].bg = COLOR_BLACK;
+       d[SI_ARTIST].color.fg = COLOR_GREEN;
+       d[SI_ARTIST].color.bg = COLOR_BLACK;
        d[SI_ARTIST].align = LEFT;
        d[SI_ARTIST].x = 45;
        d[SI_ARTIST].y = 53;
@@ -303,8 +301,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_YEAR].prefix = "(";
        d[SI_YEAR].postfix = ")";
-       d[SI_YEAR].fg = COLOR_GREEN;
-       d[SI_YEAR].bg = COLOR_BLACK;
+       d[SI_YEAR].color.fg = COLOR_GREEN;
+       d[SI_YEAR].color.bg = COLOR_BLACK;
        d[SI_YEAR].align = RIGHT;
        d[SI_YEAR].x = 90;
        d[SI_YEAR].y = 53;
@@ -312,8 +310,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_ALBUM].prefix = "A: ";
        d[SI_ALBUM].postfix = ",";
-       d[SI_ALBUM].fg = COLOR_GREEN;
-       d[SI_ALBUM].bg = COLOR_BLACK;
+       d[SI_ALBUM].color.fg = COLOR_GREEN;
+       d[SI_ALBUM].color.bg = COLOR_BLACK;
        d[SI_ALBUM].align = RIGHT;
        d[SI_ALBUM].x = 0;
        d[SI_ALBUM].y = 63;
@@ -321,8 +319,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_COMMENT].prefix = " C: ";
        d[SI_COMMENT].postfix = "";
-       d[SI_COMMENT].fg = COLOR_GREEN;
-       d[SI_COMMENT].bg = COLOR_BLACK;
+       d[SI_COMMENT].color.fg = COLOR_GREEN;
+       d[SI_COMMENT].color.bg = COLOR_BLACK;
        d[SI_COMMENT].align = LEFT;
        d[SI_COMMENT].x = 50;
        d[SI_COMMENT].y = 63;
@@ -330,8 +328,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_AFS_MODE].prefix = "";
        d[SI_AFS_MODE].postfix = "";
-       d[SI_AFS_MODE].fg = COLOR_YELLOW;
-       d[SI_AFS_MODE].bg = COLOR_BLACK;
+       d[SI_AFS_MODE].color.fg = COLOR_YELLOW;
+       d[SI_AFS_MODE].color.bg = COLOR_BLACK;
        d[SI_AFS_MODE].align = CENTER;
        d[SI_AFS_MODE].x = 0;
        d[SI_AFS_MODE].y = 77;
@@ -339,8 +337,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_ATTRIBUTES_TXT].prefix = "";
        d[SI_ATTRIBUTES_TXT].postfix = "";
-       d[SI_ATTRIBUTES_TXT].fg = COLOR_YELLOW;
-       d[SI_ATTRIBUTES_TXT].bg = COLOR_BLACK;
+       d[SI_ATTRIBUTES_TXT].color.fg = COLOR_YELLOW;
+       d[SI_ATTRIBUTES_TXT].color.bg = COLOR_BLACK;
        d[SI_ATTRIBUTES_TXT].align = CENTER;
        d[SI_ATTRIBUTES_TXT].x = 0;
        d[SI_ATTRIBUTES_TXT].y = 87;
@@ -348,8 +346,8 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
 
        d[SI_DIRECTORY].prefix = "dir: ";
        d[SI_DIRECTORY].postfix = "";
-       d[SI_DIRECTORY].fg = COLOR_YELLOW;
-       d[SI_DIRECTORY].bg = COLOR_BLACK;
+       d[SI_DIRECTORY].color.fg = COLOR_YELLOW;
+       d[SI_DIRECTORY].color.bg = COLOR_BLACK;
        d[SI_DIRECTORY].align = CENTER;
        d[SI_DIRECTORY].x = 0;
        d[SI_DIRECTORY].y = 97;
@@ -372,6 +370,7 @@ static struct theme_description themes[] = {
        },
 };
 
+/** Number of elements in the \a themes array. */
 #define NUM_THEMES (ARRAY_SIZE(themes))
 
 static int current_theme_num;
@@ -385,9 +384,18 @@ static void set_theme(int num, struct gui_theme *t)
        t->name = themes[num].name;
        themes[num].init(t);
        current_theme_num = num;
+       PARA_NOTICE_LOG("theme: %s\n", t->name);
 }
 
-void init_theme_or_die(const char *name, struct gui_theme *t)
+/**
+ * Initialize a theme.
+ *
+ * \param name Name of the theme to be initialized.
+ * \param t The function fills out this structure.
+ *
+ * This function exits if there is no theme called \a name.
+ */
+void theme_init(const char *name, struct gui_theme *t)
 {
        int i;
 
@@ -402,12 +410,30 @@ void init_theme_or_die(const char *name, struct gui_theme *t)
        exit(EXIT_FAILURE);
 }
 
-void prev_theme(struct gui_theme *t)
+/**
+ * Activate the previous available theme.
+ *
+ * \param t Theme definition is stored here.
+ *
+ * This picks the theme that comes before the currently active one, or the last
+ * available theme, if the current one is the first.
+ *
+ * \sa \ref theme_next().
+ */
+void theme_prev(struct gui_theme *t)
 {
        return set_theme(++current_theme_num, t);
 }
 
-void next_theme(struct gui_theme *t)
+/**
+ * Activate the next available theme.
+ *
+ * \param t Theme definition is stored here.
+ *
+ * This works exactly as theme_prev() but cycles forwards through the list of
+ * available themes.
+ */
+void theme_next(struct gui_theme *t)
 {
        return set_theme(--current_theme_num, t);
 }
index 7db8ba193c1eaa2fd6046efd91011c25d37da032..2f334787200e4a0134df6a108a0c4dc8a7dd0775 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -7,7 +7,12 @@
 /** \file http_recv.c paraslash's http receiver */
 
 #include <regex.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
@@ -55,12 +60,12 @@ static char *make_request_msg(void)
        return ret;
 }
 
-static void http_recv_pre_select(struct sched *s, struct task *t)
+static void http_recv_pre_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
 
-       if (generic_recv_pre_select(s, t) <= 0)
+       if (generic_recv_pre_select(s, rn) <= 0)
                return;
        if  (phd->status == HTTP_CONNECTED)
                para_fd_set(rn->fd, &s->wfds, &s->max_fileno);
@@ -73,16 +78,16 @@ static void http_recv_pre_select(struct sched *s, struct task *t)
  * area with data read from the socket. In any case, update the state of the
  * connection if necessary.
  */
-static int http_recv_post_select(struct sched *s, struct task *t)
+static int http_recv_post_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
        struct btr_node *btrn = rn->btrn;
        int ret, iovcnt;
        struct iovec iov[2];
        size_t num_bytes;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(rn->task);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
index 52383b06b465d14b8369badbc3606a7753ec90c5..9d0f49aee03cd6b30d6a73e9eda74dcb9be4cfc0 100644 (file)
@@ -1,21 +1,24 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file http_send.c paraslash's http sender */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
-#include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 #include "string.h"
 #include "server.cmdline.h"
 #include "afh.h"
-#include "afs.h"
 #include "server.h"
 #include "http.h"
 #include "list.h"
@@ -230,9 +233,9 @@ static int http_com_allow(struct sender_command_data *scd)
        return 1;
 }
 
-static char *http_info(void)
+static char *http_status(void)
 {
-       return get_sender_info(hss, "http");
+       return generic_sender_status(hss, "http");
 }
 
 /**
@@ -246,19 +249,19 @@ static char *http_info(void)
 void http_send_init(struct sender *s)
 {
        int ret;
-       s->info = http_info;
+       s->status = http_status;
        s->send = http_send;
        s->pre_select = http_pre_select;
        s->post_select = http_post_select;
        s->shutdown_clients = http_shutdown_clients;
        s->resolve_target = NULL;
        s->help = generic_sender_help;
-       s->client_cmds[SENDER_ON] = http_com_on;
-       s->client_cmds[SENDER_OFF] = http_com_off;
-       s->client_cmds[SENDER_DENY] = http_com_deny;
-       s->client_cmds[SENDER_ALLOW] = http_com_allow;
-       s->client_cmds[SENDER_ADD] = NULL;
-       s->client_cmds[SENDER_DELETE] = NULL;
+       s->client_cmds[SENDER_on] = http_com_on;
+       s->client_cmds[SENDER_off] = http_com_off;
+       s->client_cmds[SENDER_deny] = http_com_deny;
+       s->client_cmds[SENDER_allow] = http_com_allow;
+       s->client_cmds[SENDER_add] = NULL;
+       s->client_cmds[SENDER_delete] = NULL;
 
        init_sender_status(hss, conf.http_access_arg, conf.http_access_given,
                conf.http_port_arg, conf.http_max_clients_arg,
diff --git a/imdct.c b/imdct.c
index aab498a390ba3ecdeded2452120d729540e84184..d2a0681898b6497f2742e842d3b9ef1ed104621a 100644 (file)
--- a/imdct.c
+++ b/imdct.c
  * \file imdct.c Inverse modified discrete cosine transform.
  */
 
-#include <inttypes.h>
 #include <math.h>
-#include <string.h>
-#include <stdlib.h>
 #include <regex.h>
 
 #include "para.h"
@@ -58,7 +55,9 @@ struct mdct_context {
        struct fft_context fft;
 };
 
-/** cos(2 * pi * x / n) for 0 <= x <= n / 4, followed by its reverse */
+/** \cond cosine_tabs */
+
+/* cos(2 * pi * x / n) for 0 <= x <= n / 4, followed by its reverse */
 #define COSINE_TAB(n) static fftsample_t cos_ ## n[n / 2] __a_aligned(16)
 
 COSINE_TAB(16);
@@ -79,6 +78,7 @@ static fftsample_t *cos_tabs[] = {
        cos_16, cos_32, cos_64, cos_128, cos_256, cos_512, cos_1024, cos_2048,
        cos_4096, cos_8192, cos_16384, cos_32768, cos_65536,
 };
+/** \endcond cosine_tabs */
 
 __a_const static int split_radix_permutation(int i, int n)
 {
diff --git a/install-sh b/install-sh
deleted file mode 100755 (executable)
index 6ce63b9..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-#!/bin/sh
-#
-# install - install a program, script, or datafile
-#
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.  It can only install one file at a time, a restriction
-# shared with many OS's install programs.
-
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
-
-
-# put in absolute paths if you don't have them in your path; or use env. vars.
-
-mvprog="${MVPROG-mv}"
-cpprog="${CPPROG-cp}"
-chmodprog="${CHMODPROG-chmod}"
-chownprog="${CHOWNPROG-chown}"
-chgrpprog="${CHGRPPROG-chgrp}"
-stripprog="${STRIPPROG-strip}"
-rmprog="${RMPROG-rm}"
-mkdirprog="${MKDIRPROG-mkdir}"
-
-transformbasename=""
-transform_arg=""
-instcmd="$mvprog"
-chmodcmd="$chmodprog 0755"
-chowncmd=""
-chgrpcmd=""
-stripcmd=""
-rmcmd="$rmprog -f"
-mvcmd="$mvprog"
-src=""
-dst=""
-dir_arg=""
-
-while [ x"$1" != x ]; do
-    case $1 in
-       -c) instcmd=$cpprog
-           shift
-           continue;;
-
-       -d) dir_arg=true
-           shift
-           continue;;
-
-       -m) chmodcmd="$chmodprog $2"
-           shift
-           shift
-           continue;;
-
-       -o) chowncmd="$chownprog $2"
-           shift
-           shift
-           continue;;
-
-       -g) chgrpcmd="$chgrpprog $2"
-           shift
-           shift
-           continue;;
-
-       -s) stripcmd=$stripprog
-           shift
-           continue;;
-
-       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
-           shift
-           continue;;
-
-       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
-           shift
-           continue;;
-
-       *)  if [ x"$src" = x ]
-           then
-               src=$1
-           else
-               # this colon is to work around a 386BSD /bin/sh bug
-               :
-               dst=$1
-           fi
-           shift
-           continue;;
-    esac
-done
-
-if [ x"$src" = x ]
-then
-       echo "$0: no input file specified" >&2
-       exit 1
-else
-       :
-fi
-
-if [ x"$dir_arg" != x ]; then
-       dst=$src
-       src=""
-
-       if [ -d "$dst" ]; then
-               instcmd=:
-               chmodcmd=""
-       else
-               instcmd=$mkdirprog
-       fi
-else
-
-# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
-# might cause directories to be created, which would be especially bad
-# if $src (and thus $dsttmp) contains '*'.
-
-       if [ -f "$src" ] || [ -d "$src" ]
-       then
-               :
-       else
-               echo "$0: $src does not exist" >&2
-               exit 1
-       fi
-
-       if [ x"$dst" = x ]
-       then
-               echo "$0: no destination specified" >&2
-               exit 1
-       else
-               :
-       fi
-
-# If destination is a directory, append the input filename; if your system
-# does not like double slashes in filenames, you may need to add some logic
-
-       if [ -d "$dst" ]
-       then
-               dst=$dst/`basename "$src"`
-       else
-               :
-       fi
-fi
-
-## this sed command emulates the dirname command
-dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
-
-# Make sure that the destination directory exists.
-#  this part is taken from Noah Friedman's mkinstalldirs script
-
-# Skip lots of stat calls in the usual case.
-if [ ! -d "$dstdir" ]; then
-defaultIFS='
-       '
-IFS="${IFS-$defaultIFS}"
-
-oIFS=$IFS
-# Some sh's can't handle IFS=/ for some reason.
-IFS='%'
-set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
-IFS=$oIFS
-
-pathcomp=''
-
-while [ $# -ne 0 ] ; do
-       pathcomp=$pathcomp$1
-       shift
-
-       if [ ! -d "$pathcomp" ] ;
-        then
-               $mkdirprog "$pathcomp"
-       else
-               :
-       fi
-
-       pathcomp=$pathcomp/
-done
-fi
-
-if [ x"$dir_arg" != x ]
-then
-       $doit $instcmd "$dst" &&
-
-       if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi &&
-       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi &&
-       if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi &&
-       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi
-else
-
-# If we're going to rename the final executable, determine the name now.
-
-       if [ x"$transformarg" = x ]
-       then
-               dstfile=`basename "$dst"`
-       else
-               dstfile=`basename "$dst" $transformbasename |
-                       sed $transformarg`$transformbasename
-       fi
-
-# don't allow the sed command to completely eliminate the filename
-
-       if [ x"$dstfile" = x ]
-       then
-               dstfile=`basename "$dst"`
-       else
-               :
-       fi
-
-# Make a couple of temp file names in the proper directory.
-
-       dsttmp=$dstdir/_inst.$$_
-       rmtmp=$dstdir/_rm.$$_
-
-# Trap to clean up temp files at exit.
-
-       trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0
-       trap '(exit $?); exit' 1 2 13 15
-
-# Move or copy the file name to the temp name
-
-       $doit $instcmd "$src" "$dsttmp" &&
-
-# and set any options; do chmod last to preserve setuid bits
-
-# If any of these fail, we abort the whole thing.  If we want to
-# ignore errors from any of these, just make sure not to ignore
-# errors from the above "$doit $instcmd $src $dsttmp" command.
-
-       if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi &&
-       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi &&
-       if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi &&
-       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi &&
-
-# Now remove or move aside any old file at destination location.  We try this
-# two ways since rm can't unlink itself on some systems and the destination
-# file might be busy for other reasons.  In this case, the final cleanup
-# might fail but the new file should still install successfully.
-
-{
-       if [ -f "$dstdir/$dstfile" ]
-       then
-               $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null ||
-               $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null ||
-               {
-                 echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
-                 (exit 1); exit
-               }
-       else
-               :
-       fi
-} &&
-
-# Now rename the file to the real destination.
-
-       $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
-
-fi &&
-
-# The final little trick to "correctly" pass the exit status to the exit trap.
-
-{
-       (exit 0); exit
-}
index 43cb99f2781979e74b66eef640fadb7878f30aeb..ce48af329e45c31961a0a39dbba3b975d67ec207 100644 (file)
@@ -1,21 +1,19 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file interactive.c Readline abstraction for interactive sessions. */
 
+#include "para.h"
+
 #include <regex.h>
-#include <stdbool.h>
-#include <curses.h>
 #include <readline/readline.h>
 #include <readline/history.h>
 #include <sys/ioctl.h>
-#include <assert.h>
 #include <signal.h>
 
-#include "para.h"
 #include "fd.h"
 #include "buffer_tree.h"
 #include "list.h"
@@ -28,11 +26,11 @@ struct i9e_private {
        struct i9e_client_info *ici;
        FILE *stderr_stream;
        int num_columns;
+       int num_key_bindings;
        char empty_line[1000];
-       struct task task;
+       struct task *task;
        struct btr_node *stdout_btrn;
        bool last_write_was_status;
-       bool line_handler_running;
        bool input_eof;
        bool caught_sigint;
        bool caught_sigterm;
@@ -52,7 +50,7 @@ static struct i9e_private i9e_private, *i9ep = &i9e_private;
  */
 int i9e_get_error(void)
 {
-       return i9ep->task.error;
+       return task_status(i9ep->task);
 }
 
 static bool is_prefix(const char *partial, const char *full, size_t len)
@@ -227,6 +225,7 @@ static void wipe_bottom_line(void)
        fprintf(i9ep->stderr_stream, "\r");
 }
 
+#ifndef RL_FREE_KEYMAP_DECLARED
 /**
  * Free all storage associated with a keymap.
  *
@@ -237,6 +236,7 @@ static void wipe_bottom_line(void)
  * \param keymap The keymap to deallocate.
  */
 void rl_free_keymap(Keymap keymap);
+#endif
 
 /**
  * Reset the terminal and save the in-memory command line history.
@@ -312,7 +312,7 @@ free_line:
        free(line);
 }
 
-static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t)
+static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
 {
        int ret;
        struct i9e_client_info *ici = i9ep->ici;
@@ -369,7 +369,7 @@ out:
        return ret;
 }
 
-static void i9e_pre_select(struct sched *s, __a_unused struct task *t)
+static void i9e_pre_select(struct sched *s, __a_unused void *context)
 {
        int ret;
 
@@ -413,19 +413,17 @@ static void update_winsize(void)
        i9ep->empty_line[i9ep->num_columns] = '\0';
 }
 
-/**
- * Defined key sequences are mapped to keys starting with this offset. I.e.
- * pressing the first defined key sequence yields the key number \p KEY_OFFSET.
- */
-#define KEY_OFFSET 64
-
-static int dispatch_key(__a_unused int count, int key)
+static int dispatch_key(__a_unused int count, __a_unused int key)
 {
-       int ret;
+       int i, ret;
 
-       assert(key >= KEY_OFFSET);
-       ret = i9ep->ici->key_handler(key - KEY_OFFSET);
-       return ret < 0? ret : 0;
+       for (i = i9ep->num_key_bindings - 1; i >= 0; i--) {
+               if (strcmp(rl_executing_keyseq, i9ep->ici->bound_keyseqs[i]))
+                       continue;
+               ret = i9ep->ici->key_handler(i);
+               return ret < 0? ret : 0;
+       }
+       assert(0);
 }
 
 /**
@@ -437,7 +435,6 @@ static int dispatch_key(__a_unused int count, int key)
  * The caller must allocate and initialize the structure \a ici points to.
  *
  * \return Standard.
- * \sa \ref register_task().
  */
 int i9e_open(struct i9e_client_info *ici, struct sched *s)
 {
@@ -451,10 +448,13 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s)
        ret = mark_fd_nonblocking(ici->fds[1]);
        if (ret < 0)
                return ret;
-       i9ep->task.pre_select = i9e_pre_select;
-       i9ep->task.post_select = i9e_post_select;
-       sprintf(i9ep->task.status, "i9e");
-       register_task(s, &i9ep->task);
+       i9ep->task = task_register(&(struct task_info) {
+               .name = "i9e",
+               .pre_select = i9e_pre_select,
+               .post_select = i9e_post_select,
+               .context = i9ep,
+       }, s);
+
        rl_readline_name = "para_i9e";
        rl_basic_word_break_characters = " ";
        rl_attempted_completion_function = i9e_completer;
@@ -467,13 +467,11 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s)
        if (ici->bound_keyseqs) {
                char *seq;
                int i;
-               /* FIXME: This is an arbitrary constant.  */
-               for (i = 0; i < 32 && (seq = ici->bound_keyseqs[i]); i++) {
-                       char buf[2] = {KEY_OFFSET + i, '\0'};
-                       /* readline needs an allocated buffer for the macro */
-                       rl_generic_bind(ISMACR, seq, para_strdup(buf), i9ep->bare_km);
-                       rl_bind_key_in_map(KEY_OFFSET + i, dispatch_key, i9ep->bare_km);
-               }
+               /* bind each key sequence to the our dispatcher */
+               for (i = 0; (seq = ici->bound_keyseqs[i]); i++)
+                       rl_generic_bind(ISFUNC, seq, (char *)dispatch_key,
+                               i9ep->bare_km);
+               i9ep->num_key_bindings = i;
        }
        if (ici->history_file)
                read_history(ici->history_file);
@@ -481,7 +479,6 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s)
        if (ici->producer) {
                rl_callback_handler_install("", i9e_line_handler);
                i9e_attach_to_stdout(ici->producer);
-               rl_set_keymap(i9ep->bare_km);
        } else
                rl_callback_handler_install(i9ep->ici->prompt, i9e_line_handler);
        return 1;
@@ -558,8 +555,6 @@ void ie9_print_status_bar(char *buf, unsigned len)
  * Tell i9e that the caller received a signal.
  *
  * \param sig_num The number of the signal received.
- *
- * Currently the function only cares about \p SIGINT, but this may change.
  */
 void i9e_signal_dispatch(int sig_num)
 {
index 1d6b36aa1a57e87e2c61283a21627e0ab2271064..e6d53dee81ec13853a403cbfbedbf68b43accb2d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/ipc.c b/ipc.c
index 1ec53c30204e336a9b75a1992d063910c618e80d..9488224a1d4a622860adc957b27977e2bcbfc7e2 100644 (file)
--- a/ipc.c
+++ b/ipc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -209,6 +209,7 @@ size_t shm_get_shmmax(void)
                                buf[ret] = '\0';
                                shmmax = strtoul(buf, NULL, 10);
                        }
+                       close(fd);
                }
        }
 #elif defined SYSCTL_SHMMAX_VARIABLE
diff --git a/list.h b/list.h
index e4b5a1f67b066b73d2a89e0b2e9a7cd6eac00a66..66c6d91525031261ff94cd3028e672cb340c06cd 100644 (file)
--- a/list.h
+++ b/list.h
@@ -156,17 +156,6 @@ static inline int list_empty(const struct list_head *head)
 #define list_entry(ptr, type, member) \
        container_of(ptr, type, member)
 
-/**
- * iterate over a list safe against removal of list entry
- *
- * \param pos the &struct list_head to use as a loop counter.
- * \param n another &struct list_head to use as temporary storage
- * \param head the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
-       for (pos = (head)->next, n = pos->next; pos != (head); \
-               pos = n, n = pos->next)
-
 /**
  * iterate over list of given type
  *
index f8e29fea8693af08506fb6b91210010d722994ac..9b8a650326069df5ad449d617f44449a0bf5156c 100644 (file)
@@ -1,12 +1,17 @@
 args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
 
-purpose "Print information about audio file(s)."
+purpose "Print information about audio file(s)"
 
 include(header.m4)
 include(loglevel.m4)
 
 <qu>
-option "chunk_table" c
+
+###################################
+section "printing meta information"
+###################################
+
+option "chunk-table" c
 #~~~~~~~~~~~~~~~~~~~~~
 "print also the chunk table"
 flag off
@@ -27,11 +32,68 @@ option "parser-friendly" p
 flag off
 details = "
        Currently this option only affects the format of the chunk table,
-       so it has no effect if --chunk_table is not given.
+       so it has no effect if --chunk-table is not given.
 
        The human-readable output (the default) consists of one output
        line per chunk and the output contains also the chunk number,
        the duration and the size of each chunk. The parser-friendly
        output prints only the offsets, in one line.
 "
+
+#############################
+section "modifying meta tags"
+#############################
+
+option "modify" m
+#~~~~~~~~~~~~~~~~
+"modify (rather than print) tags"
+flag off
+details = "
+       When this option is given, para_afh creates the result file
+       as a temporary copy of the given file(s), but with meta
+       tags changed according to the options below. On errors,
+       the temporary file is removed, leaving the original file
+       unchanged. On success, if --backup is given, the original
+       file is moved away. Finally the temporary file is renamed to
+       the name of the original file.
+"
+
+option "backup" b
+"create backup of the original file"
+flag off
+details = "
+       The backup suffix is '~', i.e. a single tilde character is appended
+       to the given file name.
+"
+
+option "year" y
+#~~~~~~~~~~~~~~
+"set the year tag"
+string typestr="year"
+optional
+
+option "title" t
+#~~~~~~~~~~~~~~~
+"set the title tag"
+string typestr="title"
+optional
+
+option "artist" a
+#~~~~~~~~~~~~~~~~
+"set the artist/author tag"
+string typestr="artist"
+optional
+
+option "album" A
+#~~~~~~~~~~~~~~~
+"set the album tag"
+string typestr="album"
+optional
+
+option "comment" C
+#~~~~~~~~~~~~~~~~~
+"set the comment tag"
+string typestr="comment"
+optional
+
 </qu>
index f4da9d992c8ffc078710a95e182a764433e9ddf8..28a0a9ea09ef7204256beaf254f5d0e200653966 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Make an audio stream from a local file."
+purpose "Make an audio stream from a local file"
 
 description "
        The afh (audio format handler) receiver can be used to write
@@ -22,19 +22,19 @@ required
 
 option "begin-chunk" b
 #~~~~~~~~~~~~~~~~~~~~~
-"skip a number of chunks"
+"skip the beginning of the file"
 int typestr = "chunk_num"
 default = "0"
 optional
 details = "
        The chunk_num argument must be between -num_chunks and
-       num_chunks - 1 inclusively where num_chunks is the total number
-       of chunks which is printed when using the --info option. If
-       chunk_num is negative, the given number of chunks are counted
-       backwards from the end of the file. For example --begin_chunk
-       -100 instructs para_afh to start output at chunk num_chunks
-       - 100. This is mainly useful for cutting off the end of an
-       audio file.
+       num_chunks - 1, inclusively, where num_chunks is the total
+       number of chunks of the audio file given by the argument to
+       --filename. If chunk_num is negative, the given number of
+       chunks are counted backwards from the end of the file. For
+       example --begin-chunk -100 instructs the afh receiver to
+       start output at chunk num_chunks - 100. This is useful for
+       selecting the last part of an audio file.
 "
 
 option "end-chunk" e
@@ -43,7 +43,7 @@ option "end-chunk" e
 int typestr = "chunk_num"
 optional
 details = "
-       For the chunk_num argument the same rules as for --begin_chunk
+       For the chunk_num argument the same rules as for --begin-chunk
        apply. The default is to write up to the last chunk.
 "
 
index 04b4963d3a9fbe6bc342ffa808ae6f33a8a168fa..b2c56218dcd6f6a2e41646fafebb883c74b09089 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Native ALSA output plugin."
+purpose "Native ALSA output plugin"
 
 include(header.m4)
 
@@ -8,11 +8,29 @@ include(header.m4)
 option "device" d
 #~~~~~~~~~~~~~~~~
 "set PCM device"
-string typestr="device"
-default="default"
+string typestr = "device"
+default = "default"
 optional
-details="
-       On systems with dmix, a better choice than the default
-       value might be to use \"plug:swmix\".
+details = "
+       Check for the presence of a /proc/asound/ directory to see if
+       ALSA is present in your kernel. The file /proc/asound/devices
+       contains all devices ALSA knows about.
 "
+
+option "buffer-time" B
+#~~~~~~~~~~~~~~~~~~~~~
+"duration of the ALSA buffer"
+int typestr = "milliseconds"
+default = "170"
+optional
+details = "
+       This is only a hint as ALSA might pick a slightly different
+       time, depending on the sound hardware. The chosen value is
+       shown in debug output as BUFFER_TIME.
+
+       If synchronization between multiple clients is desired,
+       the same buffer time should be configured for all clients.
+"
+
 </qu>
+
index 2d4d4ce6ca5166355fa99bdbf28884a2a213daff..a02cc5b73f8e2bf85a94090d3b781b4f85efe827 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Amplify the decoded audio stream."
+purpose "Amplify the decoded audio stream"
 
 option "amp" a
 #~~~~~~~~~~~~~
index ccee4ee62f5fc23903857f9ba29e0b470792548a..29112d711694b239c82dee218ca88745a3f38dc2 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Output plugin for libao."
+purpose "Output plugin for libao"
 
 include(header.m4)
 <qu>
index 36520bfa0f16888020d431d7001c40c17c00146a..f216204f78330449c95bd09acfe35d1d1ded252f 100644 (file)
@@ -1,6 +1,6 @@
 args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
 
-purpose "Communicate with para_audiod through a local socket."
+purpose "Communicate with para_audiod through a local socket"
 
 include(header.m4)
 <qu>
index 7bae3435d19f20ed0a4f496b947cd1195e17bad5..75c045817898e8b840dc4734cd547ad282e67580 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-handle-help --no-handle-version --conf-parser"
 
-purpose "Connect to para_server, receive, decode and play audio streams."
+purpose "Connect to para_server, receive, decode and play audio streams"
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_audiod)
@@ -20,6 +20,7 @@ include(log_timing.m4)
 include(daemon.m4)
 include(user.m4)
 include(group.m4)
+include(priority.m4)
 
 <qu>
 ########################
@@ -80,25 +81,26 @@ details="
        instructed to use also \"filename\" for connecting para_audiod.
 "
 
-option "user_allow" -
+option "user-allow" -
 #~~~~~~~~~~~~~~~~~~~~
-"allow this uid"
-int typestr="uid"
-default="-1"
+"allow this user to connect to audiod"
+string typestr = "username"
 optional
 multiple
-details="
-       Allow the user identified by \"uid\" to connect to para_audiod.
-       May be specified multiple times. If not specified at all,
-       all users are allowed to connect.
-
-       This feature requires unix socket credentials and is currently
-       only supported on Linux systems. On other operating systems,
-       the option is silently ignored and all local users are allowed
-       to connect to para_audiod.
+details = "
+       Allow the user identified by username (either a string or
+       a UID) to connect to para_audiod. This option may be given
+       multiple times. If not specified at all, all users are allowed
+       to connect.
+
+       This feature is based on the ability to send unix
+       credentials through local sockets using ancillary data
+       (SCM_CREDENTIALS). Currently it only works on Linux. On
+       other operating systems the option is silently ignored and
+       all local users are allowed to connect.
 "
 
-option "clock_diff_count" -
+option "clock-diff-count" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "sync clock on startup"
 int typestr="count"
@@ -195,7 +197,7 @@ details="
 
 "
 
-option "stream_delay" -
+option "stream-delay" -
 #~~~~~~~~~~~~~~~~~~~~~~
 "time for client sync"
 int typestr="milliseconds"
index f0bbc0bd8eadf2000e69f2f95e41b22c146f4080..a5a27a05013be9224403388902df078da5e182f2 100644 (file)
@@ -1,16 +1,43 @@
 args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version --no-handle-help"
 
-purpose "Communicate with para_server through the paraslash control port."
+purpose "Communicate with para_server through the paraslash control port"
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_client)
 define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
 define(DEFAULT_HISTORY_FILE,~/.paraslash/client.history)
 <qu>
-option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional
-option "user" u "paraslash username" string typestr="username" default="<current user>" optional
-option "server_port" p "port to connect" int typestr="port" default="2990" optional
-option "key_file" k "(default='~/.paraslash/key.<user>')" string typestr="filename" optional
+option "hostname" i
+#~~~~~~~~~~~~~~~~~~
+"ip or host to connect"
+string typestr = "host"
+default = "localhost"
+optional
+
+option "user" u
+#~~~~~~~~~~~~~~
+"paraslash username"
+string typestr = "username"
+default = "<current user>"
+optional
+
+option "server-port" p
+#~~~~~~~~~~~~~~~~~~~~~
+"port to connect"
+int typestr = "port"
+default = "2990"
+optional
+
+option "key-file" k
+#~~~~~~~~~~~~~~~~~~
+"path to private key"
+string typestr = "filename"
+optional
+details = "
+       If not given, the following files are tried, in order:
+       $HOME/.paraslash/key.$LOGNAME, $HOME/.ssh/id_rsa. It is a fatal
+       error if the key file can not be opened, or is world-readable.
+"
 </qu>
 
 include(loglevel.m4)
index 63e996f35b1683020dfd2c577c67966c53a6b71d..eb08178698d9b4cefa27103599b0c3163694b6ff 100644 (file)
@@ -8,7 +8,7 @@ values = "yes","no","auto"
 default = "auto"
 optional
 
-option "log_color" -
+option "log-color" -
 #~~~~~~~~~~~~~~~~~~~
 "select a color for one type of log message"
 string typestr="color_spec"
@@ -26,9 +26,9 @@ details="
 
        Examples:
 
-               --log_color \"debug:green\"
-               --log_color \"info:yellow bold\"
-               --log_color \"notice:white red bold\"
+               --log-color \"debug:green\"
+               --log-color \"info:yellow bold\"
+               --log-color \"notice:white red bold\"
 "
 
 </qu>
index 8c701a03987fe6c8e21c25a40475483b6d0bd7f7..501f46ccfd8f0730787c37e5290fa0b19dfa71a9 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Dynamically adjust the volume of an audio stream."
+purpose "Dynamically adjust the volume of an audio stream"
 
 option "blocksize" b
 #~~~~~~~~~~~~~~~~~~~
@@ -26,7 +26,7 @@ option "inertia" i
 default="6"
 optional
 
-option "target_level" t
+option "target-level" t
 #~~~~~~~~~~~~~~~~~~~~~~
 "target signal level (0-32768)"
 int typestr="number"
index 318ba7a68101350630f04fec7af22716dabe6fc4..29f66b4440be2f238b66ead0bfed73a7d74785f7 100644 (file)
@@ -1,5 +1,5 @@
 <qu>
-option "config_file" c
+option "config-file" c
 #~~~~~~~~~~~~~~~~~~~~~
 "(default='</qu>DEFAULT_CONFIG_FILE<qu>')"
 string typestr="filename"
index b4e842ac049f4832e21e96a24658fab162f22a83..ebead6a806f372d63e0e5aaf2f4b4a1aba00f479 100644 (file)
@@ -3,9 +3,8 @@ option "daemon" d
 #~~~~~~~~~~~~~~~~
 "run as background daemon"
 flag off
-dependon="logfile"
-details="
-       Note that </qu>CURRENT_PROGRAM<qu> refuses to start in daemon mode if no
-       logfile was specified.
+details = "
+       If this option is given and no logfile was specified, all
+       messages go to /dev/null.
 "
 </qu>
index f8191fe003bec99957155d71c8ef5a5be694376b..1ba3fb59e79fb462e67a97b926816f1c7885a84e 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Receive a DCCP audio stream."
+purpose "Receive a DCCP audio stream"
 
 option "host" i
 "ip or host"
index 7c731c2b6ec1cdf15322131827fe99add0917e09..59389ffefb5105ee8af56e4d7b844377c9d5d9c7 100644 (file)
@@ -1,6 +1,6 @@
 args "--conf-parser --no-handle-version --no-handle-help"
 
-purpose "An alarm clock and volume-fader for OSS and ALSA."
+purpose "An alarm clock and volume-fader for OSS and ALSA"
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_fade)
@@ -17,11 +17,11 @@ option "mode" o
 #~~~~~~~~~~~~~~
 "how to fade volume"
        enum typestr = "mode"
-       values = "sleep", "snooze", "fade"
+       values = "sleep", "fade", "set", "snooze"
        default = "sleep"
        optional
        details="
-               para_fade knows three different fading modes:
+               para_fade knows the following modes:
 
                sleep mode: Change to the initial volume and select
                the initial afs mood/playlist. Then fade out until
@@ -33,6 +33,8 @@ option "mode" o
                fade: Fade the volume to the given value in the
                given time.
 
+               set: Just set the value and exit.
+
                snooze: Fade out, sleep a bit and fade in.
 "
 
@@ -86,12 +88,15 @@ section "Options for sleep mode"
 option "ivol" -
 #~~~~~~~~~~~~~~
 "set initial volume"
-       int typestr = "volume"
+       string typestr = "[channel:]volume"
        default = "60"
        optional
+       multiple
        details = "
                Used as the start volume, before fading out to the
-               fade out volume.
+               fade out volume. The channel part may be omitted, in
+               which case the default channel is used. This option
+               may be given multiple times.
        "
 
 option "fo-mood" -
@@ -228,4 +233,14 @@ option "fade-time" t
        int typestr = "seconds"
        default = "5"
        optional
+
+section "Options for set mode"
+##############################
+
+option "val" -
+"value to set"
+       int typestr = "value"
+       default = "0"
+       optional
+
 </qu>
index 045b8657236b57ed58b8ececdd8cd183481acb53..4f98884f4bad2c5828f420068e103d14d3923dec 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Output plugin that writes to a local file."
+purpose "Output plugin that writes to a local file"
 
 option "filename" f
 #~~~~~~~~~~~~~~~~~~
index baf6ecdb974a72ec7ceb8058d13e2264e1b448ad..b8b49f622600d749a2b1c7dccb1fe2b0f2b7c16a 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-handle-help --no-handle-version --conf-parser"
 
-purpose "Decode or process audio data from STDIN to STDOUT."
+purpose "Decode or process audio data from STDIN to STDOUT"
 
 include(header.m4)
 include(loglevel.m4)
index 1f5cf6e425bc506e8d7eae143a2b3a7319dd4a5e..8e21356f3bb362b3aecccd6f4d19bbaea58c7ae2 100644 (file)
@@ -1,6 +1,6 @@
 args "--conf-parser --no-handle-version --no-handle-help"
 
-purpose "Show para_audiod status in a curses window."
+purpose "Show para_audiod status in a curses window"
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_gui)
@@ -34,7 +34,7 @@ details = "
        available themes is printed and the program terminates.
 "
 
-option "stat_cmd" s
+option "stat-cmd" s
 #~~~~~~~~~~~~~~~~~~
 "command to read status items from"
 string typestr = "command"
@@ -56,7 +56,7 @@ details = "
 section "Mapping keys to commands"
 #---------------------------------
 
-option "key_map" k
+option "key-map" k
 #~~~~~~~~~~~~~~~~~
 "Map key k to command c using mode m."
 
index 932d88bf06825cf0ba92bd21988d5944b1c93cb4..73e98a7836c8bf1989775b5206617717519dc247 100644 (file)
@@ -1,5 +1,5 @@
 <qu>
-option "history_file" -
+option "history-file" -
 #~~~~~~~~~~~~~~~~~~~~~~
 "(default='</qu>DEFAULT_HISTORY_FILE<qu>')"
 string typestr = "filename"
index b8ece39d9809a85dfa4e3e0d7b09afefd2475954..6db3ff0444fdc7feedd067c79292d8b7abc00bb1 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Receive an HTTP audio stream."
+purpose "Receive an HTTP audio stream"
 
 include(header.m4)
 
index bb1029393151af0510dc1a5edc1d9e6b49ef99f1..070d736bda96ae739aa4cbe2dc26be154d9a6a35 100644 (file)
@@ -6,6 +6,6 @@ string typestr="filename"
 optional
 details="
        If this option is not given, </qu>CURRENT_PROGRAM<qu> writes the log
-       messages to to stderr
+       messages to stderr.
 "
 </qu>
index 548a3d5d83bcb406a66dc26a77c0a840745d868f..5130ddd1d7779790250ac52eb2e263503a1002c7 100644 (file)
@@ -1,6 +1,6 @@
 define ggo_opts
        --output-dir=$(cmdline_dir) \
-       --set-version="$(GIT_VERSION) ($(codename))" \
+       --set-version="$(GIT_VERSION)" \
        --arg-struct-name=$(*F)_args_info \
        --file-name=$(*F).cmdline \
        --func-name=$(*F)_cmdline_parser \
@@ -13,40 +13,14 @@ $(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdl
        @[ -z "$(Q)" ] || echo 'GGO $<'
        $(Q) $(GENGETOPT) $(ggo_opts) < $<
 ifeq ($(ggo_descriptions_declared),no)
-       echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h
+       @echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h
 endif
 
-$(ggo_dir)/server.ggo $(ggo_dir)/audiod.ggo: \
-       $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/color.m4 \
-       $(m4_ggo_dir)/config_file.m4 $(m4_ggo_dir)/logfile.m4 \
-       $(m4_ggo_dir)/daemon.m4 $(m4_ggo_dir)/user.m4 \
-       $(m4_ggo_dir)/group.m4 $(m4_ggo_dir)/log_timing.m4 \
-       $(m4_ggo_dir)/config_file.m4
-
-$(ggo_dir)/afh.ggo: $(m4_ggo_dir)/loglevel.m4
-$(ggo_dir)/audioc.ggo: \
-       $(m4_ggo_dir)/loglevel.m4 \
-       $(m4_ggo_dir)/history_file.m4 \
-       $(m4_ggo_dir)/complete.m4
-$(ggo_dir)/filter.ggo: $(m4_ggo_dir)/loglevel.m4
-$(ggo_dir)/fsck.ggo: $(m4_ggo_dir)/loglevel.m4
-$(ggo_dir)/gui.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4
-$(ggo_dir)/recv.ggo: $(m4_ggo_dir)/loglevel.m4
-$(ggo_dir)/write.ggo: $(m4_ggo_dir)/loglevel.m4 \
-       $(m4_ggo_dir)/channels.m4 \
-       $(m4_ggo_dir)/sample_rate.m4 \
-       $(m4_ggo_dir)/sample_format.m4
-$(ggo_dir)/client.ggo: \
-       $(m4_ggo_dir)/loglevel.m4 \
-       $(m4_ggo_dir)/config_file.m4 \
-       $(m4_ggo_dir)/history_file.m4 \
-       $(m4_ggo_dir)/complete.m4
-$(ggo_dir)/fade.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4
-$(ggo_dir)/resample_filter.ggo: \
-       $(m4_ggo_dir)/channels.m4 \
-       $(m4_ggo_dir)/sample_rate.m4 \
-       $(m4_ggo_dir)/sample_format.m4
-$(ggo_dir)/play.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4
+$(m4depdir)/%.m4d: $(m4_ggo_dir)/%.m4 | $(m4depdir)
+       @[ -z "$(Q)" ] || echo 'M4D $<'
+       $(Q) m4 -I $(m4_ggo_dir) -s $< \
+       | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
+       print "$(ggo_dir)/$(*F).ggo: " $$3}}' | sort | uniq > $@
 
 $(ggo_dir)/%.ggo: $(m4_ggo_dir)/%.m4 $(m4_ggo_dir)/header.m4 | $(ggo_dir)
        @[ -z "$(Q)" ] || echo 'M4 $<'
index a6425b3e7f296485101c52e8dc5fa72a9acac355..8b187835d1ecd7d8b109244a9e5efc1b38c2936e 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Decode an mp3 stream."
+purpose "Decode an mp3 stream"
 
 include(header.m4)
 
index 352bea5bc3cfe2e0d2d431c2e4b7c0fb1f31f60f..578d81387ec3562312572ec218b4aa4cf9a234cc 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Output plugin for the Open Sound System."
+purpose "Output plugin for the Open Sound System"
 
 option "device" d
 #~~~~~~~~~~~~~~~~
index 5add6f2e6e340f5eacd4260d33e4166522751261..83ed737ca91168156cb3c3a05efea655e8ceaabd 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Output plugin for Mac OS coreaudio."
+purpose "Output plugin for Mac OS coreaudio"
 
 section "osx options"
 #####################
index ea41f562fe504e00d0be096b83f174d4dc41129d..168755907fd302f8eb5f95c5484f04df210f95a9 100644 (file)
@@ -1,6 +1,6 @@
 args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help"
 
-purpose "Command line audio player."
+purpose "Command line audio player"
 
 description "para_play operates either in command mode or in insert
 mode. In insert mode it presents a prompt and allows to enter commands
@@ -33,7 +33,7 @@ option "randomize" z
 "randomize playlist at startup."
 flag off
 
-option "key_map" k
+option "key-map" k
 #~~~~~~~~~~~~~~~~~
 "Map key k to a command."
 
index 9e84dcfcea04934a7f5a2f3f776d5afabe0b6bc1..20ff86fe4b332038e497313540a2cea515f486a4 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Delay processing of an audio stream."
+purpose "Delay processing of an audio stream"
 
 option "duration" d
 #~~~~~~~~~~~~~~~~~~
diff --git a/m4/gengetopt/priority.m4 b/m4/gengetopt/priority.m4
new file mode 100644 (file)
index 0000000..0b37dc0
--- /dev/null
@@ -0,0 +1,16 @@
+option "priority" -
+#~~~~~~~~~~~~~~~~~~
+"adjust scheduling priority"
+int typestr = "prio"
+default = "0"
+optional
+details = "
+       The priority (also known as nice value) is a value in the range -20
+       to 19. Lower priorities cause more favorable scheduling. Since only
+       privileged processes may request a negative priority, specifying
+       a negative value works only if the daemon is started with root
+       privileges.
+
+       Failure to set the given priority value is not considered an error
+       but a log message is printed in this case.
+"
index 086c9c0a5c73049630bda211a8433ddfebba3963..9a9a88046f66b8c31fc23c670df730539525912d 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-handle-help --no-handle-version"
 
-purpose "A command line HTTP/DCCP/UDP stream grabber."
+purpose "A command line HTTP/DCCP/UDP stream grabber"
 
 include(header.m4)
 include(loglevel.m4)
index 4f4af4b0c41182d21faecf888926488bb372095e..2e5f40054edcfa9c9239139e1be3aaf8a8857597 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Transform raw audio to a different sample rate."
+purpose "Transform raw audio to a different sample rate"
 
 include(header.m4)
 
index 0a1003339c1044f675389379b83b01dd1be8c3b9..c998f9d6a7d0b192762a3402dd8b269e215d4e94 100644 (file)
@@ -7,7 +7,7 @@ values = "S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE" enum
 default = "S16_LE"
 optional
 details = "
-       It is only neccessary to specify this for raw audio. See the
+       It is only necessary to specify this for raw audio. See the
        discussion of the --channels option.
 "
 </qu>
index b3d107dbf2e40ca5f93862edde3ff18a8bd9243e..a8332a4017b2879da912ea8cd8a02a82f3b902af 100644 (file)
@@ -6,7 +6,7 @@ int typestr = "num"
 default = "44100"
 optional
 details = "
-       It is only neccessary to specify this for raw audio. See the
+       It is only necessary to specify this for raw audio. See the
        discussion of the --channels option.
 "
 </qu>
index 8707554f7719ace121dae2701e179c22ab687184..6aca7bac7f41ddb9a37d4ec96e329a210e275f1f 100644 (file)
@@ -1,6 +1,6 @@
 args "--conf-parser --no-handle-version --no-handle-help"
 
-purpose "Manage and stream audio files."
+purpose "Manage and stream audio files"
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_server)
@@ -18,6 +18,7 @@ include(color.m4)
 include(daemon.m4)
 include(user.m4)
 include(group.m4)
+include(priority.m4)
 
 <qu>
 option "port" p
@@ -42,7 +43,7 @@ include(logfile.m4)
 include(config_file.m4)
 
 <qu>
-option "user_list" -
+option "user-list" -
 #~~~~~~~~~~~~~~~~~~~
 "(default='~/.paraslash/server.users')"
 
@@ -60,7 +61,7 @@ option "autoplay" a
 "start playing on startup"
 flag off
 
-option "autoplay_delay" -
+option "autoplay-delay" -
 #~~~~~~~~~~~~~~~~~~~~~~~~
 "time to wait before streaming"
 int typestr="ms"
@@ -76,7 +77,7 @@ details="
        already up when para_server starts to stream. Of course, this
        option depends on the autoplay option.
 "
-option "announce_time" A
+option "announce-time" A
 #~~~~~~~~~~~~~~~~~~~~~~~
 "grace time for clients"
 
@@ -95,7 +96,7 @@ details="
 section "audio file selector"
 #############################
 
-option "afs_database_dir" D
+option "afs-database-dir" D
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "location of the database"
 string typestr="path"
@@ -105,7 +106,7 @@ details="
        file selector. The default is '~/.paraslash/afs_database-0.4'.
 "
 
-option "afs_socket" s
+option "afs-socket" s
 #~~~~~~~~~~~~~~~~~~~~
 "Command socket for afs"
 string typestr="path"
@@ -117,7 +118,7 @@ details="
        audio file selector via a local socket. This option specifies
        the location of that socket in the file system.
 "
-option "afs_initial_mode" i
+option "afs-initial-mode" i
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 "Mood or playlist to load on startup."
@@ -128,7 +129,7 @@ details="
        The argument of this option must be prefixed with either 'p/'
        or 'm/' to indicate whether a playlist or a mood should be
        loaded. Example:
-               --afs_initial_mode p/foo
+               --afs-initial-mode p/foo
        loads the playlist named 'foo'.
 "
 
@@ -137,7 +138,7 @@ section "http sender"
 #####################
 
 
-option "http_port" -
+option "http-port" -
 #~~~~~~~~~~~~~~~~~~~
 "tcp port for http streaming"
 int typestr="portnumber"
@@ -149,20 +150,20 @@ details="
        http request message such as 'GET / HTTP/'.
 "
 
-option "http_default_deny" -
+option "http-default-deny" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "make the http ACL a whitelist"
 flag off
 details="
        The default is to use blacklists instead, i.e. connections
        to the http sender are allowed unless the connecting host
-       matches a pattern given by a http_access option. This allows
+       matches a pattern given by a http-access option. This allows
        to use access control the other way round: Connections are
        denied from hosts which are not explicitly allowed by one or
-       more http_access options.
+       more http-access options.
 "
 
-option "http_access" -
+option "http-access" -
 #~~~~~~~~~~~~~~~~~~~~~
 "add an entry to the http ACL"
 string typestr="a.b.c.d/n"
@@ -170,13 +171,13 @@ optional
 multiple
 details="
        Add given host/network to access control list (whitelist if
-       http_default_deny was given, blacklist otherwise) before
+       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
 "
 
-option "http_no_autostart" -
+option "http-no-autostart" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "do not open tcp port on startup"
 flag off
@@ -186,7 +187,7 @@ details="
        later time by using the sender command.
 "
 
-option "http_max_clients" -
+option "http-max-clients" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "maximal number of connections"
 int typestr="number"
@@ -204,45 +205,45 @@ section "dccp sender"
 #####################
 
 
-option "dccp_port" -
+option "dccp-port" -
 #~~~~~~~~~~~~~~~~~~~
 "port for dccp streaming"
 int typestr="portnumber"
 default="8000"
 optional
 details="
-       See http_port for details.
+       See http-port for details.
 "
 
-option "dccp_default_deny" -
+option "dccp-default-deny" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "make the dccp ACL a whitelist"
 flag off
 details="
-       See http_default_deny for details.
+       See http-default-deny for details.
 "
 
-option "dccp_access" -
+option "dccp-access" -
 #~~~~~~~~~~~~~~~~~~~~~
 "add an entry to the dccp ACL"
 string typestr="a.b.c.d/n"
 optional
 multiple
 details="
-       See http_access for details.
+       See http-access for details.
 "
 
-option "dccp_max_clients" -
+option "dccp-max-clients" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "maximal number of connections"
 int typestr="number"
 default="-1"
 optional
 details="
-       See http_max_clients for details.
+       See http-max-clients for details.
 "
 
-option "dccp_max_slice_size" -
+option "dccp-max-slice-size" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "Upper bound for the FEC slice size"
 int typestr = "size"
@@ -261,7 +262,7 @@ details = "
        the MPS of an incoming connection can not be set.
 "
 
-option "dccp_data_slices_per_group" -
+option "dccp-data-slices-per-group" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "The number of non-redundant slices per FEC group"
 int typestr = "num"
@@ -270,13 +271,13 @@ default = "3"
 details = "
        This determines the number of slices in each FEC group that are
        necessary to decode the group. The given number must be smaller
-       than the value of the dccp_slices_per_group option below.
+       than the value of the dccp-slices-per-group option below.
 
        Note that the duration of a FEC group is proportional to the
-       product dccp_max_slice_size * dccp_data_slices_per_group.
+       product dccp-max-slice-size * dccp-data-slices-per-group.
 "
 
-option "dccp_slices_per_group" -
+option "dccp-slices-per-group" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "The total number of slices per FEC group"
 int typestr = "num"
@@ -284,7 +285,7 @@ optional
 default = "4"
 details = "
        This value must be larger than the value given for above
-       dccp_data_slices_per_group above. The difference being the
+       dccp-data-slices-per-group above. The difference being the
        number of redundant slices per group, i.e.  the number of
        data packets that may be lost without causing interruptions
        of the resulting audio stream.
@@ -296,7 +297,7 @@ details = "
 section "udp sender"
 ####################
 
-option "udp_target" -
+option "udp-target" -
 #~~~~~~~~~~~~~~~~~~~~
 "add udp target with optional port"
 string typestr="host[:port]"
@@ -306,7 +307,7 @@ details="
        Add given host/port to the list of targets. The 'host' argument
        can be either an IPv4/v6 address or hostname (RFC 3986 syntax).
        The 'port' argument is an optional port number. If the 'port'
-       part is absent, the 'udp_default_port' value is used.
+       part is absent, the 'udp-default-port' value is used.
 
        The following examples are possible targets:
        '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port);
@@ -317,7 +318,7 @@ details="
        This option can be given multiple times, for multiple targets.
 "
 
-option "udp_no_autostart" -
+option "udp-no-autostart" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "do not start sending"
 flag off
@@ -326,20 +327,20 @@ details="
        a later time by using the sender command.
 "
 
-option "udp_default_port" -
+option "udp-default-port" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "udp port to send to"
 int typestr="port"
 default="8000"
 optional
 
-option "udp_mcast_iface" -
+option "udp-mcast-iface" -
 #~~~~~~~~~~~~~~~~~~~~~~~~~~
 "outgoing udp multicast interface"
 string
 optional
 
-option "udp_header_interval" H
+option "udp-header-interval" H
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "duration for sending header"
 int typestr = "ms"
@@ -357,7 +358,7 @@ details = "
        audio formats, including mp3, don't need an audio file header.
 "
 
-option "udp_ttl" t
+option "udp-ttl" t
 #~~~~~~~~~~~~~~~~~
 "set time to live value"
 int typestr="num"
diff --git a/m4/gengetopt/sync_filter.m4 b/m4/gengetopt/sync_filter.m4
new file mode 100644 (file)
index 0000000..1e6f5f8
--- /dev/null
@@ -0,0 +1,45 @@
+args "--no-version --no-help"
+
+purpose "Synchronize playback between multiple clients."
+
+option "buddy" b
+#~~~~~~~~~~~~~~~
+"host to synchronize with"
+multiple
+string typestr = "url"
+optional
+details = "
+       This option may be given multiple times, one per buddy. Each
+       value may be given as a host, port pair in either IPv4 or
+       IPv6 form, with port being optional. If no port was specified
+       the listening port (as specified with --port, see below)
+       is used to send the synchronization packet to this buddy.
+"
+
+option "port" p
+#~~~~~~~~~~~~~~
+"UDP port for incoming synchronization packets"
+int typestr = "portnumber"
+default = "29900"
+optional
+details = "
+       The sync filter receives incoming synchronization packets on
+       this UDP port.
+"
+
+option "timeout" t
+#~~~~~~~~~~~~~~~~~
+"how long to wait for other clients"
+int typestr = "milliseconds"
+default = "2000"
+optional
+details = "
+       Once the sync filter receives its first chunk of input, a
+       synchronization period of the given number of milliseconds
+       begins. Playback is deferred until a synchronization packet
+       has been received from each defined buddy, or until the end
+       of the period. Buddies which did not send a synchronization
+       packet in time are temporarily disabled and are not waited for
+       during subsequent synchronization periods. They are re-enabled
+       automatically when another synchronization packet arrives.
+"
index 48770d974dc495f205a1538367511600fee97281..dcdad4f7b70143647ed40bc36e5c5cdefdc7975b 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-version --no-help"
 
-purpose "Receive an UDP audio stream."
+purpose "Receive an UDP audio stream"
 
 option "host" i
 "ip or host to receive udp packets from"
index 8b13f457a1480a3f430b31c478d5237ae9a0ba61..83e8bcabf4ccbfe642e39d8ab07eaa7371427943 100644 (file)
@@ -1,6 +1,6 @@
 args "--no-handle-help --no-handle-version"
 
-purpose "Play wav or raw audio."
+purpose "Play wav or raw audio"
 
 include(header.m4)
 include(loglevel.m4)
diff --git a/man_util.bash b/man_util.bash
new file mode 100755 (executable)
index 0000000..cb7519c
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+
+# Receivers, filters, writers are called "modules" in this script
+print_modhelp()
+{
+       local ggo="$1"
+
+       $GENGETOPT --show-detailed-help \
+               --set-version "" \
+               --set-package "" \
+               < "$ggo" | awk 'BEGIN {
+                       have_purpose=0
+                       have_usage=0
+               } {
+                       if (!have_purpose) {
+                               if ($0 ~ /^ *$/)
+                                       next
+                               printf(" (%s):", $0)
+                               have_purpose=1
+                               next
+                       }
+                       if (!have_usage) {
+                               if ($0 ~ /^Usage: /) {
+                                       have_usage=1
+                               }
+                               next
+                       }
+                       print $0
+               }'
+}
+
+make_help()
+{
+       local target="$1" module ggo
+
+       ggo="$GGO_DIR/$1.ggo"
+       $GENGETOPT --show-detailed-help \
+               --set-version "$VERSION" \
+               --set-package "para_$1" \
+               < "$ggo"
+
+       if [[ "$target" == 'recv' || "$target" == 'audiod' ]]; then
+               for module in $RECEIVERS; do
+                       ggo="$GGO_DIR/${module}_recv.ggo"
+                       [[ ! -f "$ggo" ]] && continue
+                       printf "\nOptions for the $module receiver"
+                       print_modhelp "$ggo"
+               done
+       fi
+       if [[ "$target" == 'filter' || "$target" == 'audiod' ]]; then
+               for module in $FILTERS; do
+                       ggo="$GGO_DIR/${module}_filter.ggo"
+                       [[ ! -f "$ggo" ]] && continue
+                       printf "\nOptions for the $module filter"
+                       print_modhelp "$ggo"
+               done
+       fi
+       if [[ "$target" == 'write' || "$target" == 'audiod' ]]; then
+               for module in $WRITERS; do
+                       ggo="$GGO_DIR/${module}_write.ggo"
+                       [[ ! -f "$ggo" ]] && continue
+                       printf "\nOptions for the $module writer"
+                       print_modhelp "$ggo"
+               done
+       fi
+}
+
+set -u
+
+(($# != 1)) && exit 1
+
+# These must be set by the caller (make or help2man)
+export COMMAND_LISTS FILTERS GENGETOPT GGO_DIR HELP2MAN HELP2MAN_DIR \
+       RECEIVERS VERSION WRITERS
+
+# If either --version or --help-xxx was given, we are being called by help2man
+if [[ "$1" == "--version" ]]; then
+       echo "$VERSION"
+       exit $?
+fi
+if [[ "$1" =~ --help- ]]; then
+       make_help "${1#--help-}"
+       exit $?
+fi
+
+# Called by make, run help2man
+output_file="$1"
+target="${output_file##*/para_}"
+target="${target%.*}" # server, audiod, filter, ...
+link="$HELP2MAN_DIR/para_$target"
+
+cl_opts=
+for cl in $COMMAND_LISTS; do
+       cl_opts+=" --include $cl"
+done
+
+# Create a symlink para_$target, pointing to this script. This hack is
+# necessary because help2man always includes the name of the executable in its
+# output.
+ln -sf "$PWD/$0" "$link"
+
+# This will call us again twice, with either --help-$target or --version given.
+$HELP2MAN --no-info --help-option "--help-$target" $cl_opts \
+       "$link" > "$output_file"
+if (($? != 0)); then
+       rm -f "$output_file"
+       exit 1
+fi
diff --git a/mix.h b/mix.h
index 427a84f559c8f67369c24e9f4910f9cccdb83e8a..60c4392fabab76bb58ad1c2ee0ee211d52469ad7 100644 (file)
--- a/mix.h
+++ b/mix.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -31,6 +31,6 @@ struct mixer {
        int (*get)(struct mixer_handle *handle);
        /** Change the value of the selected channel. */
        int (*set)(struct mixer_handle *handle, int val);
-       /** Free all ressources associated with the given handle. */
+       /** Free all resources associated with the given handle. */
        void (*close)(struct mixer_handle **handle);
 };
diff --git a/mm.c b/mm.c
index 5ce3476eca25fc8f5304ae81d04c1f526c1b199a..d6f3573ce438564f0edbc4c0aafa1a9642470f3d 100644 (file)
--- a/mm.c
+++ b/mm.c
@@ -1,12 +1,11 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file mm.c Paraslash's mood methods. */
 
-#include <time.h>
 #include <regex.h>
 #include <fnmatch.h>
 #include <osl.h>
@@ -18,6 +17,7 @@
 #include "afs.h"
 #include "mm.h"
 
+/** The comparators for numeric mood methods (year, bitrate, ...). */
 #define MOOD_COMPARATORS \
        MC(LESS, <) \
        MC(LESS_OR_EQUAL, <=) \
        MC(GREATER, >) \
        MC(GREATER_OR_EQUAL, >=) \
 
+/** Prefix mood comparator name with "_MC", example: MC_LESS. */
 #define MC(a, b) MC_ ## a,
+/** Each mood comparator is identified by an integer of this type. */
 enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
 #undef MC
+/** Stringfied mood comparator, example: "<". */
 #define MC(a, b) # b,
+/** Array of mood comparators represented as C strings ("<", "<=", ...). */
 static const char *mood_comparators[] = {MOOD_COMPARATORS};
 #undef MC
 
@@ -204,7 +208,7 @@ struct mm_year_data {
 
 static int mm_year_parser(int argc, char **argv, void **private)
 {
-       int ret = -E_MOOD_SYNTAX;
+       int ret;
        struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
        time_t current_time;
        struct tm *gmt;
diff --git a/mm.h b/mm.h
index b635a5a841597a4d56c389c1aee4063c92be0f12..2caace7f84220c23119ccb7d1bd7140212086a6a 100644 (file)
--- a/mm.h
+++ b/mm.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/mood.c b/mood.c
index 45b051a7267cdd9cd1a9049a00685a150681a1cb..daa8196ad1a05fc9a7aa3d0ad648fdcaddf56fb0 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -18,6 +18,7 @@
 #include "ipc.h"
 #include "mm.h"
 #include "sideband.h"
+#include "mood.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -29,7 +30,7 @@ struct afs_statistics {
        int64_t num_played_sum;
        /** Sum of last played times over all admissible files. */
        int64_t last_played_sum;
-       /** Quadratic deviation of num played time. */
+       /** Quadratic deviation of num played count. */
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
@@ -409,20 +410,18 @@ static int check_mood(struct osl_row *mood_row, void *data)
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "failed to get mood definition: %s\n",
-                       para_strerror(-ret));
+               para_printf(pb, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       ret = para_printf(pb, "checking mood %s...\n", mood_name);
-       if (ret < 0)
-               goto out;
+       para_printf(pb, "checking mood %s...\n", mood_name);
        ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        if (ret < 0)
-               para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
-                       para_strerror(-ret));
+               para_printf(pb, "mood %s: error in line %u: %s\n", mood_name,
+                       mlpd.line_num, para_strerror(-ret));
+       ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
        return ret;
@@ -431,42 +430,17 @@ out:
 /**
  * Check all moods for syntax errors.
  *
- * \param fd The afs socket.
- * \param query Unused.
+ * \param aca Only ->pbout is used for diagnostics.
+ *
+ * \return Negative on fatal errors. Inconsistent mood definitions are not
+ * considered an error.
  */
-void mood_check_callback(int fd, __a_unused const struct osl_object *query)
-{
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler
-       };
-
-       int ret = para_printf(&pb, "checking moods...\n");
-       if (ret < 0)
-               return;
-       osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
-               check_mood);
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
-}
-
-#if 0
-static unsigned int_log2(uint64_t x)
+int mood_check_callback(struct afs_callback_arg *aca)
 {
-       unsigned res = 0;
-
-       while (x) {
-               x /= 2;
-               res++;
-       }
-       return res;
+       para_printf(&aca->pbout, "checking moods...\n");
+       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
+               check_mood));
 }
-#endif
 
 static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
 {
@@ -475,31 +449,13 @@ static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
        return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
 }
 
-static long compute_num_played_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi, long mood_score)
 {
-       return -normalized_value(afsi->num_played, statistics.num,
+       mood_score -= normalized_value(afsi->num_played, statistics.num,
                statistics.num_played_sum, statistics.num_played_qd);
-}
-
-static long compute_last_played_score(struct afs_info *afsi)
-{
-       return -normalized_value(afsi->last_played, statistics.num,
+       mood_score -= normalized_value(afsi->last_played, statistics.num,
                statistics.last_played_sum, statistics.last_played_qd);
-}
-
-static long compute_dynamic_score(const struct osl_row *aft_row)
-{
-       struct afs_info afsi;
-       int64_t score, nscore = 0, lscore = 0;
-       int ret;
-
-       ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret < 0)
-               return -100;
-       nscore = compute_num_played_score(&afsi);
-       lscore = compute_last_played_score(&afsi);
-       score = nscore + lscore;
-       return score;
+       return mood_score / 3;
 }
 
 static int add_afs_statistics(const struct osl_row *row)
@@ -569,16 +525,16 @@ static int del_afs_statistics(const struct osl_row *row)
 /**
  * Structure used during mood_open().
  *
- * At mood open time, we look at each file in the audio file table in order to
- * determine whether it is admissible. If a file happens to be admissible, its
- * mood score is computed by calling each relevant mood_score_function. Next,
- * we update the afs_statistics and add a struct admissible_file_info to a
- * temporary array.
+ * At mood open time we determine the set of admissible files for the given
+ * mood. The mood score of each admissible file is computed by adding up all
+ * mood item scores. Next, we update the afs statistics and append a struct
+ * admissible_file_info to a temporary array.
  *
- * If all files have been processed that way, the final score of each
+ * When all files have been processed in this way, the final score of each
  * admissible file is computed by adding the dynamic score (which depends on
- * the afs_statistics) to the mood score.  Finally, all audio files in the
- * array are added to the score table and the admissible array is freed.
+ * the afs_statistics and the current time) to the mood score. Finally, all
+ * audio files in the temporary array are added to the score table and the
+ * array is freed.
  *
  * \sa mood_method, admissible_array.
  */
@@ -696,7 +652,13 @@ static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new
 
 static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
 {
-       long score = (compute_dynamic_score(aft_row) + mood_score) / 3;
+       long score;
+       struct afs_info afsi;
+       int ret = get_afsi_of_row(aft_row, &afsi);
+
+       if (ret < 0)
+               return ret;
+       score = compute_score(&afsi, mood_score);
        return score_add(aft_row, score);
 }
 
@@ -777,9 +739,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                if (ret < 0)
                        return ret;
        }
-       score += compute_num_played_score(&afsi);
-       score += compute_last_played_score(&afsi);
-       score /= 3;
+       score = compute_score(&afsi, score);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -870,7 +830,7 @@ int change_current_mood(char *mood_name)
        if (ret < 0)
                return ret;
        log_statistics();
-       PARA_INFO_LOG("%d admissible files \n", statistics.num);
+       PARA_INFO_LOG("%d admissible files\n", statistics.num);
        for (i = 0; i < statistics.num; i++) {
                struct admissible_file_info *a = aa.array + i;
                ret = add_to_score_table(a->aft_row, a->score);
diff --git a/mood.h b/mood.h
index f97081a96c7d16d06e2f8d8ea865547c34035134..10e9319b2b2c8e7a7a094207609634a23a6b3a22 100644 (file)
--- a/mood.h
+++ b/mood.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file mood.h Functions exported by mood.h. */
+/** \file mood.h Public functions of mood.c. */
 
 int change_current_mood(char *mood_name);
 void close_current_mood(void);
-void mood_check_callback(int fd, __a_unused const struct osl_object *query);
+int mood_check_callback(struct afs_callback_arg *aca);
index 251574a528d38a665b0ab67c88c213f6e27b5e41..73e744ec44088c4d8c92c1e19c1e6bed8b5076a0 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2003 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -22,6 +22,7 @@
 #include "error.h"
 #include "afh.h"
 #include "string.h"
+#include "fd.h"
 
 /*
  * MIN_CONSEC_GOOD_FRAMES defines how many consecutive valid MP3 frames we need
@@ -68,7 +69,7 @@ static const int mp3info_bitrate[2][3][14] = {
 static const int frame_size_index[] = {24000, 72000, 72000};
 static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"};
 
-#ifdef HAVE_LIBID3TAG
+#ifdef HAVE_ID3TAG
 
 #include <id3tag.h>
 
@@ -123,20 +124,11 @@ static char *get_strings(struct id3_frame *fr)
        return NULL;
 }
 
-static void mp3_get_id3(__a_unused unsigned char *map,
-               __a_unused size_t numbytes, int fd, struct taginfo *tags)
+/* this only sets values which are undefined so far */
+static void parse_frames(struct id3_tag *id3_t, struct taginfo *tags)
 {
        int i;
-       struct id3_tag *id3_t;
-       struct id3_file *id3_f = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY);
-
-       if (!id3_f)
-               return;
-       id3_t = id3_file_tag(id3_f);
-       if (!id3_t) {
-               id3_file_close(id3_f);
-               return;
-       }
+
        for (i = 0; i < id3_t->nframes; i++) {
                struct id3_frame *fr = id3_t->frames[i];
                if (!strcmp(fr->id, ID3_FRAME_TITLE)) {
@@ -165,10 +157,229 @@ static void mp3_get_id3(__a_unused unsigned char *map,
                        continue;
                }
        }
-       id3_file_close(id3_f);
 }
 
-#else /* HAVE_LIBID3TAG */
+static int mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd,
+               struct taginfo *tags)
+{
+       int ret = 0;
+       struct id3_tag *id3_t;
+
+       /* id3v2 tags are usually located at the beginning. */
+       id3_t = id3_tag_parse(map, numbytes);
+       if (id3_t) {
+               parse_frames(id3_t, tags);
+               ret |= 2;
+               id3_tag_delete(id3_t);
+       }
+       /* Also look for an id3v1 tag at the end of the file. */
+       if (numbytes >= 128) {
+               id3_t = id3_tag_parse(map + numbytes - 128, 128);
+               if (id3_t) {
+                       parse_frames(id3_t, tags);
+                       ret |= 1;
+                       id3_tag_delete(id3_t);
+               }
+       }
+       return ret;
+}
+
+/* These helpers are not mentioned in the libid3tag header file. */
+void id3_field_init(union id3_field *field, enum id3_field_type type);
+void id3_field_finish(union id3_field *field);
+
+/*
+ * Frames of type ID3_FRAME_COMMENT contain three fields of type language,
+ * string, and fullstring. The third field contains the actual comment.
+ */
+static int set_comment_fields(struct id3_frame *fr, id3_ucs4_t *ucs4_val)
+{
+       int ret;
+       union id3_field *field;
+
+       field = id3_frame_field(fr, 1);
+       id3_field_init(field, ID3_FIELD_TYPE_LANGUAGE);
+
+       field = id3_frame_field(fr, 2);
+       id3_field_init(field, ID3_FIELD_TYPE_STRING);
+
+       field = id3_frame_field(fr, 3);
+       id3_field_init(field, ID3_FIELD_TYPE_STRINGFULL);
+       ret = id3_field_setfullstring(field, ucs4_val);
+       if (ret < 0)
+               return -E_ID3_SETSTRING;
+       return 1;
+}
+
+/*
+ * UCS-4 stands for Universal Character Set where each character uses exactly
+ * 32 bits. It is also known as UTF-32, see ISO 10646.
+ *
+ * We have to use UCS-4 as an intermediate representation because the functions
+ * of libid3tag which set the tag content expect an array of UCS-4 characters.
+ * Fortunately, libid3tag contains conversion functions from and to UTF-8.
+ */
+static int replace_tag(char const *id, const char *val, struct id3_tag *id3_t,
+               int options)
+{
+       int ret;
+       struct id3_frame *fr;
+       union id3_field *field;
+       id3_ucs4_t *ucs4_val;
+
+       /* First, detach all frames that match the given id. */
+       while ((fr = id3_tag_findframe(id3_t, id, 0))) {
+               PARA_INFO_LOG("deleting %s frame\n", id);
+               ret = id3_tag_detachframe(id3_t, fr);
+               if (ret < 0)
+                       return -E_ID3_DETACH;
+               id3_frame_delete(fr);
+       }
+       if (!val || !*val)
+               return 0;
+       fr = id3_frame_new(id);
+       PARA_DEBUG_LOG("frame desc: %s, %d fields\n", fr->description, fr->nfields);
+
+       /* Frame 0 contains the encoding. We always use UTF-8. */
+       field = id3_frame_field(fr, 0);
+       id3_field_init(field, ID3_FIELD_TYPE_TEXTENCODING);
+       ret = id3_field_settextencoding(field, ID3_FIELD_TEXTENCODING_UTF_8);
+       if (ret < 0)
+               return -E_ID3_SETENCODING;
+       /* create UCS-4 representation */
+       ucs4_val = id3_utf8_ucs4duplicate((const id3_utf8_t *)val);
+
+       if (strcmp(id, ID3_FRAME_COMMENT) == 0)
+               ret = set_comment_fields(fr, ucs4_val);
+       else {
+               /* Non-comment frames contain the value in field 1. */
+               field = id3_frame_field(fr, 1);
+               if (options & ID3_TAG_OPTION_ID3V1) {
+                       id3_ucs4_t *ptr[] = {ucs4_val, NULL};
+                       id3_field_init(field, ID3_FIELD_TYPE_STRINGLIST);
+                       ret = id3_field_setstrings(field, 1, ptr);
+               } else {
+                       id3_field_init(field, ID3_FIELD_TYPE_STRINGFULL);
+                       ret = id3_field_setfullstring(field, ucs4_val);
+               }
+       }
+       free(ucs4_val);
+       if (ret < 0)
+               return -E_ID3_SETSTRING;
+
+       PARA_INFO_LOG("attaching %s frame, value: %s\n", id, val);
+       ret = id3_tag_attachframe(id3_t, fr);
+       if (ret < 0)
+               return -E_ID3_ATTACH;
+       return 1;
+}
+
+static int replace_tags(struct id3_tag *id3_t, struct taginfo *tags)
+{
+       int ret, options = id3_tag_options(id3_t, 0, 0);
+
+       ret = replace_tag(ID3_FRAME_ARTIST, tags->artist, id3_t, options);
+       if (ret < 0)
+               return ret;
+       ret = replace_tag(ID3_FRAME_TITLE, tags->title, id3_t, options);
+       if (ret < 0)
+               return ret;
+       ret = replace_tag(ID3_FRAME_ALBUM, tags->album, id3_t, options);
+       if (ret < 0)
+               return ret;
+       ret = replace_tag(ID3_FRAME_YEAR, tags->year, id3_t, options);
+       if (ret < 0)
+               return ret;
+       ret = replace_tag(ID3_FRAME_COMMENT, tags->comment, id3_t, options);
+       if (ret < 0)
+               return ret;
+       return 1;
+}
+
+/* id3_tag_delete() does not seem to work due to refcount issues. */
+static void free_tag(struct id3_tag *id3_t)
+{
+       int i, j;
+       for (i = 0; i < id3_t->nframes; i++) {
+               struct id3_frame *fr = id3_t->frames[i];
+               for (j = 0; j < fr->nfields; j++) {
+                       union id3_field *field = &fr->fields[j];
+                       id3_field_finish(field);
+               }
+               free(fr);
+       }
+       free(id3_t->frames);
+       free(id3_t);
+}
+
+static int mp3_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int fd, __a_unused const char *filename)
+{
+       int ret;
+       id3_length_t old_v2size, new_v2size;
+       id3_byte_t v1_buffer[128], *v2_buffer;
+       size_t data_sz;
+       struct id3_tag *v1_tag = NULL, *v2_tag = NULL;
+
+       if (mapsize >= 128) {
+               v1_tag = id3_tag_parse((const id3_byte_t *)map + mapsize - 128,
+                       128);
+               if (v1_tag) {
+                       PARA_NOTICE_LOG("replacing id3v1 tag\n");
+                       ret = replace_tags(v1_tag, tags);
+                       if (ret < 0)
+                               goto out;
+                       id3_tag_render(v1_tag, v1_buffer);
+               }
+       }
+       old_v2size = 0;
+       v2_tag = id3_tag_parse((const id3_byte_t *)map, mapsize);
+       if (v2_tag) {
+               PARA_NOTICE_LOG("replacing id3v2 tag\n");
+               old_v2size = v2_tag->paddedsize;
+       } else if (!v1_tag) {
+               PARA_NOTICE_LOG("no id3 tags found, adding id3v2 tag\n");
+               v2_tag = id3_tag_new();
+               assert(v2_tag);
+       }
+       if (v2_tag) {
+               /*
+                * Turn off all options to avoid creating an extended header.
+                * id321 does not understand it.
+                */
+               id3_tag_options(v2_tag, ~0U, 0);
+               ret = replace_tags(v2_tag, tags);
+               if (ret < 0)
+                       goto out;
+               new_v2size = id3_tag_render(v2_tag, NULL);
+               v2_buffer = para_malloc(new_v2size);
+               id3_tag_render(v2_tag, v2_buffer);
+               PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
+               ret = write_all(fd, (char *)v2_buffer, new_v2size);
+               free(v2_buffer);
+               if (ret < 0)
+                       goto out;
+       }
+       data_sz = mapsize - old_v2size;
+       if (v1_tag && data_sz >= 128)
+               data_sz -= 128;
+       PARA_INFO_LOG("writing data part (%zu bytes)\n", data_sz);
+       ret = write_all(fd, map + old_v2size, data_sz);
+       if (ret < 0)
+               goto out;
+       if (v1_tag) {
+               PARA_INFO_LOG("writing v1 tag\n");
+               ret = write_all(fd, (char *)v1_buffer, 128);
+       }
+out:
+       if (v1_tag)
+               free_tag(v1_tag);
+       if (v2_tag)
+               free_tag(v2_tag);
+       return ret;
+}
+
+#else /* HAVE_ID3TAG */
 
 /*
  * Remove trailing whitespace from the end of a string
@@ -181,7 +392,7 @@ static char *unpad(char *string)
        return string;
 }
 
-static void mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd,
+static int mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd,
        struct taginfo *tags)
 {
        char title[31], artist[31], album[31], year[5], comment[31];
@@ -189,7 +400,7 @@ static void mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd,
 
        if (numbytes < 128 || strncmp("TAG", (char *)map + numbytes - 128, 3)) {
                PARA_DEBUG_LOG("no id3 v1 tag\n");
-               return;
+               return 0;
        }
        fpos = numbytes - 125;
        memcpy(title, map + fpos, 30);
@@ -216,8 +427,9 @@ static void mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd,
        tags->year = para_strdup(year);
        tags->album = para_strdup(album);
        tags->comment = para_strdup(comment);
+       return 1;
 }
-#endif /* HAVE_LIBID3TAG */
+#endif /* HAVE_ID3TAG */
 
 static int header_frequency(struct mp3header *h)
 {
@@ -387,6 +599,7 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
        unsigned chunk_table_size = 1000; /* gets increased on demand */
        off_t fpos = 0;
        struct mp3header header;
+       const char *tag_versions[] = {"no", "id3v1", "id3v2", "id3v1+id3v2"};
 
        afhi->chunks_total = 0;
        afhi->chunk_table = para_malloc(chunk_table_size * sizeof(uint32_t));
@@ -443,11 +656,11 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
        afhi->channels = header_channels(&header);
        afhi->seconds_total = (tv2ms(&total_time) + 500) / 1000;
        tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv);
-       PARA_DEBUG_LOG("%lu chunks, each %lums\n", afhi->chunks_total,
+       PARA_DEBUG_LOG("%" PRIu32 "chunks, each %lums\n", afhi->chunks_total,
                tv2ms(&afhi->chunk_tv));
-       afhi->techinfo = make_message("%cbr, %s", vbr? 'v' : 'c',
-               header_mode(&header));
-       mp3_get_id3(map, numbytes, fd, &afhi->tags);
+       ret = mp3_get_id3(map, numbytes, fd, &afhi->tags);
+       afhi->techinfo = make_message("%cbr, %s, %s tags", vbr? 'v' : 'c',
+               header_mode(&header), tag_versions[ret]);
        return 1;
 err_out:
        PARA_ERROR_LOG("%s\n", para_strerror(-ret));
@@ -482,4 +695,7 @@ void mp3_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = mp3_get_file_info;
        afh->suffixes = mp3_suffixes;
+#ifdef HAVE_LIBID3TAG
+       afh->rewrite_tags = mp3_rewrite_tags;
+#endif /* HAVE_LIBID3TAG */
 }
index 33c0dfc9f7d5019484a17f97a47064fb75798358..5f6935d1a5be70c7fa89dd0368bca48e34664422 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -77,9 +77,9 @@ static void mp3dec_close(struct filter_node *fn)
 
 #define MP3DEC_MAX_FRAME 8192
 
-static int mp3dec_post_select(__a_unused struct sched *s, struct task *t)
+static int mp3dec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        int i, ret;
        struct private_mp3dec_data *pmd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
diff --git a/net.c b/net.c
index 708e83f2aae7df4d17a7d59abea182ec657d1911..42418e5f20d9e168eb0af5c7da9c2d1ffa1c2127 100644 (file)
--- a/net.c
+++ b/net.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  */
 #define _GNU_SOURCE
 
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <netdb.h>
 
 /* At least NetBSD needs these. */
@@ -43,7 +48,7 @@
  * \param netmask Value of the netmask part in \a cidr or the
  *               default of 32 if not specified.
  *
- * \return Pointer to \a addr if succesful, NULL on error.
+ * \return Pointer to \a addr if successful, NULL on error.
  * \sa RFC 4632
  */
 char *parse_cidr(const char *cidr,
@@ -142,9 +147,9 @@ static bool host_string_ok(const char *host)
  * \param hostlen The maximum length of \a host.
  * \param port   To return the port number (if any) of \a url.
  *
- * \return Pointer to \a host, or NULL if failed.
- * If NULL is returned, \a host and \a portnum are undefined. If no
- * port number was present in \a url, \a portnum is set to -1.
+ * \return Pointer to \a host, or \p NULL if failed.  If \p NULL is returned,
+ * \a host and \a port are undefined. If no port number was present in \a url,
+ * \a port is set to -1.
  *
  * \sa RFC 3986, 3.2.2/3.2.3
  */
@@ -168,16 +173,16 @@ char *parse_url(const char *url,
                if (*o++ != ']' || (*o != '\0' && *o != ':'))
                        goto failed;
        } else {
-               for (; (*c = *o == ':'? '\0' : *o); c++, o++)
-                       if (c == end)
+               for (; (*c = *o == ':'? '\0' : *o); c++, o++) {
+                       if (c == end && o[1])
                                goto failed;
+               }
        }
 
        if (*o == ':')
                if (para_atoi32(++o, port) < 0 ||
                    *port < 0 || *port > 0xffff)
                        goto failed;
-
        if (host_string_ok(host))
                return host;
 failed:
@@ -203,7 +208,7 @@ const char *stringify_port(int port, const char *transport)
                struct servent *se = getservbyport(htons(port), transport);
 
                if (se == NULL)
-                       snprintf(service, sizeof(service), "%u", port);
+                       snprintf(service, sizeof(service), "%d", port);
                else
                        snprintf(service, sizeof(service), "%s", se->s_name);
        }
@@ -328,7 +333,14 @@ static void flowopt_setopts(int sockfd, struct flowopts *fo)
                }
 }
 
-static void flowopt_cleanup(struct flowopts *fo)
+/**
+ * Deallocate all resources of a flowopts structure.
+ *
+ * \param fo A pointer as returned from flowopt_new().
+ *
+ * It's OK to pass \p NULL here in which case the function does nothing.
+ */
+void flowopt_cleanup(struct flowopts *fo)
 {
        struct pre_conn_opt *cur, *next;
 
@@ -344,137 +356,148 @@ static void flowopt_cleanup(struct flowopts *fo)
 }
 
 /**
- * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
+ * Resolve an IPv4/IPv6 address.
  *
  * \param l4type The layer-4 type (\p IPPROTO_xxx).
- * \param passive Whether this is a passive (1) or active (0) socket.
+ * \param passive Whether \p AI_PASSIVE should be included as hint.
  * \param host Remote or local hostname or IPv/6 address string.
- * \param port_number Decimal port number.
- * \param fo Socket options to be set before making the connection.
+ * \param port_number Used to set the port in each returned address structure.
+ * \param result addrinfo structures are returned here.
  *
- * This creates a ready-made IPv4/v6 socket structure after looking up the
- * necessary parameters. The interpretation of \a host depends on the value of
- * \a passive:
- *     - on a passive socket host is interpreted as an interface IPv4/6 address
- *       (can be left NULL);
- *     - on an active socket, \a host is the peer DNS name or IPv4/6 address
- *       to connect to;
- *     - \a port_number is in either case the numeric port number (not service
- *       string).
- *
- * Furthermore, bind(2) is called on passive sockets, and connect(2) on active
- * sockets. The algorithm tries all possible address combinations until it
- * succeeds. If \a fo is supplied, options are set and cleanup is performed.
- *
- * \return This function returns 1 on success and \a -E_ADDRESS_LOOKUP when no
- * matching connection could be set up (with details in the error log).
- *
- *  \sa ipv6(7), getaddrinfo(3), bind(2), connect(2).
- */
-int makesock(unsigned l4type, bool passive,
-            const char *host, uint16_t port_number,
-            struct flowopts *fo)
-{
-       struct addrinfo *local = NULL, *src = NULL, *remote = NULL,
-               *dst = NULL, hints;
-       unsigned int    l3type = AF_UNSPEC;
-       int             rc, on = 1, sockfd = -1,
-                       socktype = sock_type(l4type);
+ * The interpretation of \a host depends on the value of \a passive. On a
+ * passive socket host is interpreted as an interface IPv4/6 address (can be
+ * left NULL). On an active socket, \a host is the peer DNS name or IPv4/6
+ * address to connect to.
+ *
+ * \return Standard.
+ *
+ * \sa getaddrinfo(3).
+ */
+int lookup_address(unsigned l4type, bool passive, const char *host,
+               int port_number, struct addrinfo **result)
+{
+       int ret;
        char port[6]; /* port number has at most 5 digits */
+       struct addrinfo *addr = NULL, hints;
 
-       sprintf(port, "%u", port_number);
+       *result = NULL;
+       sprintf(port, "%u", port_number & 0xffff);
        /* Set up address hint structure */
        memset(&hints, 0, sizeof(hints));
-       hints.ai_family = l3type;
-       hints.ai_socktype = socktype;
-       /* 
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = sock_type(l4type);
+       /*
         * getaddrinfo does not support SOCK_DCCP, so for the sake of lookup
         * (and only then) pretend to be UDP.
         */
        if (l4type == IPPROTO_DCCP)
                hints.ai_socktype = SOCK_DGRAM;
-
        /* only use addresses available on the host */
        hints.ai_flags = AI_ADDRCONFIG;
-       if (l3type == AF_INET6)
-               /* use v4-mapped-v6 if no v6 addresses found */
-               hints.ai_flags |= AI_V4MAPPED | AI_ALL;
-
        if (passive && host == NULL)
                hints.ai_flags |= AI_PASSIVE;
-
        /* Obtain local/remote address information */
-       if ((rc = getaddrinfo(host, port, &hints, passive ? &local : &remote))) {
-               PARA_ERROR_LOG("can not resolve %s address %s#%s: %s.\n",
-                               layer4_name(l4type),
-                               host? host : (passive? "[loopback]" : "[localhost]"),
-                               port, gai_strerror(rc));
-               rc = -E_ADDRESS_LOOKUP;
-               goto out;
+       ret = getaddrinfo(host, port, &hints, &addr);
+       if (ret != 0) {
+               PARA_ERROR_LOG("can not resolve %s address %s#%s: %s\n",
+                       layer4_name(l4type),
+                       host? host : (passive? "[loopback]" : "[localhost]"),
+                       port, gai_strerror(ret));
+               return -E_ADDRESS_LOOKUP;
        }
+       *result = addr;
+       return 1;
+}
 
-       /* Iterate over all src/dst combination, exhausting dst first */
-       for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
-               if (src && dst && src->ai_family == AF_INET
-                               && dst->ai_family == AF_INET6)
-                       goto get_next_dst; /* v4 -> v6 is not possible */
-
-               sockfd = socket(src ? src->ai_family : dst->ai_family,
-                       socktype, l4type);
-               if (sockfd < 0)
-                       goto get_next_dst;
+/**
+ * Create an active or passive socket.
+ *
+ * \param l4type \p IPPROTO_TCP, \p IPPROTO_UDP, or \p IPPROTO_DCCP.
+ * \param passive Whether to call bind(2) or connect(2).
+ * \param ai Address information as obtained from \ref lookup_address().
+ * \param fo Socket options to be set before making the connection.
+ *
+ * bind(2) is called on passive sockets, and connect(2) on active sockets. The
+ * algorithm tries all possible address combinations until it succeeds. If \a
+ * fo is supplied, options are set but cleanup must be performed in the caller.
+ *
+ * \return File descriptor on success, \p E_MAKESOCK on errors.
+ *
+ * \sa \ref lookup_address(), \ref makesock(), ip(7), ipv6(7), bind(2),
+ * connect(2).
+ */
+int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
+               struct flowopts *fo)
+{
+       int ret = -E_MAKESOCK, on = 1;
 
+       for (; ai; ai = ai->ai_next) {
+               int fd;
+               ret = socket(ai->ai_family, sock_type(l4type), l4type);
+               if (ret < 0)
+                       continue;
+               fd = ret;
+               flowopt_setopts(fd, fo);
+               if (!passive) {
+                       if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
+                               return fd;
+                       close(fd);
+                       continue;
+               }
                /*
                 * Reuse the address on passive sockets to avoid failure on
                 * restart (protocols using listen()) and when creating
                 * multiple listener instances (UDP multicast).
                 */
-               if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
-                                                 &on, sizeof(on)) == -1) {
-                       rc = errno;
-                       close(sockfd);
-                       PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n",
-                                      strerror(rc));
-                       rc = -ERRNO_TO_PARA_ERROR(rc);
-                       break;
-               }
-               flowopt_setopts(sockfd, fo);
-
-               if (src) {
-                       if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) {
-                               close(sockfd);
-                               goto get_next_src;
-                       }
-                       if (!dst) /* bind-only completed successfully */
-                               break;
+               if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
+                               sizeof(on)) == -1) {
+                       close(fd);
+                       continue;
                }
-
-               if (dst && connect(sockfd, dst->ai_addr, dst->ai_addrlen) == 0)
-                       break; /* connection completed successfully */
-               close(sockfd);
-get_next_dst:
-               if (dst && (dst = dst->ai_next))
+               if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+                       close(fd);
                        continue;
-get_next_src:
-               if (src && (src = src->ai_next)) /* restart inner loop */
-                       dst = remote;
+               }
+               return fd;
        }
-out:
-       if (local)
-               freeaddrinfo(local);
-       if (remote)
-               freeaddrinfo(remote);
-       flowopt_cleanup(fo);
-
-       if (src == NULL && dst == NULL) {
-               if (rc >= 0)
-                       rc = -E_MAKESOCK;
-               PARA_ERROR_LOG("can not create %s socket %s#%s.\n",
-                       layer4_name(l4type), host? host : (passive?
-                       "[loopback]" : "[localhost]"), port);
-               return rc;
+       return -E_MAKESOCK;
+}
+
+/**
+ * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
+ *
+ * \param l4type The layer-4 type (\p IPPROTO_xxx).
+ * \param passive Whether this is a passive or active socket.
+ * \param host Passed to \ref lookup_address().
+ * \param port_number Passed to \ref lookup_address().
+ * \param fo Passed to \ref makesock_addrinfo().
+ *
+ * This creates a ready-made IPv4/v6 socket structure after looking up the
+ * necessary parameters. The function first calls \ref lookup_address() and
+ * passes the address information to makesock_addrinfo() to create and
+ * initialize the socket.
+ *
+ * \return The newly created file descriptor on success, a negative error code
+ * on failure.
+ *
+ * \sa \ref lookup_address(), \ref makesock_addrinfo().
+ */
+int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_number,
+               struct flowopts *fo)
+{
+       struct addrinfo *ai;
+       int ret = lookup_address(l4type, passive, host, port_number, &ai);
+
+       if (ret >= 0)
+               ret = makesock_addrinfo(l4type, passive, ai, fo);
+       if (ai)
+               freeaddrinfo(ai);
+       if (ret < 0) {
+               PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
+               layer4_name(l4type), host? host : (passive?
+               "[loopback]" : "[localhost]"), port_number);
        }
-       return sockfd;
+       return ret;
 }
 
 /**
@@ -578,7 +601,7 @@ static inline int estimated_header_overhead(const int af_type)
  *
  * \param sockfd The socket file descriptor.
  *
- * The socket must be connected. See RFC 1122, 3.3.3. If the protocol familiy
+ * The socket must be connected. See RFC 1122, 3.3.3. If the protocol family
  * could not be determined, \p AF_INET is assumed.
  *
  * \return The maximum message size of the address family type.
@@ -599,11 +622,9 @@ int generic_max_transport_msg_size(int sockfd)
 }
 
 /**
- * Look up the local or remote side of a connected socket structure.
+ * Look up the remote side of a connected socket structure.
  *
  * \param fd The socket descriptor of the connected socket.
- * \param getname Either \p getsockname() for local, or \p getpeername() for
- * remote side.
  *
  * \return A static character string identifying hostname and port of the
  * chosen side in numeric host:port format.
@@ -611,7 +632,7 @@ int generic_max_transport_msg_size(int sockfd)
  * \sa getsockname(2), getpeername(2), parse_url(), getnameinfo(3),
  * services(5), nsswitch.conf(5).
  */
-static char *__get_sock_name(int fd, typeof(getsockname) getname)
+char *remote_name(int fd)
 {
        struct sockaddr_storage ss;
        const struct sockaddr *sa;
@@ -620,7 +641,7 @@ static char *__get_sock_name(int fd, typeof(getsockname) getname)
        static char output[sizeof(hbuf) + sizeof(sbuf) + 4];
        int ret;
 
-       if (getname(fd, (struct sockaddr *)&ss, &sslen) < 0) {
+       if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) {
                PARA_ERROR_LOG("can not determine address from fd %d: %s\n",
                        fd, strerror(errno));
                snprintf(output, sizeof(output), "(unknown)");
@@ -641,50 +662,46 @@ static char *__get_sock_name(int fd, typeof(getsockname) getname)
 }
 
 /**
- * Look up the local side of a connected socket structure.
- *
- * \param sockfd The file descriptor of the socket.
+ * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage.
  *
- * \return A pointer to a static buffer containing hostname an port. This
- * buffer must not be freed by the caller.
+ * \param ss Container of IPv4/6 address.
+ * \param ia Extracted IPv4 address (different from 0) or 0 if unsuccessful.
  *
- * \sa remote_name().
+ * \sa RFC 3493.
  */
-char *local_name(int sockfd)
+void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia)
 {
-       return __get_sock_name(sockfd, getsockname);
-}
+       const struct sockaddr *sa = normalize_ip_address(ss);
 
-/**
- * Look up the remote side of a connected socket structure.
- *
- * \param sockfd The file descriptor of the socket.
- *
- * \return Analogous to the return value of \ref local_name() but for the
- * remote side.
- *
- * \sa local_name().
- */
-char *remote_name(int sockfd)
-{
-       return __get_sock_name(sockfd, getpeername);
+       memset(ia, 0, sizeof(*ia));
+       if (sa->sa_family == AF_INET)
+               *ia = ((struct sockaddr_in *)sa)->sin_addr;
 }
 
 /**
- * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage.
- * \param ss Container of IPv4/6 address
- * \return Extracted IPv4 address (different from 0) or 0 if unsuccessful.
+ * Compare the address part of IPv4/6 addresses.
  *
- * \sa RFC 3493
+ * \param sa1 First address.
+ * \param sa2 Second address.
+ *
+ * \return True iff the IP address of \a sa1 and \a sa2 match.
  */
-struct in_addr extract_v4_addr(const struct sockaddr_storage *ss)
+bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2)
 {
-       struct in_addr ia = {.s_addr = 0};
-       const struct sockaddr *sa = normalize_ip_address(ss);
-
-       if (sa->sa_family == AF_INET)
-               ia = ((struct sockaddr_in *)sa)->sin_addr;
-       return ia;
+       if (!sa1 || !sa2)
+               return false;
+       if (sa1->sa_family != sa2->sa_family)
+               return false;
+       if (sa1->sa_family == AF_INET) {
+               struct sockaddr_in *a1 = (typeof(a1))sa1,
+                       *a2 = (typeof (a2))sa2;
+               return a1->sin_addr.s_addr == a2->sin_addr.s_addr;
+       } else if (sa1->sa_family == AF_INET6) {
+               struct sockaddr_in6 *a1 = (typeof(a1))sa1,
+                       *a2 = (typeof (a2))sa2;
+               return !memcmp(a1, a2, sizeof(*a1));
+       } else
+               return false;
 }
 
 /**
@@ -817,52 +834,66 @@ int dccp_available_ccids(uint8_t **ccid_array)
  * \return Positive on success, \p -E_NAME_TOO_LONG if \a name is longer
  * than \p UNIX_PATH_MAX.
  */
-static int init_unix_addr(struct sockaddr_un *u, const char *name)
+static int init_unix_addr(struct sockaddr_un *u, const char *name,
+               bool abstract)
 {
-       if (strlen(name) >= UNIX_PATH_MAX)
+       if (strlen(name) + abstract >= UNIX_PATH_MAX)
                return -E_NAME_TOO_LONG;
        memset(u->sun_path, 0, UNIX_PATH_MAX);
        u->sun_family = PF_UNIX;
-       strcpy(u->sun_path, name);
+       strcpy(u->sun_path + abstract, name);
        return 1;
 }
 
 /**
- * Prepare, create, and bind a socket for local communication.
+ * Create a socket for local communication and listen on it.
  *
  * \param name The socket pathname.
- * \param unix_addr Pointer to the \p AF_UNIX socket structure.
- * \param mode The desired mode of the socket.
+ * \param mode The desired permissions of the socket.
+ *
+ * This function creates a passive local socket for sequenced, reliable,
+ * two-way, connection-based byte streams. The socket file descriptor is set to
+ * nonblocking mode and listen(2) is called to prepare the socket for
+ * accepting incoming connection requests.
  *
- * This function creates a local socket for sequenced, reliable,
- * two-way, connection-based byte streams.
+ * If mode is zero, an abstract socket (a non-portable Linux extension) is
+ * created. In this case the socket name has no connection with filesystem
+ * pathnames.
  *
- * \return The file descriptor, on success, negative on errors.
+ * \return The file descriptor on success, negative error code on failure.
  *
- * \sa socket(2)
- * \sa bind(2)
- * \sa chmod(2)
+ * \sa socket(2), \sa bind(2), \sa chmod(2), listen(2), unix(7).
  */
-int create_local_socket(const char *name, struct sockaddr_un *unix_addr,
-               mode_t mode)
+int create_local_socket(const char *name, mode_t mode)
 {
+       struct sockaddr_un unix_addr;
        int fd, ret;
+       bool abstract = mode == 0;
 
-       ret = init_unix_addr(unix_addr, name);
+       ret = init_unix_addr(&unix_addr, name, abstract);
        if (ret < 0)
                return ret;
        ret = socket(PF_UNIX, SOCK_STREAM, 0);
        if (ret < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
        fd = ret;
-       ret = bind(fd, (struct sockaddr *) unix_addr, UNIX_PATH_MAX);
+       ret = mark_fd_nonblocking(fd);
+       if (ret < 0)
+               goto err;
+       ret = bind(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
-       ret = -E_CHMOD;
-       if (chmod(name, mode) < 0)
+       if (!abstract) {
+               ret = -E_CHMOD;
+               if (chmod(name, mode) < 0)
+                       goto err;
+       }
+       if (listen(fd , 5) < 0) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
+       }
        return fd;
 err:
        close(fd);
@@ -888,17 +919,22 @@ int connect_local_socket(const char *name)
        int fd, ret;
 
        PARA_DEBUG_LOG("connecting to %s\n", name);
-       ret = init_unix_addr(&unix_addr, name);
-       if (ret < 0)
-               return ret;
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
-       if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) == -1) {
-               ret = -ERRNO_TO_PARA_ERROR(errno);
+       /* first try (linux-only) abstract socket */
+       ret = init_unix_addr(&unix_addr, name, true);
+       if (ret < 0)
                goto err;
-       }
-       return fd;
+       if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
+               return fd;
+       /* next try pathname socket */
+       ret = init_unix_addr(&unix_addr, name, false);
+       if (ret < 0)
+               goto err;
+       if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
+               return fd;
+       ret = -ERRNO_TO_PARA_ERROR(errno);
 err:
        close(fd);
        return ret;
@@ -914,16 +950,18 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
        return recv_buffer(fd, buf, size) > 0? 1 : -E_RECVMSG;
 }
 #else /* HAVE_UCRED */
+
 /**
- * Send \p NULL-terminated buffer and Unix credentials of the current process.
+ * Send a buffer and the credentials of the current process to a socket.
  *
- * \param sock The socket file descriptor.
- * \param buf The buffer to be sent.
+ * \param sock The file descriptor of the sending socket.
+ * \param buf The zero-terminated buffer to send.
  *
- * \return On success, this call returns the number of characters sent.  On
- * error, \p -E_SENDMSG is returned.
+ * \return On success, this call returns the number of bytes sent. On errors,
+ * \p -E_SENDMSG is returned.
  *
- * \sa sendmsg(2), okir's Black Hats Manual.
+ * \sa \ref recv_cred_buffer, sendmsg(2), socket(7), unix(7), okir's Black Hats
+ * Manual.
  */
 ssize_t send_cred_buffer(int sock, char *buf)
 {
@@ -936,7 +974,7 @@ ssize_t send_cred_buffer(int sock, char *buf)
 
        /* Response data */
        iov.iov_base = buf;
-       iov.iov_len  = strlen(buf);
+       iov.iov_len = strlen(buf);
        c.pid = getpid();
        c.uid = getuid();
        c.gid = getgid();
@@ -954,7 +992,7 @@ ssize_t send_cred_buffer(int sock, char *buf)
        *(struct ucred *)CMSG_DATA(cmsg) = c;
        msg.msg_controllen = cmsg->cmsg_len;
        ret = sendmsg(sock, &msg, 0);
-       if (ret  < 0)
+       if (ret < 0)
                ret = -E_SENDMSG;
        return ret;
 }
@@ -970,13 +1008,13 @@ static void dispose_fds(int *fds, unsigned num)
 /**
  * Receive a buffer and the Unix credentials of the sending process.
  *
- * \param fd the socket file descriptor.
- * \param buf the buffer to store the message.
- * \param size the size of \a buffer.
+ * \param fd The file descriptor of the receiving socket.
+ * \param buf The buffer to store the received message.
+ * \param size The length of \a buf in bytes.
  *
- * \return negative on errors, the user id on success.
+ * \return Negative on errors, the user id of the sending process on success.
  *
- * \sa recvmsg(2), okir's Black Hats Manual.
+ * \sa \ref send_cred_buffer and the references given there.
  */
 int recv_cred_buffer(int fd, char *buf, size_t size)
 {
@@ -1009,7 +1047,7 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
                } else
                        if (cmsg->cmsg_level == SOL_SOCKET
                                        && cmsg->cmsg_type == SCM_RIGHTS) {
-                               dispose_fds((int *) CMSG_DATA(cmsg),
+                               dispose_fds((int *)CMSG_DATA(cmsg),
                                        (cmsg->cmsg_len - CMSG_LEN(0))
                                        / sizeof(int));
                        }
diff --git a/net.h b/net.h
index ae5e80150cca3b8d9b4aef93fb9dfe8e98592e79..b2bb47c9b40b43fe2eb7c8813e6ffa7364b0853e 100644 (file)
--- a/net.h
+++ b/net.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -61,6 +61,7 @@ struct flowopts;
 extern struct flowopts *flowopt_new(void);
 extern void flowopt_add(struct flowopts *fo, int level, int opt,
                const char *name, const void *val, int len);
+void flowopt_cleanup(struct flowopts *fo);
 /** Flowopt shortcut macros */
 #define OPT_ADD(fo, lev, opt, val, len)        flowopt_add(fo, lev, opt, #opt, val, len)
 
@@ -101,12 +102,17 @@ _static_inline_ bool is_valid_ipv6_address(const char *address)
        return inet_pton(AF_INET6, address, &test_it) != 0;
 }
 
+int lookup_address(unsigned l4type, bool passive, const char *host,
+               int port_number, struct addrinfo **result);
+
 /**
  * Generic socket creation (passive and active sockets).
  */
-extern int makesock(unsigned l4type, bool passive,
-                   const char *host, uint16_t port_number,
-                   struct flowopts *fo);
+int makesock(unsigned l4type, bool passive, const char *host,
+               uint16_t port_number, struct flowopts *fo);
+
+int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
+               struct flowopts *fo);
 
 static inline int para_connect_simple(unsigned l4type,
                                      const char *host, uint16_t port)
@@ -114,7 +120,8 @@ static inline int para_connect_simple(unsigned l4type,
        return makesock(l4type, 0, host, port, NULL);
 }
 
-extern struct in_addr extract_v4_addr(const struct sockaddr_storage *ss);
+void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
+bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2);
 
 /**
  * Functions to support listening sockets.
@@ -129,7 +136,6 @@ static inline int para_listen_simple(unsigned l4type, uint16_t port)
 }
 
 /** Pretty-printing of IPv4/6 socket addresses */
-extern char *local_name(int sockfd);
 extern char *remote_name(int sockfd);
 
 /**
@@ -141,8 +147,7 @@ int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
 
 int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd);
-int create_local_socket(const char *name, struct sockaddr_un *unix_addr,
-       mode_t mode);
+int create_local_socket(const char *name, mode_t mode);
 int connect_local_socket(const char *name);
 int recv_cred_buffer(int, char *, size_t);
 ssize_t send_cred_buffer(int, char*);
index a6120be28bfcf8bfb581cc1fb37a8f37776a2533..32f8bc1beda6f10376db3d3842e0e3cfcaf02e96 100644 (file)
--- a/ogg_afh.c
+++ b/ogg_afh.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -20,6 +20,20 @@ struct private_vorbis_data {
        vorbis_comment vc;
 };
 
+/*
+ * Vorbis uses three header packets, all of which are required: the
+ * identification header, the comments header, and the setup header.
+ *
+ * The identification header identifies the bitstream as Vorbis. It contains
+ * the Vorbis version and simple audio characteristics of the stream such as
+ * sample rate and number of channels.
+ *
+ * The comment header includes user text comments (tags) and a vendor string
+ * for the application/library that produced the bitstream.
+ *
+ * The setup header includes extensive CODEC setup information as well as the
+ * complete VQ and Huffman codebooks needed for decoding.
+ */
 static int vorbis_packet_callback(ogg_packet *packet, int packet_num,
                __a_unused int serial, struct afh_info *afhi, void *private_data)
 {
@@ -159,6 +173,39 @@ fail:
 
 static const char* ogg_suffixes[] = {"ogg", NULL};
 
+static int vorbis_make_meta_packet(struct taginfo *tags, ogg_packet *result)
+{
+       vorbis_comment vc;
+       int ret;
+
+       vorbis_comment_init(&vc);
+       vorbis_comment_add_tag(&vc, "artist", tags->artist);
+       vorbis_comment_add_tag(&vc, "title", tags->title);
+       vorbis_comment_add_tag(&vc, "album", tags->album);
+       vorbis_comment_add_tag(&vc, "year", tags->year);
+       vorbis_comment_add_tag(&vc, "comment", tags->comment);
+       ret = vorbis_commentheader_out(&vc, result);
+       vorbis_comment_clear(&vc);
+       if (ret != 0)
+               return -E_VORBIS_COMMENTHEADER;
+       return 1;
+}
+
+static int vorbis_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int output_fd,
+               __a_unused const char *filename)
+{
+       int ret;
+       ogg_packet packet;
+
+       ret = vorbis_make_meta_packet(tags, &packet);
+       if (ret < 0)
+               return ret;
+       ret = ogg_rewrite_tags(map, mapsize, output_fd, (char *)packet.packet,
+               packet.bytes);
+       free(packet.packet);
+       return ret;
+}
 /**
  * The init function of the ogg vorbis audio format handler.
  *
@@ -169,4 +216,5 @@ void ogg_init(struct audio_format_handler *afh)
        afh->get_file_info = ogg_vorbis_get_file_info;
        afh->get_header = vorbis_get_header;
        afh->suffixes = ogg_suffixes;
+       afh->rewrite_tags = vorbis_rewrite_tags;
 }
index ac70eb3f0fc543a316f636c7a589cfaa60018c5c..e49b803b4e7a0e6492161d7be5c2b2fa21a16a09 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -14,7 +14,7 @@
 #include "error.h"
 #include "string.h"
 #include "ogg_afh_common.h"
-
+#include "fd.h"
 
 /* Taken from decoder_example.c of libvorbis-1.2.3. */
 static int process_packets_2_and_3(ogg_sync_state *oss,
@@ -159,7 +159,7 @@ int ogg_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        afhi->seconds_total = num_frames / afhi->frequency;
        /* use roughly one page per chunk */
        frames_per_chunk = num_frames / i;
-       PARA_INFO_LOG("%lu seconds, %d frames/chunk\n",
+       PARA_INFO_LOG("%" PRIu32 "seconds, %d frames/chunk\n",
                afhi->seconds_total, frames_per_chunk);
        ct_size = 250;
        afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
@@ -188,3 +188,147 @@ out:
        ogg_sync_clear(&oss);
        return ret;
 }
+
+static int write_ogg_page(int fd, const ogg_page *op)
+{
+       int ret;
+
+       PARA_DEBUG_LOG("header/body: %lu/%lu\n", op->header_len, op->body_len);
+       ret = xwrite(fd, (const char *)op->header, op->header_len);
+       if (ret < 0)
+               return ret;
+       return xwrite(fd, (const char *)op->body, op->body_len);
+}
+
+/**
+ * Change meta tags of ogg files.
+ *
+ * \param map The (read-only) memory map of the input file.
+ * \param map_sz The size of the input file in bytes.
+ * \param fd The output file descriptor.
+ * \param meta_packet Codec-specific packet containing modified tags.
+ * \param meta_sz Size of the metadata packet.
+ *
+ * This function writes a new ogg file content using file descriptor \a fd,
+ * which must correspond to a file which has been opened for writing.  The
+ * second packet is supposed to contain the metadata, and is replaced by \a
+ * meta_packet. This output file has to be closed by the caller.
+ *
+ * \return Standard.
+ */
+int ogg_rewrite_tags(const char *map, size_t map_sz, int fd,
+               char *meta_packet, size_t meta_sz)
+{
+       ogg_sync_state oss_in, oss_out;
+       ogg_stream_state stream_in, stream_out, *si = NULL, *so = NULL;
+       ogg_packet packet;
+       ogg_page op;
+       char *buf;
+       int serial, ret;
+       long len = map_sz;
+
+       ogg_sync_init(&oss_in);
+       ogg_sync_init(&oss_out);
+
+       ret = -E_OGG_SYNC;
+       buf = ogg_sync_buffer(&oss_in, len);
+       if (!buf)
+               goto out;
+       memcpy(buf, map, len);
+       ret = -E_OGG_SYNC;
+       if (ogg_sync_wrote(&oss_in, len) < 0)
+               goto out;
+       if (ogg_sync_pageout(&oss_in, &op) != 1)
+               goto out;
+       ret = ogg_page_serialno(&op);
+       serial = ret;
+
+       si = &stream_in;
+       ogg_stream_init(si, serial);
+       /* Packet #0 goes to an own page */
+       ret = -E_STREAM_PAGEIN;
+       if (ogg_stream_pagein(si, &op) < 0)
+               goto out;
+       ret = -E_STREAM_PACKETOUT;
+       if (ogg_stream_packetout(si, &packet) != 1)
+               goto out;
+       ret = -E_STREAM_PACKETIN;
+       so = &stream_out;
+       ogg_stream_init(so, serial);
+       if (ogg_stream_packetin(so, &packet) != 0)
+               goto out;
+       ret = ogg_stream_flush(so, &op);
+       assert(ret != 0);
+       /* packets have been flushed into the page. */
+       ret = write_ogg_page(fd, &op);
+       if (ret < 0)
+               goto out;
+       /*
+        * For all supported ogg/xxx audio formats the meta data packet is
+        * packet #1. Write out our modified version of this packet.
+        */
+       packet.packetno = 1;
+       packet.b_o_s = packet.e_o_s = 0;
+       packet.packet = (typeof(packet.packet))meta_packet;
+       packet.bytes = meta_sz;
+       ret = -E_STREAM_PACKETIN;
+       if (ogg_stream_packetin(so, &packet) != 0)
+               goto out;
+       /* Copy ogg packets, ignoring the meta data packet. */
+       for (;;) {
+               ret = ogg_stream_packetout(si, &packet);
+               if (ret == -1)
+                       break;
+               if (ret != 1) {
+                       ret = -E_STREAM_PAGEOUT;
+                       if (ogg_sync_pageout(&oss_in, &op) < 0)
+                               goto out;
+                       ret = -E_STREAM_PAGEIN;
+                       if (ogg_stream_pagein(si, &op))
+                               goto out;
+                       continue;
+               }
+               PARA_DEBUG_LOG("packet: bytes: %d, granule: %d, packetno: %u\n",
+                       (int)packet.bytes, (int)packet.granulepos,
+                       (int)packet.packetno);
+               /* ignore meta data packet which we replaced */
+               if (packet.packetno == 1)
+                       continue;
+               ret = -E_STREAM_PACKETIN;
+               if (ogg_stream_packetin(so, &packet) != 0)
+                       goto out;
+               /* only create a new ogg page if granulepos is valid */
+               if (packet.granulepos == -1)
+                       continue;
+               /* force remaining packets into a page */
+               for (;;) {
+#ifdef HAVE_OGG_STREAM_FLUSH_FILL
+                       ret = ogg_stream_flush_fill(so, &op, INT_MAX);
+#else
+                       ret = ogg_stream_flush(so, &op);
+#endif
+                       if (ret <= 0)
+                               break;
+                       PARA_DEBUG_LOG("writing page (%lu bytes)\n",
+                               op.header_len + op.body_len);
+                       ret = write_ogg_page(fd, &op);
+                       if (ret < 0)
+                               goto out;
+               }
+       }
+       if (ogg_stream_flush(so, &op)) {
+               /* write remaining data */
+               ret = write_ogg_page(fd, &op);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = 1;
+out:
+       ogg_sync_clear(&oss_in);
+       ogg_sync_clear(&oss_out);
+       if (si)
+               ogg_stream_clear(si);
+       if (so)
+               ogg_stream_clear(so);
+       return ret;
+}
index a4d44f4395a178c7cdfab2a0b74baba81c37aef3..7b9d1313af4816b79d1177c7898b94a70ee24775 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  */
 
 /**
- * Callback structure provided by vorbis/speex audio format handlers.
+ * Callback structure provided by audio format handlers.
  *
- * Both audio formats utilize the ogg container format. Meta info about
- * the audio file is contained in the first three ogg packets.
+ * All audio formats which utilize the ogg container format define a callback
+ * function whose purpose is to extract the meta information about the audio
+ * file from the first few ogg packets of the bitstream.
  */
 struct ogg_afh_callback_info {
        /**
-         * ogg_get_file_info() calls this function for each of the three
-         * header packets. If this callback returns a negative value, the
-         * audio file is considered invalid and the chunk table is not
-         * created. If it returns zero, the end of the header has been
-         * reached and no further ogg packets should be processed.
-         */
+        * ogg_get_file_info() calls this function for the first three ogg
+        * packets, or until the callback returns a non-positive value. If it
+        * returns negative, the audio file was not recognized by the audio
+        * format handler. If it returns zero, the audio file is considered
+        * valid, and no further processing by the callback is needed. In this
+        * case the header chunk, i.e. chunk number zero, is defined as the
+        * concatenation of all packets that have been processed by the
+        * callback.
+        */
        int (*packet_callback)(ogg_packet *packet, int packet_num,
                int serial, struct afh_info *afhi, void *private_data);
-       /** Vorbis/speex specific data. */
+       /** Data specific to the audio format handler. */
        void *private_data;
 };
 
 int ogg_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
                struct ogg_afh_callback_info *ci);
+int ogg_rewrite_tags(const char *map, size_t mapsize, int fd,
+               char *meta_packet, size_t meta_sz);
index f41150f2638c62d4dd48fc23f9705f1dcf94ac4d..4b801356075ba8b7f1ebb4280708aac94194912a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -136,14 +136,13 @@ open:
                0, /* no initial bytes */
                ovc); /* the ov_open_callbacks */
        if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) {
-               /* this might be due to the input buffer being too small */
+               /* maybe the input buffer is too small */
                if (!btr_no_parent(btrn)) {
                        fn->min_iqs += 1000;
                        iqs = btr_get_input_queue_size(btrn);
                        ret = 0;
                        if (iqs < fn->min_iqs)
                                goto out;
-                       PARA_CRIT_LOG("iqs: %zu\n", iqs);
                        btr_merge(btrn, fn->min_iqs);
                        pod->converted = 0;
                        goto open;
@@ -179,12 +178,20 @@ out:
        return ret;
 }
 
+/** Suspend decoding if output queue size is larger than that. */
 #define OGGDEC_MAX_OUTPUT_SIZE (96 * 1024)
+
+/**
+  * Allocate chunks of this size and produce at most one chunk of output per
+  * ->post_select() invocation. If the buffer could only be filled partially
+  * due to insufficient input being available, it is shrunk to the real output
+  * size and the resized buffer is fed into the output queue.
+  */
 #define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
 
-static void ogg_pre_select(struct sched *s, struct task *t)
+static void ogg_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret;
@@ -199,9 +206,9 @@ static void ogg_pre_select(struct sched *s, struct task *t)
        sched_min_delay(s);
 }
 
-static int ogg_post_select(__a_unused struct sched *s, struct task *t)
+static int ogg_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret, have;
@@ -236,13 +243,8 @@ static int ogg_post_select(__a_unused struct sched *s, struct task *t)
                        break;
                fn->min_iqs = 0;
                have += ret;
-               if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
-                       continue;
-               if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
+               if (have >= OGGDEC_OUTPUT_CHUNK_SIZE)
                        break;
-               btr_add_output(buf, have, btrn);
-               buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
-               have = 0;
        }
        pod->have_more = (ret > 0);
        if (have > 0) {
index b079bcd5f3f373ed3b9e4826f010edd0e70a6201..e7511d36325b9e067e9defa10b39ac84911b4d9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -19,6 +19,8 @@
 
 static const char* opus_suffixes[] = {"opus", NULL};
 
+#define OPUS_COMMENT_HEADER "OpusTags"
+
 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
                char **p)
 {
@@ -40,7 +42,7 @@ static int opus_get_comments(char *comments, int length,
        /* min size of a opus header is 16 bytes */
        if (length < 16)
                return -E_OPUS_COMMENT;
-       if (memcmp(p, "OpusTags", 8) != 0)
+       if (memcmp(p, OPUS_COMMENT_HEADER, strlen(OPUS_COMMENT_HEADER)) != 0)
                return -E_OPUS_COMMENT;
        p += 8;
        val = read_u32(p);
@@ -94,7 +96,8 @@ static int opus_packet_callback(ogg_packet *packet, int packet_num,
                if (ret < 0)
                        return ret;
                afhi->channels = oh->channels;
-               afhi->techinfo = make_message("header version %d, input sample rate: %dHz",
+               afhi->techinfo = make_message(
+                       "header version %d, input sample rate: %" PRIu32 "Hz",
                        oh->version, oh->input_sample_rate);
                /*
                 * The input sample rate is irrelevant for afhi->frequency as
@@ -133,6 +136,97 @@ static int opus_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        return 1;
 }
 
+static size_t opus_make_meta_packet(struct taginfo *tags, char **result)
+{
+       size_t sz;
+       char *buf, *p;
+       size_t comment_len = strlen(tags->comment),
+               artist_len = strlen(tags->artist),
+               title_len = strlen(tags->title),
+               album_len = strlen(tags->album),
+               year_len = strlen(tags->year);
+       uint32_t comment_sz = comment_len,
+               artist_sz = artist_len + strlen("artist="),
+               title_sz = title_len + strlen("title="),
+               album_sz = album_len + strlen("album="),
+               year_sz = year_len + strlen("year=");
+       uint32_t num_tags;
+
+       sz = strlen(OPUS_COMMENT_HEADER)
+               + 4 /* comment length (always present) */
+               + comment_sz
+               + 4; /* number of tags */
+       num_tags = 0;
+       if (artist_len) {
+               num_tags++;
+               sz += 4 + artist_sz;
+       }
+       if (title_len) {
+               num_tags++;
+               sz += 4 + title_sz;
+       }
+       if (album_len) {
+               num_tags++;
+               sz += 4 + album_sz;
+       }
+       if (year_len) {
+               num_tags++;
+               sz += 4 + year_sz;
+       }
+       PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
+       /* terminating zero byte for the last sprintf() */
+       buf = p = para_malloc(sz + 1);
+       memcpy(p, OPUS_COMMENT_HEADER, strlen(OPUS_COMMENT_HEADER));
+       p += strlen(OPUS_COMMENT_HEADER);
+       write_u32(p, comment_sz);
+       p += 4;
+       strcpy(p, tags->comment);
+       p += comment_sz;
+       write_u32(p, num_tags);
+       p += 4;
+       if (artist_len) {
+               write_u32(p, artist_sz);
+               p += 4;
+               sprintf(p, "artist=%s", tags->artist);
+               p += artist_sz;
+       }
+       if (title_len) {
+               write_u32(p, title_sz);
+               p += 4;
+               sprintf(p, "title=%s", tags->title);
+               p += title_sz;
+       }
+       if (album_len) {
+               write_u32(p, album_sz);
+               p += 4;
+               sprintf(p, "album=%s", tags->album);
+               p += album_sz;
+       }
+       if (year_len) {
+               write_u32(p, year_sz);
+               p += 4;
+               sprintf(p, "year=%s", tags->year);
+               p += year_sz;
+       }
+       assert(p == buf + sz);
+       *result = buf;
+       return sz;
+}
+
+static int opus_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int output_fd,
+               __a_unused const char *filename)
+{
+       char *meta_packet;
+       size_t meta_sz;
+       int ret;
+
+       meta_sz = opus_make_meta_packet(tags, &meta_packet);
+       ret = ogg_rewrite_tags(map, mapsize, output_fd, meta_packet, meta_sz);
+       free(meta_packet);
+       return ret;
+}
+
 /**
  * The init function of the ogg/opus audio format handler.
  *
@@ -142,4 +236,5 @@ void opus_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = opus_get_file_info,
        afh->suffixes = opus_suffixes;
+       afh->rewrite_tags = opus_rewrite_tags;
 }
index 927df1f3c2f881fb036b13df50fbd49b43ec568d..67a8841e8b8ce7fe172043a144332cb1db4c577f 100644 (file)
@@ -104,7 +104,7 @@ int opus_parse_header(const char *packet, int len, struct opus_header *h)
        if (!read_chars(&p, &ch, 1))
                return -E_OPUS_HEADER;
        h->version = ch;
-       if((h->version & 240) != 0) /* Only major version 0 supported. */
+       if ((h->version & 240) != 0) /* Only major version 0 supported. */
                return -E_OPUS_HEADER;
 
        if (!read_chars(&p, &ch, 1))
index cae9a6e9164de556ce5bde23de6c81aad7b476c3..2bcf5919f934622278dc2f9ba85b0516cf1d3767 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2013 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 90e65bc369571fbf9e8cde134efc07a17b76e124..6a93f41f6b25f9e9d36b58726708861a52ed6b89 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 2002-2007 Jean-Marc Valin
  * Copyright (c) 2008 CSIRO
  * Copyright (c) 2007-2012 Xiph.Org Foundation
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -72,6 +72,7 @@ struct opusdec_context {
        int channels;
        int preskip;
        bool have_opus_stream;
+       bool have_more;
        ogg_int32_t opus_serialno;
 };
 
@@ -204,60 +205,84 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
        return 1;
 }
 
-static int opusdec_post_select(__a_unused struct sched *s, struct task *t)
+#define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
+
+static int opusdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret;
-       char *btr_buf, *data;
-       size_t nbytes;
        ogg_packet op;
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret <= 0)
-               goto out;
-       btr_merge(btrn, fn->min_iqs);
-       nbytes = btr_next_buffer(btrn, &btr_buf);
-       nbytes = PARA_MIN(nbytes, (size_t)32768);
-       ret = 0;
-       if (nbytes == 0)
-               goto out;
-       data = ogg_sync_buffer(&ctx->oy, nbytes);
-       memcpy(data, btr_buf, nbytes);
-       btr_consume(btrn, nbytes);
-       ogg_sync_wrote(&ctx->oy, nbytes);
-       for (;;) { /* loop over all ogg pages we got */
-               ret = 0;
-               if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1)
+       if (ret < 0) {
+               if (ret != -E_BTR_EOF) /* fatal error */
+                       goto out;
+               if (!ctx->have_more) /* EOF */
                        goto out;
-               if (!ctx->stream_init) {
-                       ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
+       } else if (ret == 0 && !ctx->have_more) /* nothing to do */
+               goto out;
+       if (btr_get_output_queue_size(btrn) > OPUSDEC_MAX_OUTPUT_SIZE)
+               return 0;
+       for (;;) {
+               int serial;
+               if (ctx->stream_init) {
+                       ret = ogg_stream_packetout(&ctx->os, &op);
+                       if (ret == 1)
+                               break;
+               }
+               while (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1) {
+                       char *btr_buf, *data;
+                       size_t nbytes = btr_next_buffer(btrn, &btr_buf);
+                       nbytes = PARA_MIN(nbytes, (size_t)24 * 1024);
+                       //PARA_CRIT_LOG("nbytes: %d\n", nbytes);
+                       ctx->have_more = false;
+                       if (nbytes == 0)
+                               return 0;
+                       data = ogg_sync_buffer(&ctx->oy, nbytes);
+                       memcpy(data, btr_buf, nbytes);
+                       btr_consume(btrn, nbytes);
+                       ogg_sync_wrote(&ctx->oy, nbytes);
+               }
+               ctx->have_more = true;
+               serial = ogg_page_serialno(&ctx->ogg_page);
+               if (ctx->stream_init) {
+                       if (serial != ctx->os.serialno)
+                               ogg_stream_reset_serialno(&ctx->os, serial);
+               } else {
+                       ogg_stream_init(&ctx->os, serial);
                        ctx->stream_init = true;
                }
-               if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno)
-                       ogg_stream_reset_serialno(&ctx->os,
-                               ogg_page_serialno(&ctx->ogg_page));
                /* Add page to the bitstream */
                ogg_stream_pagein(&ctx->os, &ctx->ogg_page);
-               for (;;) { /* loop over all opus packets */
-                       ret = ogg_stream_packetout(&ctx->os, &op);
-                       if (ret != 1)
-                               break;
-                       ret = decode_packet(ctx, &op, btrn);
-                       if (ret < 0)
-                               goto out;
-                       ctx->packet_count++;
-                       if (ctx->eos)
-                               ctx->have_opus_stream = false;
-               }
        }
+       ret = decode_packet(ctx, &op, btrn);
+       if (ret < 0)
+               goto out;
+       ctx->packet_count++;
+       if (ctx->eos)
+               ctx->have_opus_stream = false;
 out:
        if (ret < 0)
                btr_remove_node(&fn->btrn);
        return ret;
 }
 
+static void opusdec_pre_select(struct sched *s, void *context)
+{
+       struct filter_node *fn = context;
+       struct opusdec_context *ctx = fn->private_data;
+       int ret = btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL);
+
+       if (ret != 0)
+               return sched_min_delay(s);
+       if (!ctx->have_more)
+               return;
+       if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE)
+               return sched_min_delay(s);
+}
+
 /**
  * The init function of the opusdec filter.
  *
@@ -269,7 +294,7 @@ void opusdec_filter_init(struct filter *f)
 {
        f->open = opusdec_open;
        f->close = opusdec_close;
-       f->pre_select = generic_filter_pre_select;
+       f->pre_select = opusdec_pre_select;
        f->post_select = opusdec_post_select;
        f->execute = opusdec_execute;
 }
index 5182ad238b81f706ebac2f6abdcaec3859b809dc..8e87452b816e68f03f0931e79efe007d7491b6c8 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1998-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -98,7 +98,7 @@ static int oss_mix_set_channel(struct mixer_handle *handle,
                handle->id = i;
                return 1;
        }
-       return -E_OSS_MIXER_CHANNEL;
+       return -E_BAD_CHANNEL;
 }
 
 static int oss_mix_get(struct mixer_handle *handle)
index 3c61a445eec2456ec5fdcf585a9d95753d4cd71c..12cf8b69bef2a7a189425df8ed15604f5cf10220 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -8,7 +8,6 @@
 
 #include <regex.h>
 #include <sys/ioctl.h>
-#include <fcntl.h>
 #include <sys/soundcard.h>
 
 #include "para.h"
@@ -31,6 +30,29 @@ struct private_oss_write_data {
        int bytes_per_frame;
 };
 
+/*
+ * We keep one bit of static storage to make sure only one instance of the oss
+ * writer is running at any given time.
+ */
+static bool sound_device_busy;
+
+static bool sound_device_is_busy(void)
+{
+       return sound_device_busy;
+}
+
+static void set_sound_device_busy(void)
+{
+       assert(!sound_device_busy);
+       sound_device_busy = true;
+}
+
+static void set_sound_device_idle(void)
+{
+       assert(sound_device_busy);
+       sound_device_busy = false;
+}
+
 static int get_oss_format(enum sample_format sf)
 {
        switch (sf) {
@@ -44,13 +66,13 @@ static int get_oss_format(enum sample_format sf)
        }
 }
 
-static void oss_pre_select(struct sched *s, struct task *t)
+static void oss_pre_select(struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
        int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
 
-       if (ret == 0)
+       if (ret == 0 || (sound_device_is_busy() && !powd))
                return;
        if (ret < 0 || !powd)
                return sched_min_delay(s);
@@ -65,6 +87,7 @@ static void oss_close(struct writer_node *wn)
                return;
        close(powd->fd);
        free(powd);
+       set_sound_device_idle();
 }
 
 /*
@@ -123,7 +146,7 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
         * Set sampling rate
         *
         * If we request a higher sampling rate than is supported by the
-        * device, the the highest possible speed is automatically used. The
+        * device, the highest possible speed is automatically used. The
         * value actually used is returned as the new value of the argument.
         */
        rate = sample_rate;
@@ -157,17 +180,17 @@ err_free:
        return ret;
 }
 
-static int oss_post_select(__a_unused struct sched *s,
-               struct task *t)
+static int oss_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        size_t frames, bytes;
        int ret;
        char *data;
+       audio_buf_info abi;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(wn->task);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
@@ -175,12 +198,16 @@ static int oss_post_select(__a_unused struct sched *s,
                goto out;
        if (!powd) {
                int32_t rate, ch, format;
+
+               if (sound_device_is_busy())
+                       return 0;
                get_btr_sample_rate(btrn, &rate);
                get_btr_channels(btrn, &ch);
                get_btr_sample_format(btrn, &format);
                ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
+               set_sound_device_busy();
                return 0;
        }
        btr_merge(btrn, wn->min_iqs);
@@ -193,6 +220,15 @@ static int oss_post_select(__a_unused struct sched *s,
        ret = 0;
        if (!FD_ISSET(powd->fd, &s->wfds))
                goto out;
+       /* get maximal number of bytes that can be written */
+       ret = ioctl(powd->fd, SNDCTL_DSP_GETOSPACE, &abi);
+       if (ret >= 0) {
+               size_t max_frames = abi.bytes / powd->bytes_per_frame;
+               if (max_frames == 0)
+                       goto out;
+               /* cap number of frames to avoid sound artefacts */
+               frames = PARA_MIN(frames, max_frames);
+       }
        ret = xwrite(powd->fd, data, frames * powd->bytes_per_frame);
        if (ret < 0)
                goto out;
index f1e308d8beb498586f60acbe1fa93b24077bc320..18a2c084b9dfce04a309ecf4286b01ddc81ad898 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -274,9 +274,9 @@ static inline bool need_drain_delay(struct private_osx_write_data *powd)
        return btr_get_input_queue_size(powd->callback_btrn) != 0;
 }
 
-static void osx_write_pre_select(struct sched *s, struct task *t)
+static void osx_write_pre_select(struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_osx_write_data *powd = wn->private_data;
        int ret;
        bool drain_delay_nec = false;
@@ -301,14 +301,14 @@ static void osx_write_pre_select(struct sched *s, struct task *t)
        sched_request_timeout_ms(50, s);
 }
 
-static int osx_write_post_select(__a_unused struct sched *s, struct task *t)
+static int osx_write_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_osx_write_data *powd = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        int ret;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(wn->task);
        if (ret < 0)
                goto fail;
        if (!powd) {
diff --git a/para.h b/para.h
index 4de4af4a23dd0f0a6e225279f9e72423a027adc4..f08d43a8e96cbaa607518615acdb22703a1cf292 100644 (file)
--- a/para.h
+++ b/para.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #include <limits.h>
 #include <stdarg.h>
 #include <ctype.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <sys/un.h> /* needed by create_pf_socket */
 #include <string.h>
 #include <assert.h>
 #include <stdbool.h>
+#include <inttypes.h>
+#include <sys/uio.h>
 #include "gcc-compat.h"
 
 /** used in various contexts */
@@ -62,7 +60,7 @@ extern __printf_2_3 void (*para_log)(int, const char*, ...);
  *
  */
 #define DEFINE_STDERR_LOGGER(funcname, loglevel_barrier) \
-       __printf_2_3 void funcname(int ll, const char* fmt,...) \
+       static __printf_2_3 void funcname(int ll, const char* fmt,...) \
        { \
                va_list argp; \
                if (ll < loglevel_barrier) \
@@ -82,14 +80,6 @@ extern __printf_2_3 void (*para_log)(int, const char*, ...);
 
 /** Sent by para_client to initiate the authentication procedure. */
 #define AUTH_REQUEST_MSG "auth rsa "
-/** Sent by para_server for commands that expect a data file. */
-#define AWAITING_DATA_MSG "\nAwaiting Data."
-/** Sent by para_server if authentication was successful. */
-#define PROCEED_MSG "Proceed."
-/** Length of the \p PROCEED_MSG string. */
-#define PROCEED_MSG_LEN strlen(PROCEED_MSG)
-/** Sent by para_client to indicate the end of the command line. */
-#define EOC_MSG "\nEnd of Command."
 
 /* exec */
 int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds);
@@ -98,7 +88,6 @@ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds);
 int tv_diff(const struct timeval *b, const struct timeval *a,
                struct timeval *diff);
 long unsigned tv2ms(const struct timeval *tv);
-void d2tv(double x, struct timeval *tv);
 void tv_add(const struct timeval *a, const struct timeval *b,
                struct timeval *sum);
 void tv_scale(const unsigned long mult, const struct timeval *tv,
@@ -217,7 +206,7 @@ _static_inline_ bool iov_valid(const struct iovec *iov)
  *
  *     2. The wav header (para_write only).
  *
- *     3. The --format option of para_write.
+ *     3. The --sample-format option of para_write.
  */
 #define SAMPLE_FORMATS \
        SAMPLE_FORMAT(SF_S8, "8 bit signed"), \
diff --git a/play.c b/play.c
index 18bfab98ca60a5e3982b6356c76fccc9650a20d8..55c9ec1bbb635946fe8ec92eadb52fa0accc8bf6 100644 (file)
--- a/play.c
+++ b/play.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -57,7 +57,7 @@ enum state_change_request_type {
 };
 
 struct play_task {
-       struct task task;
+       struct task *task;
        /* A bit array of invalid files (those will be skipped). */
        bool *invalid;
        /* The file which is currently open. */
@@ -184,10 +184,10 @@ static void parse_config_or_die(int argc, char *argv[])
                loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
        for (i = 0; i < conf.key_map_given; i++) {
-               char *s = strchr(conf.key_map_arg[i] + 1, ':');
-               if (s)
+               char *kma = conf.key_map_arg[i];
+               if (*kma && strchr(kma + 1, ':'))
                        continue;
-               PARA_EMERG_LOG("invalid key map arg: %s\n", conf.key_map_arg[i]);
+               PARA_EMERG_LOG("invalid key map arg: %s\n", kma);
                goto err;
        }
        free(config_file);
@@ -243,13 +243,16 @@ static void wipe_receiver_node(struct play_task *pt)
 /* returns: 0 not eof, 1: eof, < 0: fatal error.  */
 static int get_playback_error(struct play_task *pt)
 {
-       int err = pt->wn.task.error;
+       int err;
 
+       if (!pt->wn.task)
+               return 0;
+       err = task_status(pt->wn.task);
        if (err >= 0)
                return 0;
-       if (pt->fn.task.error >= 0)
+       if (task_status(pt->fn.task) >= 0)
                return 0;
-       if (pt->rn.task.error >= 0)
+       if (task_status(pt->rn.task) >= 0)
                return 0;
        if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF
                        || err == -E_WRITE_COMMON_EOF)
@@ -260,23 +263,26 @@ static int get_playback_error(struct play_task *pt)
 static int eof_cleanup(struct play_task *pt)
 {
        struct writer *w = writers + DEFAULT_WRITER;
-       struct filter *decoder = filters + pt->fn.filter_num;
+       const struct filter *decoder = filter_get(pt->fn.filter_num);
        int ret;
 
        ret = get_playback_error(pt);
        if (ret == 0)
                return ret;
        PARA_NOTICE_LOG("cleaning up wn/fn nodes\n");
+       task_reap(&pt->wn.task);
        w->close(&pt->wn);
        btr_remove_node(&pt->wn.btrn);
        w->free_config(pt->wn.conf);
        memset(&pt->wn, 0, sizeof(struct writer_node));
 
+       task_reap(&pt->fn.task);
        decoder->close(&pt->fn);
        btr_remove_node(&pt->fn.btrn);
        free(pt->fn.conf);
        memset(&pt->fn, 0, sizeof(struct filter_node));
 
+       task_reap(&pt->rn.task);
        btr_remove_node(&pt->rn.btrn);
        /*
         * On eof (ret > 0), we do not wipe the receiver node struct until a
@@ -295,7 +301,7 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b)
 
 static void shuffle(char **base, size_t num)
 {
-       srandom(now->tv_sec);
+       srandom(time(NULL));
        qsort(base, num, sizeof(char *), shuffle_compare);
 }
 
@@ -351,9 +357,6 @@ static int open_new_file(struct play_task *pt)
                free(tmp);
                tmp = NULL;
        }
-       pt->rn.task.pre_select = afh_recv->pre_select;
-       pt->rn.task.post_select = afh_recv->post_select;
-       sprintf(pt->rn.task.status, "%s receiver node", afh_recv->name);
        return 1;
 fail:
        wipe_receiver_node(pt);
@@ -363,9 +366,9 @@ fail:
 static int load_file(struct play_task *pt)
 {
        const char *af;
-       char *tmp;
+       char *tmp, buf[20];
        int ret;
-       struct filter *decoder;
+       const struct filter *decoder;
 
        btr_remove_node(&pt->rn.btrn);
        if (!pt->rn.receiver || pt->next_file != pt->current_file) {
@@ -373,7 +376,6 @@ static int load_file(struct play_task *pt)
                if (ret < 0)
                        return ret;
        } else {
-               char buf[20];
                pt->rn.btrn = new_recv_btrn(&pt->rn);
                sprintf(buf, "repos %lu", pt->start_chunk);
                ret = btr_exec_up(pt->rn.btrn, buf, &tmp);
@@ -391,10 +393,7 @@ static int load_file(struct play_task *pt)
        if (ret < 0)
                goto fail;
        pt->fn.filter_num = ret;
-       decoder = filters + ret;
-       pt->fn.task.pre_select = decoder->pre_select;
-       pt->fn.task.post_select = decoder->post_select;
-       sprintf(pt->fn.task.status, "%s decoder", af);
+       decoder = filter_get(ret);
        pt->fn.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
                        .handler = decoder->execute, .context = &pt->fn));
@@ -402,11 +401,23 @@ static int load_file(struct play_task *pt)
 
        /* setup default writer */
        pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num);
-       pt->wn.task.error = 0;
 
        /* success, register tasks */
-       register_task(&sched, &pt->rn.task);
-       register_task(&sched, &pt->fn.task);
+       pt->rn.task = task_register(
+               &(struct task_info) {
+                       .name = afh_recv->name,
+                       .pre_select = afh_recv->pre_select,
+                       .post_select = afh_recv->post_select,
+                       .context = &pt->rn
+               }, &sched);
+       sprintf(buf, "%s decoder", af);
+       pt->fn.task = task_register(
+               &(struct task_info) {
+                       .name = buf,
+                       .pre_select = decoder->pre_select,
+                       .post_select = decoder->post_select,
+                       .context = &pt->fn
+               }, &sched);
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
        return 1;
 fail:
@@ -452,7 +463,8 @@ again:
 
 static void kill_stream(struct play_task *pt)
 {
-       task_notify(&pt->wn.task, E_EOF);
+       if (pt->wn.task)
+               task_notify(pt->wn.task, E_EOF);
 }
 
 #ifdef HAVE_READLINE
@@ -621,23 +633,24 @@ static char **get_mapped_keyseqs(void)
        return result;
 }
 
-#include "play_completion.h"
+#include "play.command_list.h"
 
+typedef int play_command_handler_t(struct play_task *, int, char**);
+static play_command_handler_t PLAY_COMMAND_HANDLERS;
 
 /* defines one command of para_play */
 struct pp_command {
        const char *name;
-       int (*handler)(struct play_task *, int, char**);
+       play_command_handler_t *handler;
        const char *description;
        const char *usage;
        const char *help;
 };
 
-#include "play_command_list.h"
 static struct pp_command pp_cmds[] = {DEFINE_PLAY_CMD_ARRAY};
 #define FOR_EACH_COMMAND(c) for (c = 0; pp_cmds[c].name; c++)
 
-#include "play_completion.h"
+#include "play.completion.h"
 static struct i9e_completer pp_completers[];
 
 I9E_DUMMY_COMPLETER(jmp);
@@ -908,6 +921,8 @@ static int com_jmp(struct play_task *pt, int argc, char **argv)
                return ret;
        if (percent < 0 || percent > 100)
                return -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (percent == 100)
+               return com_next(pt, 1, (char *[]){"next", NULL});
        if (pt->playing && !pt->fn.btrn)
                return 0;
        pt->start_chunk = percent * pt->num_chunks / 100;
@@ -1016,7 +1031,7 @@ static void sigint_handler(int sig)
  * stderr. Once the i9e subsystem has been initialized, we switch to the i9e
  * log facility.
  */
-static void session_open(__a_unused struct play_task *pt)
+static void session_open(struct play_task *pt)
 {
        int ret;
        char *history_file;
@@ -1085,9 +1100,8 @@ static void session_update_time_string(struct play_task *pt, char *str, unsigned
  * terminates. Subsequent calls to i9e_get_error() then return negative and we
  * are allowed to call i9e_close() and terminate as well.
  */
-static int session_post_select(__a_unused struct sched *s, struct task *t)
+static int session_post_select(__a_unused struct sched *s, struct play_task *pt)
 {
-       struct play_task *pt = container_of(t, struct play_task, task);
        int ret;
 
        if (pt->background)
@@ -1109,9 +1123,8 @@ static int session_post_select(__a_unused struct sched *s, struct task *t)
 
 #else /* HAVE_READLINE */
 
-static int session_post_select(struct sched *s, struct task *t)
+static int session_post_select(struct sched *s, struct play_task *pt)
 {
-       struct play_task *pt = container_of(t, struct play_task, task);
        char c;
 
        if (!FD_ISSET(STDIN_FILENO, &s->rfds))
@@ -1134,9 +1147,9 @@ static void session_update_time_string(__a_unused struct play_task *pt,
 }
 #endif /* HAVE_READLINE */
 
-static void play_pre_select(struct sched *s, struct task *t)
+static void play_pre_select(struct sched *s, void *context)
 {
-       struct play_task *pt = container_of(t, struct play_task, task);
+       struct play_task *pt = context;
        char state;
 
        para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
@@ -1173,9 +1186,9 @@ static unsigned get_time_string(struct play_task *pt, char **result)
        );
 }
 
-static int play_post_select(struct sched *s, struct task *t)
+static int play_post_select(struct sched *s, void *context)
 {
-       struct play_task *pt = container_of(t, struct play_task, task);
+       struct play_task *pt = context;
        int ret;
 
        ret = eof_cleanup(pt);
@@ -1183,7 +1196,7 @@ static int play_post_select(struct sched *s, struct task *t)
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
-       ret = session_post_select(s, t);
+       ret = session_post_select(s, pt);
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
@@ -1232,7 +1245,6 @@ int main(int argc, char *argv[])
        filter_init();
        writer_init();
 
-       clock_get_realtime(now);
        sched.default_timeout.tv_sec = 5;
 
        parse_config_or_die(argc, argv);
@@ -1247,11 +1259,14 @@ int main(int argc, char *argv[])
        pt->rq = CRT_FILE_CHANGE;
        pt->current_file = conf.inputs_num - 1;
        pt->playing = true;
-       pt->task.pre_select = play_pre_select;
-       pt->task.post_select = play_post_select;
-       sprintf(pt->task.status, "play task");
-       register_task(&sched, &pt->task);
+       pt->task = task_register(&(struct task_info){
+               .name = "play",
+               .pre_select = play_pre_select,
+               .post_select = play_post_select,
+               .context = pt,
+       }, &sched);
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
index 6441aca7a8293066e70132f57231a4c9b8643bf7..01392030a00305cea41707f4e40016507f0e20e1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -95,9 +95,9 @@ static int check_playlist_path(char *path, void *data)
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
-       if (ret >= 0)
-               return 1;
-       return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+       if (ret < 0)
+               para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+       return 1; /* do not fail the loop on bad paths */
 }
 
 static int check_playlist(struct osl_row *row, void *data)
@@ -107,47 +107,33 @@ static int check_playlist(struct osl_row *row, void *data)
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
-       if (ret < 0)
-               return para_printf(pb, "failed to get playlist data: %s\n",
+       if (ret < 0) { /* log error, but continue */
+               para_printf(pb, "failed to get playlist data: %s\n",
                        para_strerror(-ret));
+               return 1;
+       }
        if (*playlist_name) { /* skip dummy row */
-               ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
-               if (ret < 0)
-                       return ret;
-               ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               para_printf(pb, "checking playlist %s...\n", playlist_name);
+               for_each_line(FELF_READ_ONLY, playlist_def.data,
                        playlist_def.size, check_playlist_path, pb);
        }
        osl_close_disk_object(&playlist_def);
-       return ret;
+       return 1;
 }
 
 /**
  * Check the playlist table for inconsistencies.
  *
- * \param fd The afs socket.
- * \param query Unused.
+ * \param aca This callback ignores ->query.
  *
- * \return The return value of the underlying call to osl_rbtree_loop().
+ * \return Standard. Invalid paths are reported, but are not considered an
+ * error.
  */
-void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
+int playlist_check_callback(struct afs_callback_arg *aca)
 {
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler,
-       };
-       int ret = para_printf(&pb, "checking playlists...\n");
-
-       if (ret < 0)
-               return;
-       osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
-               check_playlist);
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
+       para_printf(&aca->pbout, "checking playlists...\n");
+       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+               check_playlist));
 }
 
 /**
index c72d6d4951bb784816502a982185647ef9bcfe63..f1a38929043ec3b1ed3993f05ca49d7eb1b52574 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 655c981b921e0a0b484b5567ad9d448077d39631..6078da0757f885eec2fec0dcb9fbda88cb99f454 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -28,9 +28,9 @@ struct private_prebuffer_data {
        struct timeval barrier;
 };
 
-static void prebuffer_pre_select(struct sched *s, struct task *t)
+static void prebuffer_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
@@ -56,9 +56,9 @@ static void prebuffer_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
-static int prebuffer_post_select(__a_unused struct sched *s, struct task *t)
+static int prebuffer_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
@@ -70,14 +70,14 @@ static int prebuffer_post_select(__a_unused struct sched *s, struct task *t)
                return 0;
        if (iqs < conf->size_arg)
                return 0;
-       btr_splice_out_node(fn->btrn);
+       btr_splice_out_node(&fn->btrn);
        return -E_PREBUFFER_SUCCESS;
 }
 
 static int prebuffer_parse_config(int argc, char **argv, void **config)
 {
        struct prebuffer_filter_args_info *conf = para_calloc(sizeof(*conf));
-       int ret = -E_PREBUFFER_SYNTAX;
+       int ret;
 
        prebuffer_filter_cmdline_parser(argc, argv, conf);
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
diff --git a/recv.c b/recv.c
index f28809da6c2b8c5b03f975e940cbe61cbdd4b27c..f42a7dc04d70a8095cc9fe0becb298562710db61 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -65,6 +65,7 @@ int main(int argc, char *argv[])
        struct receiver_node rn;
        struct stdout_task sot = {.btrn = NULL};
        static struct sched s;
+       struct task_info ti;
 
        recv_cmdline_parser(argc, argv, &conf);
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
@@ -91,17 +92,18 @@ int main(int argc, char *argv[])
 
        sot.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.parent = rn.btrn, .name = "stdout"));
-       stdout_set_defaults(&sot);
-       register_task(&s, &sot.task);
+       stdout_task_register(&sot, &s);
 
-       rn.task.pre_select = r->pre_select;
-       rn.task.post_select = r->post_select;
-       sprintf(rn.task.status, "%s", r->name);
-       register_task(&s, &rn.task);
+       ti.name = r->name;
+       ti.pre_select = r->pre_select;
+       ti.post_select = r->post_select;
+       ti.context = &rn;
+       rn.task = task_register(&ti, &s);
 
        s.default_timeout.tv_sec = 1;
        s.default_timeout.tv_usec = 0;
        ret = schedule(&s);
+       sched_shutdown(&s);
 out:
        if (r_opened)
                r->close(&rn);
diff --git a/recv.h b/recv.h
index 68222acffc7fc41f8fa2c4d644ff70ee3c4f753d..1a0de659e92be1d6f68838c3408b8859c4278a43 100644 (file)
--- a/recv.h
+++ b/recv.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -17,7 +17,7 @@ struct receiver_node {
        /** Pointer to the configuration data for this instance. */
        void *conf;
        /** The task associated with this instance. */
-       struct task task;
+       struct task *task;
        /** The receiver node is always the root of the buffer tree. */
        struct btr_node *btrn;
        /** Each receiver node maintains a buffer pool for the received data. */
@@ -103,7 +103,7 @@ struct receiver {
         *
         * \sa select(2), time.c struct task, struct sched.
         */
-       void (*pre_select)(struct sched *s, struct task *t);
+       void (*pre_select)(struct sched *s, void *context);
        /**
         * Evaluate the result from select().
         *
@@ -115,7 +115,7 @@ struct receiver {
         *
         * \sa select(2), struct receiver.
         */
-       int (*post_select)(struct sched *s, struct task *t);
+       int (*post_select)(struct sched *s, void *context);
 
        /** The two help texts of this receiver. */
        struct ggo_help help;
@@ -142,7 +142,7 @@ struct receiver {
 void recv_init(void);
 void *check_receiver_arg(char *ra, int *receiver_num);
 void print_receiver_helps(unsigned flags);
-int generic_recv_pre_select(struct sched *s, struct task *t);
+int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
 
 /** \cond receiver */
 extern void http_recv_init(struct receiver *r);
index 2ea8a5992034419cf0375bae43851172c136b765..59630dfcc5f5f6fc5cfa973fd25c4476366b6b92 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -50,19 +50,19 @@ static void *parse_receiver_args(int receiver_num, char *options)
 }
 
 /**
- * check if given string is a valid command line for any receiver
+ * Check if the given string is a valid receiver specifier.
  *
  * \param \ra string of the form receiver_name:options
  * \param receiver_num contains the number of the receiver upon success
  *
- * This function checks whether \a ra starts with the name of a supported
- * paraslash receiver, optinally followed by a colon and any options for that
- * receiver. If a valid receiver name was found and further are present, the
- * remaining part of \a ra is passed to that receiver's config parser.
+ * This function checks whether \a ra starts with the name of a receiver,
+ * optionally followed by options for that receiver. If a valid receiver name
+ * was found the remaining part of \a ra is passed to the receiver's config
+ * parser.
  *
- * \return On success, a pointer to the gengetopt args info struct is returned
- * and \a receiver_num contains the number of the receiver. Otherwise this function
- * returns \p NULL.
+ * \return On success, a pointer to the receiver-specific gengetopt args info
+ * struct is returned and \a receiver_num contains the number of the receiver.
+ * On errors, the function returns \p NULL.
  */
 void *check_receiver_arg(char *ra, int *receiver_num)
 {
@@ -116,7 +116,7 @@ void print_receiver_helps(unsigned flags)
  * Simple pre-select hook, used by all receivers.
  *
  * \param s Scheduler info.
- * \param t Determines the receiver node.
+ * \param rn The receiver node.
  *
  * This requests a minimal delay from the scheduler if the status of the buffer
  * tree node indicates an error/eof condition. No file descriptors are added to
@@ -125,12 +125,10 @@ void print_receiver_helps(unsigned flags)
  * \return The status of the btr node of the receiver node, i.e. the return
  * value of the underlying call to \ref btr_node_status().
  */
-int generic_recv_pre_select(struct sched *s, struct task *t)
+int generic_recv_pre_select(struct sched *s, struct receiver_node *rn)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
        int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT);
 
-       t->error = 0;
        if (ret < 0)
                sched_min_delay(s);
        return ret;
index 5ad584ded50eaad435f5afdfeedccac9f40bcd75..6a285ec3bbcacfd88941aeec5d913862adeb8839 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -65,9 +65,9 @@ static void resample_open(struct filter_node *fn)
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
 
-static void resample_pre_select(struct sched *s, struct task *t)
+static void resample_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
        int ret = btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL);
 
@@ -202,10 +202,10 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
        return data.input_frames_used;
 }
 
-static int resample_post_select(__a_unused struct sched *s, struct task *t)
+static int resample_post_select(__a_unused struct sched *s, void *context)
 {
        int ret;
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
        struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
index a5a1c2594f0de72f7fc75dffeac3d93c8cf59d6c..e6f03ee835a1c65bd2bdfca060dc0538f5f1e41a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index ce89d4b792db234ea8bba7dc04f04ce1076cf08e..b422d57610e422e0d661e23b9960521be6512dbb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/sched.c b/sched.c
index d42e149801e4a7ecabd4f96cc1f79802eb7a4ff1..1db9169b38b511918cc711fc28dcb667e1242e5f 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -7,7 +7,6 @@
 /** \file sched.c Paraslash's scheduling functions. */
 
 #include <regex.h>
-#include <assert.h>
 
 #include "para.h"
 #include "ipc.h"
 #include "time.h"
 #include "error.h"
 
-static struct timeval now_struct;
-struct timeval *now = &now_struct;
-
-/*
- * Remove a task from the scheduler.
- *
- * \param t The task to remove.
- *
- * If the pre_select pointer of \a t is not \p NULL, it is removed from
- * the pre_select list of the scheduler. Same goes for \a post_select.
+/**
+ * The possible states of a task.
+ *
+ * In addition to the states listed here, a task may also enter zombie state.
+ * This happens when its ->post_select function returns negative, the ->status
+ * field is then set to this return value. Such tasks are not scheduled any
+ * more (i.e. ->pre_select() and ->post_select() are no longer called), but
+ * they stay on the scheduler task list until \ref task_reap() or
+ * \ref sched_shutdown() is called.
  */
-static void unregister_task(struct task *t)
-{
-       assert(t->error < 0);
-       PARA_INFO_LOG("unregistering %s (%s)\n", t->status,
-               para_strerror(-t->error));
-       if (t->pre_select)
-               list_del(&t->pre_select_node);
-       if (t->post_select)
-               list_del(&t->post_select_node);
-}
+enum task_status {
+       /** Task has been reaped and may be removed from the task list. */
+       TS_DEAD,
+       /** Task is active. */
+       TS_RUNNING,
+};
 
-static inline bool timeout_is_zero(struct sched *s)
-{
-       struct timeval *tv = &s->select_timeout;
-       return tv->tv_sec == 0 && tv->tv_usec == 0;
-}
+struct task {
+       /** A copy of the task name supplied when the task was registered. */
+       char *name;
+       /** Copied during task_register(). */
+       struct task_info info;
+       /* TS_RUNNING, TS_DEAD, or zombie (negative value). */
+       int status;
+       /** Position of the task in the task list of the scheduler. */
+       struct list_head node;
+       /** If less than zero, the task was notified by another task. */
+       int notification;
+};
+
+static struct timeval now_struct;
+const struct timeval *now = &now_struct;
 
 static void sched_preselect(struct sched *s)
 {
        struct task *t, *tmp;
 
-       list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+               if (t->status < 0)
+                       continue;
                if (t->notification != 0)
                        sched_min_delay(s);
-               if (t->pre_select)
-                       t->pre_select(s, t);
+               if (t->info.pre_select)
+                       t->info.pre_select(s, t->info.context);
        }
 }
 
+static void unlink_and_free_task(struct task *t)
+{
+       PARA_INFO_LOG("freeing task %s\n", t->name);
+       list_del(&t->node);
+       free(t->name);
+       free(t);
+}
+
 //#define SCHED_DEBUG 1
 static inline void call_post_select(struct sched *s, struct task *t)
 {
+       int ret;
+
 #ifndef SCHED_DEBUG
-       t->error = t->post_select(s, t);
+       ret = t->info.post_select(s, t->info.context);
 #else
        struct timeval t1, t2, diff;
        unsigned long pst;
 
        clock_get_realtime(&t1);
-       t->error = t->post_select(s, t);
+       ret = t->info.post_select(s, t->info.context);
        clock_get_realtime(&t2);
        tv_diff(&t1, &t2, &diff);
        pst = tv2ms(&diff);
        if (pst > 50)
                PARA_WARNING_LOG("%s: post_select time: %lums\n",
-                       t->status, pst);
+                       t->name, pst);
 #endif
+       t->status = ret < 0? ret : TS_RUNNING;
 }
 
-static void sched_post_select(struct sched *s)
+static unsigned sched_post_select(struct sched *s)
 {
        struct task *t, *tmp;
+       unsigned num_running_tasks = 0;
 
-       list_for_each_entry_safe(t, tmp, &s->post_select_list, post_select_node) {
-               if (t->error >= 0)
-                       call_post_select(s, t);
-//             PARA_INFO_LOG("%s: %d\n", t->status, t->ret);
-               t->notification = 0;
-               if (t->error >= 0)
-                       continue;
-               unregister_task(t);
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+               if (t->status == TS_DEAD) /* task has been reaped */
+                       unlink_and_free_task(t);
+               else if (t->status == TS_RUNNING) {
+                       call_post_select(s, t); /* sets t->status */
+                       t->notification = 0;
+                       if (t->status == TS_RUNNING)
+                               num_running_tasks++;
+               }
        }
+       return num_running_tasks;
 }
 
 /**
- * The core function for all paraslash programs.
+ * The core function of all paraslash programs.
  *
  * \param s Pointer to the scheduler struct.
  *
@@ -103,14 +123,15 @@ static void sched_post_select(struct sched *s)
  * the fd sets of \a s.  Next, it calls para_select() and makes the result available
  * to the registered tasks by calling their post_select hook.
  *
- * \return Zero if no more tasks are left in either of the two lists, negative
- * if para_select returned an error.
+ * \return Zero if no more tasks are left in the task list, negative if the
+ * select function returned an error.
  *
- * \sa task, now.
+ * \sa \ref now.
  */
 int schedule(struct sched *s)
 {
        int ret;
+       unsigned num_running_tasks;
 
        if (!s->select_function)
                s->select_function = para_select;
@@ -119,7 +140,7 @@ again:
        FD_ZERO(&s->wfds);
        s->select_timeout = s->default_timeout;
        s->max_fileno = -1;
-       clock_get_realtime(now);
+       clock_get_realtime(&now_struct);
        sched_preselect(s);
        ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
                &s->select_timeout);
@@ -135,42 +156,105 @@ again:
                FD_ZERO(&s->rfds);
                FD_ZERO(&s->wfds);
        }
-       clock_get_realtime(now);
-       sched_post_select(s);
-       if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_list))
+       clock_get_realtime(&now_struct);
+       num_running_tasks = sched_post_select(s);
+       if (num_running_tasks == 0)
                return 0;
        goto again;
 }
 
 /**
- * Add a task to the scheduler.
+ * Obtain the error status of a task and deallocate its resources.
+ *
+ * \param tptr Identifies the task to reap.
  *
- * \param t The task to add.
- * \param s The scheduler instance to add the task to.
+ * This function is similar to wait(2) in that it returns information about a
+ * terminated task and allows to release the resources associated with the
+ * task. Until this function is called, the terminated task remains in a zombie
+ * state.
  *
- * If the pre_select pointer of \a t is not \p NULL, it is added to
- * the pre_select list of the scheduler. Same goes for post_select.
+ * \return If \a tptr is \p NULL, or \a *tptr is \p NULL, the function does
+ * nothing and returns zero. Otherwise, it is checked whether the task
+ * identified by \a tptr is still running. If it is, the function returns zero
+ * and again, no action is taken. Otherwise the (negative) error code of the
+ * terminated task is returned and \a *tptr is set to \p NULL. The task will
+ * then be removed removed from the scheduler task list.
  *
- * \sa task::pre_select, task::post_select
+ * \sa \ref sched_shutdown(), wait(2).
  */
-void register_task(struct sched *s, struct task *t)
+int task_reap(struct task **tptr)
 {
-       PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
-       t->notification = 0;
-       if (!s->pre_select_list.next)
-               INIT_LIST_HEAD(&s->pre_select_list);
-       if (!s->post_select_list.next)
-               INIT_LIST_HEAD(&s->post_select_list);
-       if (t->pre_select) {
-               PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
-               list_add_tail(&t->pre_select_node, &s->pre_select_list);
-       }
-       if (t->post_select) {
-               PARA_DEBUG_LOG("post_select: %p\n", &t->post_select);
-               list_add_tail(&t->post_select_node, &s->post_select_list);
+       struct task *t;
+       int ret;
+
+       if (!tptr)
+               return 0;
+       t = *tptr;
+       if (!t)
+               return 0;
+       if (t->status >= 0)
+               return 0;
+       ret = t->status;
+       /*
+        * With list_for_each_entry_safe() it is only safe to remove the
+        * _current_ list item. Since we are being called from the loop in
+        * schedule() via some task's ->post_select() function, freeing the
+        * given task here would result in use-after-free bugs in schedule().
+        * So we only set the task status to TS_DEAD which tells schedule() to
+        * free the task in the next iteration of its loop.
+        */
+       t->status = TS_DEAD;
+
+       *tptr = NULL;
+       return ret;
+}
+
+/**
+ * Deallocate all resources of all tasks of a scheduler instance.
+ *
+ * \param s The scheduler instance.
+ *
+ * This should only be called after \ref schedule() has returned.
+ */
+void sched_shutdown(struct sched *s)
+{
+       struct task *t, *tmp;
+
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
+               if (t->status == TS_RUNNING)
+                       /* The task list should contain only terminated tasks. */
+                       PARA_WARNING_LOG("shutting down running task %s\n",
+                               t->name);
+               unlink_and_free_task(t);
        }
 }
 
+/**
+ * Add a task to the scheduler task list.
+ *
+ * \param info Task information supplied by the caller.
+ * \param s The scheduler instance.
+ *
+ * \return A pointer to a newly allocated task structure. It will be
+ * freed by sched_shutdown().
+ */
+struct task *task_register(struct task_info *info, struct sched *s)
+{
+       struct task *t = para_malloc(sizeof(*t));
+
+       assert(info->post_select);
+
+       if (!s->task_list.next)
+               INIT_LIST_HEAD(&s->task_list);
+
+       t->info = *info;
+       t->name = para_strdup(info->name);
+       t->notification = 0;
+       t->status = TS_RUNNING;
+       list_add_tail(&t->node, &s->task_list);
+       return t;
+}
+
 /**
  * Get the list of all registered tasks.
  *
@@ -186,21 +270,15 @@ char *get_task_list(struct sched *s)
        struct task *t, *tmp;
        char *msg = NULL;
 
-       list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
-               char *tmp_msg;
-               tmp_msg = make_message("%s%p\tpre\t%s\n", msg? msg : "", t, t->status);
-               free(msg);
-               msg = tmp_msg;
-       }
-       list_for_each_entry_safe(t, tmp, &s->post_select_list, post_select_node) {
+       list_for_each_entry_safe(t, tmp, &s->task_list, node) {
                char *tmp_msg;
-//             if (t->pre_select)
-//                     continue;
-               tmp_msg = make_message("%s%p\tpost\t%s\n", msg? msg : "", t, t->status);
+               tmp_msg = make_message("%s%p\t%s\t%s\n", msg? msg : "", t,
+                       t->status == TS_DEAD? "dead" :
+                               (t->status == TS_RUNNING? "running" : "zombie"),
+                       t->name);
                free(msg);
                msg = tmp_msg;
        }
-       //PARA_DEBUG_LOG("task list:\n%s", msg);
        return msg;
 }
 
@@ -228,7 +306,7 @@ void task_notify(struct task *t, int err)
        assert(err > 0);
        if (t->notification == -err) /* ignore subsequent notifications */
                return;
-       PARA_INFO_LOG("notifying task %s: %s\n", t->status, para_strerror(err));
+       PARA_INFO_LOG("notifying task %s: %s\n", t->name, para_strerror(err));
        t->notification = -err;
 }
 
@@ -243,11 +321,30 @@ void task_notify(struct task *t, int err)
  *
  * \sa \ref task_notify().
  */
-int task_get_notification(struct task *t)
+int task_get_notification(const struct task *t)
 {
        return t->notification;
 }
 
+/**
+ * Return the status value of a task.
+ *
+ * \param t The task to get the status value from.
+ *
+ * \return Zero if task does not exist, one if task is running, negative error
+ * code if task has terminated.
+ */
+int task_status(const struct task *t)
+{
+       if (!t)
+               return 0;
+       if (t->status == TS_DEAD) /* pretend dead tasks don't exist */
+               return 0;
+       if (t->status == TS_RUNNING)
+               return 1;
+       return t->status;
+}
+
 /**
  * Set the notification value of all tasks of a scheduler instance.
  *
@@ -261,9 +358,7 @@ void task_notify_all(struct sched *s, int err)
 {
        struct task *t;
 
-       list_for_each_entry(t, &s->pre_select_list, pre_select_node)
-               task_notify(t, err);
-       list_for_each_entry(t, &s->post_select_list, post_select_node)
+       list_for_each_entry(t, &s->task_list, node)
                task_notify(t, err);
 }
 
diff --git a/sched.h b/sched.h
index 234a8458d5870a1f14c66c81c140b61c0c20bf01..ada1cc106c068706fc175cc883450c17a237f4b7 100644 (file)
--- a/sched.h
+++ b/sched.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 /**
  * Paraslash's scheduler.
  *
- * Designed with KISS in mind. It manages two lists of tasks.  The pre_select
- * list contains pointers to functions that are called before calling select()
- * from the main loop. Similarly, \a post_select_list is a list of function
- * pointers each of which is called after the select call. Tasks add hooks to
- * these lists by registering themselves to the scheduler.
+ * Designed with KISS in mind. It maintains a list of task structures which is
+ * extended when a new task is registered. Each task may define a pre_select
+ * function which is called from the scheduler main loop before it calls
+ * select(). Similarly, each task must define a post_select function which is
+ * called after the select call.
  */
 struct sched {
        /** Initial value before any pre_select call. */
@@ -29,46 +29,37 @@ struct sched {
        int max_fileno;
        /** If non-NULL, use this function instead of para_select. */
        int (*select_function)(int, fd_set *, fd_set *, struct timeval *);
-       /** Currently active pre_select functions. */
-       struct list_head pre_select_list;
-       /** Currently active post_select functions. */
-       struct list_head post_select_list;
+       /** Tasks which have been registered to the scheduler. */
+       struct list_head task_list;
 };
 
-/**
- * Paraslash's task structure.
- *
- * Before registering a task to the scheduler, the task structure must be
- * filled in properly by the caller.
- *
- * \sa \ref sched.
- */
-struct task {
+struct task;
+
+/** Information that must be supplied by callers of \ref task_register(). */
+struct task_info {
+       /** Used for log messages and by \ref get_task_list(). */
+       const char *name;
        /**
-        * The pre select hook of \a t.
+        * The optional pre select method.
         *
         * Its purpose is to add file descriptors to the fd sets of the
         * scheduler and to decrease the select timeout if necessary.
         */
-       void (*pre_select)(struct sched *s, struct task *t);
+       void (*pre_select)(struct sched *s, void *context);
        /**
-        * The post select hook of \a t.
+        * The mandatory post select method.
         *
         * Its purpose is to evaluate and act upon the results of the previous
         * select call. If this function returns a negative value, the
         * scheduler unregisters the task.
         */
-       int (*post_select)(struct sched *s, struct task *t);
-       /** Whether this task is in error state. */
-       int error;
-       /** Position of the task in the pre_select list of the scheduler. */
-       struct list_head pre_select_node;
-       /** Position of the task in the post_select list of the scheduler. */
-       struct list_head post_select_node;
-       /** Descriptive text and current status of the task. */
-       char status[255];
-       /** If less than zero, the task was notified by another task. */
-       int notification;
+       int (*post_select)(struct sched *s, void *context);
+       /**
+        * This pointer is saved when the task is registered. It is passed to
+        * ->pre_select() and ->post_select(). Usually this is a pointer to the
+        * struct owned by the caller which contains the task pointer.
+        */
+       void *context;
 };
 
 /**
@@ -77,14 +68,17 @@ struct task {
  * scheduler are allowed to block, this value should be accurate enough so that
  * there is no need to call clock_gettime() directly.
  */
-extern struct timeval *now;
+extern const struct timeval *now;
 
-void register_task(struct sched *s, struct task *t);
+struct task *task_register(struct task_info *info, struct sched *s);
 int schedule(struct sched *s);
+void sched_shutdown(struct sched *s);
 char *get_task_list(struct sched *s);
 void task_notify(struct task *t, int err);
 void task_notify_all(struct sched *s, int err);
-int task_get_notification(struct task *t);
+int task_get_notification(const struct task *t);
+int task_status(const struct task *t);
+int task_reap(struct task **tptr);
 void sched_min_delay(struct sched *s);
 void sched_request_timeout(struct timeval *to, struct sched *s);
 void sched_request_timeout_ms(long unsigned ms, struct sched *s);
diff --git a/score.c b/score.c
index ca94e8de3d645015b45ae0462938f1e2e4ebe559..81b3ded0021f41115dcc78a8f9605d722195c389 100644 (file)
--- a/score.c
+++ b/score.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -71,7 +71,7 @@ static struct osl_column_description score_cols[] = {
        },
        [SCORECOL_SCORE] = {
                .storage_type = OSL_NO_STORAGE,
-               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE,
+               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
                .name = "score",
                .compare_function = score_compare,
                .data_size = sizeof(long)
@@ -168,13 +168,11 @@ static int get_nth_score(unsigned n, long *score)
  * \param aft_row Determines the audio file to change.
  * \param percent The position to re-insert the audio file.
  *
- * The percent parameter must be between \p 0 and 100 and. A value of zero
- * means to re-insert the audio file into the score table with a score lower
- * than any other admissible file.
+ * The percent parameter must be between 0 and 100. A value of zero means to
+ * re-insert the audio file into the score table with a score lower than any
+ * other admissible file.
  *
- * \return Positive on success, negative on errors. Possible errors: Errors
- * returned by osl_get_row(), get_num_admissible_files(), osl_get_nth_row(),
- * osl_get_object(), osl_update_object().
+ * \return Positive on success, negative on errors.
  */
 int score_update(const struct osl_row *aft_row, long percent)
 {
@@ -247,29 +245,12 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row,
  *
  * This is used for the ls command. The \a data parameter is passed as the
  * second argument to \a func.
- *
- * \sa admissible_file_loop_reverse().
  */
 int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
 {
        return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func));
 }
 
-/**
- * Loop over all files in the score table in reverse order.
- *
- * \param data As in admissible_file_loop().
- * \param func As in admissible_file_loop().
- *
- * \return Same return value as admissible_file_loop().
- *
- * \sa admissible_file_loop(), osl_rbtree_loop_reverse().
- */
-int admissible_file_loop_reverse(void *data, osl_rbtree_loop_func *func)
-{
-       return osl(osl_rbtree_loop_reverse(score_table, SCORECOL_SCORE, data, func));
-}
-
 /**
  * Get the admissible audio file with highest score.
  *
diff --git a/send.h b/send.h
index 01e0d0a850586312d4c6f4376ee7373ccdde9f93..0c74f0ea93208ec6b24878c963e671a4da8adb8d 100644 (file)
--- a/send.h
+++ b/send.h
@@ -1,13 +1,26 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file send.h Sender-related defines and structures. */
 
-/** The sender subcommands. */
-enum {SENDER_ADD, SENDER_DELETE, SENDER_ALLOW, SENDER_DENY, SENDER_ON, SENDER_OFF, NUM_SENDER_CMDS};
+#define SENDER_SUBCOMMANDS \
+       SENDER_SUBCOMMAND(add) /**< Add a target (udp only). */ \
+       SENDER_SUBCOMMAND(delete) /**< Delete a target (udp only). */ \
+       SENDER_SUBCOMMAND(allow) /**< Allow connections from given IP address(es). */ \
+       SENDER_SUBCOMMAND(deny) /**< Deny connections from given IP address(es). */ \
+       SENDER_SUBCOMMAND(on) /**< Activate the sender. */ \
+       SENDER_SUBCOMMAND(off) /**< Deactivate the sender. */ \
+
+#define SENDER_SUBCOMMAND(_name) SENDER_ ## _name,
+enum sender_subcommand {
+       SENDER_SUBCOMMANDS
+       NUM_SENDER_CMDS /**< Used as array size in struct \ref sender. */
+};
+#undef SENDER_SUBCOMMAND
+#define SENDER_SUBCOMMAND(_name) #_name,
 
 /**
  * Describes one supported sender of para_server.
@@ -37,7 +50,7 @@ struct sender {
         *
         * The result must be dynamically allocated and is freed by the caller.
         */
-       char* (*info)(void);
+       char* (*status)(void);
        /**
         * The send-hook.
         *
@@ -171,7 +184,7 @@ void shutdown_client(struct sender_client *sc, struct sender_status *ss);
 void shutdown_clients(struct sender_status *ss);
 void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args,
        int port, int max_clients, int default_deny);
-char *get_sender_info(struct sender_status *ss, const char *name);
+char *generic_sender_status(struct sender_status *ss, const char *name);
 
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
index 250a2a0bc5a80f8973b33d43bcb2e12e3ee723e9..ec7ab67108334c85085b46b84ada1db45eb91a82 100644 (file)
@@ -1,13 +1,18 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file send_common.c Functions used by more than one paraslash sender. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
@@ -154,7 +159,7 @@ void init_sender_status(struct sender_status *ss, char **access_arg,
  *
  * \return The string printed in the "si" command.
  */
-char *get_sender_info(struct sender_status *ss, const char *name)
+char *generic_sender_status(struct sender_status *ss, const char *name)
 {
        char *clnts = NULL, *ret;
        struct sender_client *sc, *tmp_sc;
@@ -166,14 +171,12 @@ char *get_sender_info(struct sender_status *ss, const char *name)
                clnts = tmp;
        }
        ret = make_message(
-               "%s sender:\n"
-               "\tstatus: %s\n"
-               "\tport: %s\n"
-               "\tnumber of connected clients: %d\n"
-               "\tmaximal number of clients: %d%s\n"
-               "\tconnected clients: %s\n"
-               "\taccess %s list: %s\n",
-               name,
+               "status: %s\n"
+               "port: %s\n"
+               "number of connected clients: %d\n"
+               "maximal number of clients: %d%s\n"
+               "connected clients: %s\n"
+               "access %s list: %s\n",
                (ss->listen_fd >= 0)? "on" : "off",
                stringify_port(ss->port, strcmp(name, "http") ? "dccp" : "tcp"),
                ss->num_clients,
index f26abef5753204c4628c4183519f50f02b8f674e..71a9ec505f6d096a1dc589a9cdf6c1f484e5090d 100644 (file)
--- a/server.c
+++ b/server.c
@@ -1,74 +1,45 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file server.c Paraslash's main server. */
 
-
 /**
- * \mainpage Paraslash API Reference
- *
- * Starting points for getting an overview:
- *
- *
- *     - The main programs: \ref server.c, \ref audiod.c, \ref client.c,
- *       \ref audioc.c, \ref afh.c, \ref play.c,
- *     - Server: \ref server_command, \ref sender,
- *     - Audio file selector: \ref audio_format_handler, \ref afs_table,
- *     - Client: \ref receiver, \ref receiver_node, \ref filter,
- *       \ref filter_node, \ref writer_node.
- *
- *
- * The gory details, listed by topic:
- *
- *     - Audio format handlers: \ref send_common.c \ref mp3_afh.c,
- *       \ref ogg_afh.c, \ref aac_afh.c, \ref wma_afh.c, \ref spx_afh.c
- *     - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c,
- *       \ref aacdec_filter.c, \ref wmadec_filter.c, spxdec_filter.c,
- *       \ref flacdec_filter.c,
- *     - Volume normalizer: \ref compress_filter.c,
- *     - Output: \ref alsa_write.c, \ref osx_write.c, \ref oss_write.c,
- *     - http: \ref http_recv.c, \ref http_send.c,
- *     - udp: \ref udp_recv.c, \ref udp_send.c,
- *     - dccp: \ref dccp_recv.c, \ref dccp_send.c,
- *     - Audio file selector: \ref afs.c, \ref aft.c, \ref mood.c,
- *     - Afs structures: \ref afs_table, \ref audio_file_data,
- *       \ref afs_info \ref afh_info,
- *     - Afs tables: \ref aft.c, \ref mood.c, \ref playlist.c,
- *       \ref attribute.c, \ref score.c,
- *     - The virtual streaming system: \ref vss.c, \ref chunk_queue.c.
+ * \mainpage Main data structures and selected APIs:
  *
- * Lower levels:
- *
- *     - Scheduling: \ref sched.c, \ref sched.h,
- *     - Networking: \ref net.c,
- *     - File descriptors: \ref fd.c,
- *     - Signals: \ref signal.c,
- *     - Daemons: \ref daemon.c,
- *     - Strings: \ref string.c, \ref string.h,
+ *     - Senders: \ref sender,
+ *     - Audio file selector: \ref afs_info, \ref afs_table,
+ *     - Audio format handler: \ref audio_format_handler, \ref afh_info
+ *     - Receivers/filters/writers: \ref receiver, \ref receiver_node,
+ *       \ref filter, \ref filter_node, \ref writer_node, \ref writer.
+ *     - Scheduling: \ref sched.h,
+ *     - Buffer trees: \ref buffer_tree.h,
+ *     - Sideband API: \ref sideband.h,
+ *     - Crypto: \ref crypt.h, \ref crypt_backend.h,
+ *     - Error subsystem: \ref error.h, \ref error2.c,
+ *     - Inter process communication: \ref ipc.h,
+ *     - Forward error correction: \ref fec.h,
+ *     - Daemons: \ref daemon.h,
+ *     - Mixer API: \ref mix.h,
+ *     - Interactive sessions: \ref interactive.h,
+ *     - File descriptors: \ref fd.h,
+ *     - Signals: \ref signal.h,
+ *     - Networking: \ref net.h,
  *     - Time: \ref time.c,
- *     - Spawning processes: \ref exec.c,
- *     - Inter process communication: \ref ipc.c,
- *     - Blob tables: \ref blob.c,
- *     - The error subsystem: \ref error.h.
- *     - Access control for paraslash senders: \ref acl.c, \ref acl.h.
- *     - Internal crypto API: \ref crypt.h.
- *     - interactive sessions (libreadline): \ref interactive.c.
- *
- * Low-level data structures:
- *
- *     - Doubly linked lists: \ref list.h,
- *     - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h,
- *     - openssl: \ref crypt.c
- *     - libgcrypt: \ref gcrypt.c
- *     - Forward error correction: \ref fec.c.
+ *     - Doubly linked lists: \ref list.h.
  */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <signal.h>
 #include <regex.h>
 #include <osl.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
@@ -104,7 +75,7 @@ INIT_SERVER_ERRLISTS;
 
 /**
  * Pointer to shared memory area for communication between para_server
- * and its children. Exported to vss.c. command.c and to afs.
+ * and its children. Exported to vss.c, command.c and to afs.
  */
 struct misc_meta_data *mmd;
 
@@ -126,6 +97,7 @@ int mmd_mutex;
 static char *user_list_file = NULL;
 
 static struct sched sched;
+static struct signal_task *signal_task;
 
 /** The task responsible for server command handling. */
 struct server_command_task {
@@ -136,30 +108,20 @@ struct server_command_task {
        /** Argument vector passed to para_server's main function. */
        char **argv;
        /** The command task structure for scheduling. */
-       struct task task;
+       struct task *task;
 };
 
-static int want_colors(void)
-{
-       if (conf.color_arg == color_arg_no)
-               return 0;
-       if (conf.color_arg == color_arg_yes)
-               return 1;
-       if (conf.logfile_given)
-               return 0;
-       return isatty(STDERR_FILENO);
-}
-
-static void init_colors_or_die(void)
+/**
+ * Return the list of tasks for the server process.
+ *
+ * This is called from \a com_tasks(). The helper is necessary since command
+ * handlers can not access the scheduler structure directly.
+ *
+ * \return A dynamically allocated string that must be freed by the caller.
+ */
+char *server_get_tasks(void)
 {
-       int i;
-
-       if (!want_colors())
-               return;
-       daemon_set_flag(DF_COLOR_LOG);
-       daemon_set_default_log_colors();
-       for (i = 0; i < conf.log_color_given; i++)
-               daemon_set_log_color_or_die(conf.log_color_arg[i]);
+       return get_task_list(&sched);
 }
 
 /*
@@ -244,7 +206,9 @@ void parse_config_or_die(int override)
                daemon_set_logfile(conf.logfile_arg);
                daemon_open_log_or_die();
        }
-       init_colors_or_die();
+
+       daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+               conf.logfile_given, conf.log_color_arg, conf.log_color_given);
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
        daemon_set_flag(DF_LOG_TIME);
@@ -261,12 +225,6 @@ out:
        exit(EXIT_FAILURE);
 }
 
-static void signal_pre_select(struct sched *s, struct task *t)
-{
-       struct signal_task *st = container_of(t, struct signal_task, task);
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-}
-
 /*
  * called when server gets SIGHUP or when client invokes hup command.
  */
@@ -279,7 +237,7 @@ static void handle_sighup(void)
                kill(mmd->afs_pid, SIGHUP);
 }
 
-static int signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused void *context)
 {
        int signum = para_next_signal(&s->rfds);
 
@@ -335,33 +293,31 @@ cleanup:
 
 static void init_signal_task(void)
 {
-       static struct signal_task signal_task_struct,
-               *st = &signal_task_struct;
-
-       st->task.pre_select = signal_pre_select;
-       st->task.post_select = signal_post_select;
-       sprintf(st->task.status, "signal task");
-
-       PARA_NOTICE_LOG("setting up signal handling\n");
-       st->fd = para_signal_init(); /* always successful */
+       signal_task = signal_init_or_die();
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGHUP);
        para_install_sighandler(SIGCHLD);
        para_sigaction(SIGPIPE, SIG_IGN);
-       add_close_on_fork_list(st->fd);
-       register_task(&sched, &st->task);
+       add_close_on_fork_list(signal_task->fd);
+       signal_task->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = signal_post_select,
+               .context = signal_task,
+
+       }, &sched);
 }
 
-static void command_pre_select(struct sched *s, struct task *t)
+static void command_pre_select(struct sched *s, void *context)
 {
-       struct server_command_task *sct = container_of(t, struct server_command_task, task);
+       struct server_command_task *sct = context;
        para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
 }
 
-static int command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, void *context)
 {
-       struct server_command_task *sct = container_of(t, struct server_command_task, task);
+       struct server_command_task *sct = context;
 
        int new_fd, ret, i;
        char *peer_name;
@@ -391,6 +347,9 @@ static int command_post_select(struct sched *s, struct task *t)
                goto out;
        }
        if (child_pid) {
+               /* avoid problems with non-fork-safe PRNGs */
+               unsigned char buf[16];
+               get_random_bytes_or_die(buf, sizeof(buf));
                close(new_fd);
                /* parent keeps accepting connections */
                return 0;
@@ -399,7 +358,7 @@ static int command_post_select(struct sched *s, struct task *t)
        free(chunk_table);
        alarm(ALARM_TIMEOUT);
        close_listed_fds();
-       para_signal_shutdown();
+       signal_shutdown(signal_task);
        /*
         * put info on who we are serving into argv[0] to make
         * client ip visible in top/ps
@@ -422,8 +381,6 @@ static void init_server_command_task(int argc, char **argv)
                *sct = &server_command_task_struct;
 
        PARA_NOTICE_LOG("initializing tcp command socket\n");
-       sct->task.pre_select = command_pre_select;
-       sct->task.post_select = command_post_select;
        sct->argc = argc;
        sct->argv = argv;
        ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
@@ -434,8 +391,12 @@ static void init_server_command_task(int argc, char **argv)
        if (ret < 0)
                goto err;
        add_close_on_fork_list(sct->listen_fd); /* child doesn't need the listener */
-       sprintf(sct->task.status, "server command task");
-       register_task(&sched, &sct->task);
+       sct->task = task_register(&(struct task_info) {
+               .name = "server command",
+               .pre_select = command_pre_select,
+               .post_select = command_post_select,
+               .context = sct,
+       }, &sched);
        return;
 err:
        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
@@ -502,14 +463,13 @@ static void server_init(int argc, char **argv)
        version_handle_flag("server", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
-       drop_privileges_or_die(conf.user_arg, conf.group_arg);
+       daemon_set_priority(conf.priority_arg);
+       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
        /* parse config file, open log and set defaults */
        parse_config_or_die(0);
-       log_welcome("para_server");
+       daemon_log_welcome("server");
        init_ipc_or_die(); /* init mmd struct and mmd->lock */
-       /* make sure, the global now pointer is uptodate */
-       clock_get_realtime(now);
-       set_server_start_time(now);
+       daemon_set_start_time();
        init_user_list(user_list_file);
        /* become daemon */
        if (conf.daemon_given)
@@ -547,7 +507,7 @@ static void server_init(int argc, char **argv)
 static void status_refresh(void)
 {
        static int prev_uptime = -1, prev_events = -1;
-       int uptime = get_server_uptime(now);
+       int uptime = daemon_get_uptime(now);
 
        if (prev_events != mmd->events)
                goto out;
@@ -596,6 +556,7 @@ int main(int argc, char *argv[])
        server_init(argc, argv);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
index 83097f1f2862a719d0b002c229d3b8fc102e5f8e..5f46ba1eae979b7d1da428d0cd0a069f23640ee5 100644 (file)
@@ -4,15 +4,14 @@ SN: list of server commands
 ---
 N: ff
 P: VSS_READ | VSS_WRITE
-D: Jump amount of time forwards or backwards in current audio file.
+D: Jump N seconds forward or backward.
 U: ff n[-]
-H: Set the 'R' (reposition request) bit of the vss status flags
-H: and enqueue a request to jump n seconds forwards or backwards
-H: in the current audio file.
+H: This sets the 'R' (reposition request) bit of the vss status flags
+H: which enqueues a request to jump n seconds forwards or backwards.
 H:
-H: EXAMPLE
+H: Example:
 H:
-H:     ff 30-
+H:     para_client ff 30-
 H:
 H: jumps 30 seconds backwards.
 ---
@@ -21,41 +20,41 @@ P: 0
 D: Print online help.
 U: help [command]
 H: Without any arguments, help prints a list of available commands. When
-H: issued with a command name as first argument, print out a description
-H: for that command.
+H: called with a command name as first argument, it prints the description
+H: of that command.
 ---
 N: hup
 P: VSS_WRITE
-D: Force reload of config file, log file and user list.
+D: Reload config file, log file and user list.
 U: hup
 H: Reread the config file and the user list file, close and reopen the log
-H: file, and tell all children to do the same.
+H: file, and ask the afs process to do the same. Sending the HUP signal to
+H: the server process has the same effect.
 ---
 N: jmp
 P: VSS_READ | VSS_WRITE
-D: Jump to given position in current audio file.
-U: jmp [n]
-H: Set the 'R' (reposition request) bit of the vss status flags
-H: and enqueue a request to jump to n% of the current audio file,
-H: where 0 <= n <= 100.
+D: Jump to the given position.
+U: jmp n
+H: Set the 'R' (reposition request) bit of the vss status flags and enqueue a
+H: request to jump to n% of the current audio file, where 0 <= n <= 100.
 ---
 N: next
 P: VSS_READ | VSS_WRITE
-D: Skip rest of the current audio file.
+D: Close the current audio file.
 U: next
-H: Set the 'N' (next audio file) bit of the vss status flags. When
-H: playing, change audio file immediately. Equivalent to stop
-H: if paused, NOP if stopped.
+H: Set the 'N' (next audio file) bit of the vss status flags which instructs the
+H: server to close its current audio file if necessary. If the 'P' bit (playing)
+H: is on, playing continues with the next audio file. This command is equivalent
+H: to stop if paused, and has no effect if stopped.
 ---
 N: nomore
 P: VSS_READ | VSS_WRITE
 D: Stop playing after current audio file.
 U: nomore
-H: Set the 'O' (no more) bit of the vss status flags. This instructs
-H: para_server to clear the 'P' (playing) bit as soon as it encounters
-H: the 'N' (next audio file) bit being set.
-H: Use this command instead of stop if you don't like
-H: sudden endings.
+H: Set the 'O' (no more) bit of the vss status flags which asks para_server to
+H: clear the 'P' (playing) bit after the 'N' (next audio file) bit transitions
+H: from off to on (because the end of the current audio file is reached). Use this
+H: command instead of stop if you don't like sudden endings.
 ---
 N: pause
 P: VSS_READ | VSS_WRITE
@@ -65,54 +64,66 @@ H: Clear the 'P' (playing) bit of the vss status flags.
 ---
 N: play
 P: VSS_READ | VSS_WRITE
-D: Start playing or resume playing when paused.
+D: Start or resume playing.
 U: play
-H: Set the 'P' (playing) bit of the vss status flags. This
-H: results in starting/continuing to stream.
+H: Set the 'P' (playing) bit of the vss status flags.
 ---
 N: sender
 P: VSS_READ | VSS_WRITE
-D: Control paraslash internal senders.
+D: Control paraslash senders.
 U: sender [s cmd [arguments]]
-H: send command cmd to sender s. cmd may be one of the following:
-H: help, on, off, add, delete, allow, or deny. Note that not all senders
-H: support each command. Try e.g. 'para_client sender http help' for
-H: more information about the http sender. If no argument is given,
-H: print out a list of all senders that are compiled in.
+H: Send a command to a specific sender. The following commands are available, but
+H: not all senders support every command.
+H:
+H:     help, on, off, add, delete, allow, deny, status.
+H:
+H: The help command prints the help text of the given sender. If no command is
+H: given the list of compiled in senders is shown.
+H:
+H: Example:
+H:
+H:     para_client sender http help
 ---
 N: si
 P: 0
 D: Print server info.
 U: si
-H: Print server uptime and other information.
+H: Show server and afs PID, number of connections, uptime and more.
 ---
 N: stat
 P: VSS_READ
-D: Print status info for the current audio file.
+D: Print information about the current audio file.
 U: stat [-n=num] [-p]
-H: If -n is given, the command exits after having displayed the status n
-H: times. Otherwise, the command runs in an endless loop.
+H: If -n is given, exit after the status information has been shown n times.
+H: Otherwise, the command runs in an endless loop.
 H:
 H: The -p option activates parser-friendly output: Each status item is
-H: prefixed with its size in bytes and the status items identifiers are
+H: prefixed with its size in bytes and the status item identifiers are
 H: printed as numerical values.
 ---
 N: stop
 P: VSS_READ | VSS_WRITE
-D: Stop streaming.
+D: Stop playing.
 U: stop
-H: Clear the 'P' (play) bit and set the 'N' bit of the vss status
-H: flags.
+H: Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of the vss
+H: status flags, effectively stopping playback.
+---
+N: tasks
+P: 0
+D: List server tasks.
+U: tasks
+H: For each task, print ID, status and name.
 ---
 N: term
 P: VSS_READ | VSS_WRITE
-D: Terminate para_server.
+D: Ask the server to terminate.
 U: term
-H: Shuts down the server. Instead of this command, you can also send
-H: SIGINT or SIGTERM. It should never be necessary to send SIGKILL.
+H: Shut down the server. Instead of this command, you can also send SIGINT or
+H: SIGTERM to the para_server process. It should never be necessary to send
+H: SIGKILL.
 ---
 N: version
 P: 0
-D: Print server's version.
+D: Print the git version string of para_server.
 U: version
-H: Show version and other info
+H: Show version and other info.
index 820295a3ed8f627431081e8eecc540e43c9a19c1..8de691ca321bd49db2d7c25398537f31b7d03a0c 100644 (file)
--- a/server.h
+++ b/server.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 #define MMD_INFO_SIZE 16384
 
 /** The maximum length of the host component in an URL */
-#define MAX_HOSTLEN    256
+#define MAX_HOSTLEN 256
 
 
-/** Holds the arguments for the para_server's sender command. */
-struct sender_command_data{
-       /** Greater than 0 indicates that a sender cmd is already queued. */
+/** Arguments for the sender command. */
+struct sender_command_data {
+       /** Greater than zero indicates that a sender cmd is already queued. */
        int cmd_num;
        /** The number of the sender in question. */
        int sender_num;
@@ -34,35 +34,31 @@ struct sender_command_data{
 };
 
 /**
- * Used for parent-child communication.
+ * Miscellaneous data for communication between server and command handlers.
  *
- * There's only one struct of this type which lives in shared memory
- * for communication between the server instances. Access to this
- * area is serialized via mmd_lock() and mmd_unlock(). There are two
- * reasons for a variable to be included here:
+ * There's only one instance of this structure which lives in a shared memory
+ * area. Command handlers communicate with the server process through this
+ * area. Changes made by the command handlers stay after the command handler
+ * exits. Conversely, changes made by the server process propagate to the
+ * command handlers. Access to this area is serialized via mmd_lock() and
+ * mmd_unlock().
  *
- *     - At least one command (i.e. child of the server) must be able to
- *     change its value.
- *
- * or
- *
- *     - The contents are listed in the stat command and have to be up to
- *     date.
+ * There are two reasons for a variable to be included here: (a) at least one
+ * command handler changes its value, or (b) updates by the server must
+ * propagate to the stat command handlers.
  */
 struct misc_meta_data {
        /** The size of the current audio file in bytes. */
        size_t size;
-       /** The last modification time of the current audio file. */
-       time_t mtime;
        /** The "old" status flags -- commands may only read them. */
        unsigned int vss_status_flags;
        /** The new status flags -- commands may set them. */
        unsigned int new_vss_status_flags;
-       /** The number of data chunks sent for the current audio file. */
+       /** The number of data chunks sent so far. */
        long unsigned chunks_sent;
        /** Set by the jmp/ff commands to the new position in chunks. */
        long unsigned repos_request;
-       /** The number of the chunk currently sent out. */
+       /** The number of the chunk currently being sent. */
        long unsigned current_chunk;
        /** The milliseconds that have been skipped of the current audio file. */
        long offset;
@@ -71,8 +67,8 @@ struct misc_meta_data {
        /**
         * The event counter.
         *
-        * Commands may increase this to force a status update to be sent to all
-        * connected clients.
+        * Commands may increase this to force a status update to be sent to
+        * all connected stat clients.
         */
        unsigned int events;
        /** The number of audio files already sent. */
@@ -98,3 +94,4 @@ extern struct server_args_info conf;
 
 __noreturn void handle_connect(int fd, const char *peername);
 void parse_config_or_die(int override);
+char *server_get_tasks(void);
index 6004651f110537ecf0da5eaf220365078e264cd9..9b64341a8ea8d883b1eac56a5c7db8cab4a23eae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -7,7 +7,6 @@
 /** \file sideband.c Implementation of the sideband API. */
 
 #include <regex.h>
-#include <sys/uio.h>
 
 #include "para.h"
 #include "error.h"
index 50daaa608f9b7cb507f07ea394cc522d4ed7a6f6..c9b698f978634858ae3ff8c95150975677be35ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
        DESIGNATOR(EXIT__SUCCESS), \
        /* Command failed. */ \
        DESIGNATOR(EXIT__FAILURE), \
+       /* The next chunk of the blob (addblob commands only) */ \
+       DESIGNATOR(BLOB_DATA), \
+       /* An afs callback failed. */ \
+       DESIGNATOR(AFS_CB_FAILURE), \
 
 /** Just prefix with \p SBD_. */
 #define DESIGNATOR(x) SBD_ ## x
index 3b202a2c8ba6a62b23fa46f7e99ed207c4f08750..5d6e6e45dcd8e728c1bfcc6e08899bf08d4fc512 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -7,10 +7,15 @@
 
 #include <signal.h>
 #include <sys/types.h>
+#include <regex.h>
 
 #include "para.h"
 #include "error.h"
 #include "fd.h"
+#include "list.h"
+#include "string.h"
+#include "sched.h"
+#include "signal.h"
 
 static int signal_pipe[2];
 
@@ -29,12 +34,14 @@ static int signal_pipe[2];
  * ready for reading, see select(2).
  *
  * \return This function either succeeds or calls exit(2) to terminate the
- * current process. On success, the file descriptor of the read end of the
- * signal pipe is returned.
+ * current process. On success, a signal task structure is returned.
  */
-int para_signal_init(void)
+struct signal_task *signal_init_or_die(void)
 {
+       struct signal_task *st;
        int ret;
+
+       PARA_NOTICE_LOG("setting up signal handling\n");
        if (pipe(signal_pipe) < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err_out;
@@ -45,7 +52,9 @@ int para_signal_init(void)
        ret = mark_fd_nonblocking(signal_pipe[1]);
        if (ret < 0)
                goto err_out;
-       return signal_pipe[0];
+       st = para_calloc(sizeof(*st));
+       st->fd = signal_pipe[0];
+       return st;
 err_out:
        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
@@ -220,9 +229,12 @@ int para_next_signal(fd_set *rfds)
 }
 
 /**
- * Close the write end of the signal pipe.
+ * Close the write end of the signal pipe, deallocate resources.
+ *
+ * \param st The pointer obtained earlier from signal_init_or_die().
  */
-void para_signal_shutdown(void)
+void signal_shutdown(struct signal_task *st)
 {
        close(signal_pipe[1]);
+       free(st);
 }
index 92af8170529fc1cbe3d5fa41d8bc79a2257f29d7..b5b06f356999062f3b9b233e8303eea33015804a 100644 (file)
--- a/signal.h
+++ b/signal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -13,14 +13,20 @@ struct signal_task {
        /** The signal pipe. */
        int fd;
        /** The associated task structure. */
-       struct task task;
+       struct task *task;
 };
 
-int para_signal_init(void);
+_static_inline_ void signal_pre_select(struct sched *s, void *context)
+{
+       struct signal_task *st = context;
+       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+}
+
+struct signal_task *signal_init_or_die(void);
 void para_sigaction(int sig, void (*handler)(int));
 void para_install_sighandler(int);
 int para_reap_child(pid_t *pid);
 int para_next_signal(fd_set *rfds);
-void para_signal_shutdown(void);
+void signal_shutdown(struct signal_task *st);
 void para_block_signal(int sig);
 void para_unblock_signal(int sig);
diff --git a/skencil/overview.sk b/skencil/overview.sk
deleted file mode 100644 (file)
index 5ab648c..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-##Sketch 1 2
-document()
-layout('A4',0)
-layer('Layer 1',1,1,0,0,(0,0,0))
-lw(1)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(184.297,654.341,0)
-bs(184.297,732.403,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('open file',(1.19433e-15,1,-1,1.19433e-15,181.259,668.683))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('mp3 data',(-1.83691e-16,-1,1,-1.83691e-16,193.585,717.172))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(190.547,726.853,0)
-bs(190.547,655.774,0)
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(145.824,181.165,0)
-bs(228.568,181.165,0)
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(228.568,161.165,0)
-bs(145.824,161.165,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('connect local socket',(1.19433e-15,1,-1,1.19433e-15,272.988,207.639))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(307.4,339.815,0)
-bs(307.4,200,0)
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(280,200,0)
-bs(280,339.815,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('server & audiod status',(-1.83691e-16,-1,1,-1.83691e-16,310.438,332.705))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(99.2303,144.289,0)
-bs(99.2303,73.1918,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('s&a status',(-1.83691e-16,-1,1,-1.83691e-16,102.268,137.713))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(61.1648,150.482,0)
-bs(61.1647,71.2323,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('cmd output',(-1.83691e-16,-1,1,-1.83691e-16,64.2028,142.553))
-lw(0.992126)
-ld((0.10000000000000001, 1.0))
-b()
-bs(24.7094,792.443,0)
-bs(310.724,792.443,0)
-bs(310.421,640.011,0)
-bs(561.98,639.817,0)
-bs(561.98,538.528,0)
-bs(24.7094,538.528,0)
-bs(24.7094,792.443,0)
-bC()
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Italic')
-txt('server host',(252.845,779.314))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Italic')
-txt('client host',(512.93,400.509))
-G()
-lw(1)
-r(119.09,0,0,-28.2587,39.0214,375.322)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('para_client',(45.2504,355.601))
-G_()
-G()
-lw(1)
-r(143.312,0,0,-28.2587,408.597,189.57)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('sound_device',(414.265,169.849))
-G_()
-G()
-lw(1)
-r(94.8684,0,0,-28.2587,34.9844,58.6731)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('terminal',(42.4226,38.9517))
-G_()
-G()
-lw(1)
-r(131.201,0,0,-28.2587,237.937,189.4)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('para_audioc',(244.894,169.678))
-G_()
-G()
-lw(1)
-r(94.8684,0,0,-28.2587,34.9844,189.4)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('para_gui',(40.4306,169.678))
-G_()
-fp((0.9,0.9,0.9))
-lw(1)
-r(177.037,0,0,-89.6831,384.699,788.574)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Bold')
-Fs(36)
-txt('paraslash',(399.201,755.055))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Bold')
-Fs(36)
-txt('0.4.0',(437.217,712.433))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('fork & exec',(153.764,182.009))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('s&a status',(158.223,162.009))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('fork & exec',(1.19433e-15,1,-1,1.19433e-15,70.3615,232.519))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(73.3994,201.263,0)
-bs(73.3995,330.639,0)
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(103.54,330.639,0)
-bs(103.539,201.263,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('cmd output',(-1.83691e-16,-1,1,-1.83691e-16,106.577,297.647))
-lw(0.992126)
-ld((0.10000000000000001, 1.0))
-b()
-bs(29.1803,415.392,0)
-bs(570.132,415.357,0)
-bs(570.132,26.3198,0)
-bs(27.1619,26.3198,0)
-bs(27.1619,413.359,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt(' server cmd',(2.57235e-15,1,-1,2.57235e-15,359.444,441.487))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('result',(-1.83691e-16,-1,1,-1.83691e-16,380.89,496.536))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('connect tcp',(1.19433e-15,1,-1,1.19433e-15,420.523,446.644))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(426.278,433.194,0)
-bs(426.278,524.243,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('server status',(-1.83691e-16,-1,1,-1.83691e-16,438.254,513.515))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(431.433,522.224,0)
-bs(431.433,434.203,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('mp3 data ',(-1.83691e-16,-1,1,-1.83691e-16,513.965,505.064))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(506.551,520.71,0)
-bs(506.551,433.193,0)
-G()
-lw(1)
-r(301.847,0,0,-28.2587,220.095,375.322)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('para_audiod',(311.703,355.601))
-G_()
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(144.486,419.683,0)
-bs(144.486,431.685,0)
-bs(368.033,431.685,0)
-bs(368.033,531.257,0)
-lw(1)
-la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(151.551,419.179,0)
-bs(151.551,424.722,0)
-bs(374.601,424.722,0)
-bs(374.601,529.224,0)
-lw(1)
-ld((4, 4))
-r(156.432,0,0,-101.428,50.1221,646.762)
-G()
-lw(1)
-r(41.883,0,0,-28.2587,160.634,643.23)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('afh',(166.229,623.509))
-G_()
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Italic')
-txt('afs process',(106.135,651.24))
-lw(1)
-r(0,0,0,-0.504639,125.815,629.1)
-lw(1)
-ld((5, 5))
-r(272.999,0,0,-67.6189,281.238,614.467)
-lw(1)
-r(78.2156,0,0,-28.2587,289.816,609.925)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('vss',(313.588,590.204))
-lw(1)
-r(73.674,0,0,-59.5452,470.974,609.925)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('sender',(477.579,574.056))
-lw(1)
-r(167.533,0,0,-28.2587,289.816,577.629)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('cmd',(0.886791,0,0,1,355.258,557.908))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Italic')
-txt('server process',(286.284,618.44))
-G()
-lw(1)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(216.351,599.242,0)
-bs(272.871,599.242,0)
-lw(1)
-la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(216.351,603.697,0)
-bs(272.87,603.697,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('open fd',(223.502,588.236))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('request fd',(217.951,607.917))
-G_()
-G()
-lw(1)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(216.688,555.341,0)
-bs(273.208,555.341,0)
-lw(1)
-la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(216.688,559.796,0)
-bs(273.207,559.796,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('result',(228.885,545.344))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('afs cmd',(222.83,563.006))
-G_()
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(79.9931,735.431,0)
-bs(79.9931,652.573,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('osl result',(-1.83691e-16,-1,1,-1.83691e-16,83.0311,719.475))
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('osl query',(3.06152e-16,1,-1,3.06152e-16,68.1098,669.658))
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(73.6709,653.078,0)
-bs(73.6709,735.936,0)
-G()
-lw(1)
-r(119.595,0,0,-46.425,75.8576,784.523)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-txt('file system',(108.732,771.908))
-G()
-G()
-lw(1)
-r(48.4434,0,0,-21.1941,81.9131,764.929)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-txt('osl table',(85.9688,750.758))
-G_()
-G()
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-txt('mp3 file',(143.495,750.295))
-lw(1)
-r(48.4434,0,0,-21.1941,139.44,764.929)
-G_()
-G_()
-G_()
-lw(1)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(379.134,590.245,0)
-bs(461.495,590.245,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-txt('mp3 data',(397.646,596.3))
-G()
-lw(1)
-r(120.099,0,0,-53.4896,68.2886,608.411)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('afs',(0.886791,0,0,1,115.93,576.074))
-G_()
-G()
-lw(1)
-r(75.1101,0,0,-28.2587,56.6797,643.23)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('libosl',(67.5587,620.179))
-G_()
-G()
-lw(0.992126)
-la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1))
-b()
-bs(466.036,335.949,0)
-bs(466.036,203.05,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,476.076,294.966))
-G_()
-guidelayer('Guide Lines',1,0,0,1,(0,0,1))
-guide(-307.905,0)
-grid((0,0,20,20),0,(0,0,1),'Grid')
diff --git a/spx.h b/spx.h
index c4999f07a6a32c2229afa8187c13871c64b3a7f8..1567ce8789ccca9598fee6036b23d37e2549fec9 100644 (file)
--- a/spx.h
+++ b/spx.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 8f7d715cf20cc730d985845818c8dd49f4bde200..108cfaa49c1acf2168858f1120357569379f9c71 100644 (file)
--- a/spx_afh.c
+++ b/spx_afh.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -163,6 +163,94 @@ static int spx_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        return ogg_get_file_info(map, numbytes, afhi, &spx_callback_info);
 }
 
+static size_t spx_make_meta_packet(struct taginfo *tags, char **result)
+{
+       size_t sz;
+       char *buf, *p;
+       size_t comment_len = strlen(tags->comment),
+               artist_len = strlen(tags->artist),
+               title_len = strlen(tags->title),
+               album_len = strlen(tags->album),
+               year_len = strlen(tags->year);
+       uint32_t comment_sz = comment_len,
+               artist_sz = artist_len + strlen("artist="),
+               title_sz = title_len + strlen("title="),
+               album_sz = album_len + strlen("album="),
+               year_sz = year_len + strlen("year=");
+       uint32_t num_tags;
+
+       sz = 4 /* comment length (always present) */
+               + comment_sz
+               + 4; /* number of tags */
+       num_tags = 0;
+       if (artist_len) {
+               num_tags++;
+               sz += 4 + artist_sz;
+       }
+       if (title_len) {
+               num_tags++;
+               sz += 4 + title_sz;
+       }
+       if (album_len) {
+               num_tags++;
+               sz += 4 + album_sz;
+       }
+       if (year_len) {
+               num_tags++;
+               sz += 4 + year_sz;
+       }
+       PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
+       /* terminating zero byte for the last sprintf() */
+       buf = p = para_malloc(sz + 1);
+       write_u32(p, comment_sz);
+       p += 4;
+       strcpy(p, tags->comment);
+       p += comment_sz;
+       write_u32(p, num_tags);
+       p += 4;
+       if (artist_len) {
+               write_u32(p, artist_sz);
+               p += 4;
+               sprintf(p, "artist=%s", tags->artist);
+               p += artist_sz;
+       }
+       if (title_len) {
+               write_u32(p, title_sz);
+               p += 4;
+               sprintf(p, "title=%s", tags->title);
+               p += title_sz;
+       }
+       if (album_len) {
+               write_u32(p, album_sz);
+               p += 4;
+               sprintf(p, "album=%s", tags->album);
+               p += album_sz;
+       }
+       if (year_len) {
+               write_u32(p, year_sz);
+               p += 4;
+               sprintf(p, "year=%s", tags->year);
+               p += year_sz;
+       }
+       assert(p == buf + sz);
+       *result = buf;
+       return sz;
+}
+
+static int spx_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int output_fd,
+               __a_unused const char *filename)
+{
+       char *meta_packet;
+       size_t meta_sz;
+       int ret;
+
+       meta_sz = spx_make_meta_packet(tags, &meta_packet);
+       ret = ogg_rewrite_tags(map, mapsize, output_fd, meta_packet, meta_sz);
+       free(meta_packet);
+       return ret;
+}
+
 /**
  * The init function of the ogg/speex audio format handler.
  *
@@ -172,4 +260,5 @@ void spx_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = spx_get_file_info,
        afh->suffixes = speex_suffixes;
+       afh->rewrite_tags = spx_rewrite_tags;
 }
index 3bd3d48d2b9fbc70fe8c8283a3ab47421ef1c463..c850cd198683b103215ee5a01615d27248937caa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2002-2006 Jean-Marc Valin
- * Copyright (C) 2010-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 9bba042c040c0687eb33f0fc156dbf62f06392c9..644d287aaf6a4daba32dd023c9770691aa3f4d24 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2002-2006 Jean-Marc Valin
- * Copyright (C) 2010-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -75,7 +75,7 @@ struct private_spxdec_data {
        int lookahead;
        /** The state information about the current stream. */
        ogg_stream_state os;
-       /** Whether \a os initialized. */
+       /** Whether \a os is initialized. */
        bool stream_init;
 };
 
@@ -128,7 +128,14 @@ static int speexdec_init(struct filter_node *fn)
 #define le_short(s) ((short) (s))
 #endif
 
+/**
+ * Size of the output buffer.
+ *
+ * Valid streams have frame sizes in the range from 160 to 640. To avoid buffer
+ * overflows, we bail out if the decoder reports a value bigger than this.
+ */
 #define MAX_FRAME_SIZE 2000
+
 /* Copy Ogg packet to Speex bitstream */
 static int speexdec_write_frames(int packet_no,
                struct private_spxdec_data *psd, int skip_samples,
@@ -139,7 +146,14 @@ static int speexdec_write_frames(int packet_no,
        for (j = 0; j != psd->shi.nframes; j++) {
                short output[MAX_FRAME_SIZE], *btr_output;
                int skip = skip_samples + psd->lookahead, skip_idx = 0;
-               int samples, new_frame_size = psd->shi.frame_size;
+               int samples, this_frame_size,
+                       new_frame_size = psd->shi.frame_size;
+
+               if (speex_decoder_ctl(psd->shi.state, SPEEX_GET_FRAME_SIZE,
+                               &this_frame_size) == 0) {
+                       if (this_frame_size > MAX_FRAME_SIZE)
+                               return -E_SPX_DECODE_OVERFLOW;
+               };
 
                if (speex_decode_int(psd->shi.state, &psd->bits, output) < 0)
                        return -E_SPX_DECODE;
@@ -238,9 +252,9 @@ static int compute_skip_samples(ogg_page *og, struct private_spxdec_data *psd)
        return ret;
 }
 
-static int speexdec_post_select(__a_unused struct sched *s, struct task *t)
+static int speexdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct private_spxdec_data *psd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret, ns;
diff --git a/stat.c b/stat.c
index 8036de3a9d682d9bfe7506a09ee52cabbb9780ea..ac18363df2899156d7ed8b452b8d6143fcb2c40d 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
diff --git a/stdin.c b/stdin.c
index 20b9250e9b7fbf3c252e20dff8891bfeeb5112dd..e5c40bcb092a1faa59a91a33bd2d7b1975f1f3e7 100644 (file)
--- a/stdin.c
+++ b/stdin.c
@@ -1,12 +1,11 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file stdin.c Functions that deal with reading from stdin. */
 
-#include <assert.h>
 #include <regex.h>
 
 #include "para.h"
 #include "buffer_tree.h"
 #include "string.h"
 
-/**
- * The pre_select function of the stdin task.
- *
- * \param s The scheduler this task was registered to.
- * \param t The task structure of the stdin task.
- *
- * This function is always successful. If there is space left in the
- * buffer of the stdin task, it adds \p STDIN_FILENO to the read fd set
- * of \a s.
+/*
+ * If there is space left in the buffer of the stdin task add STDIN_FILENO to
+ * the read fd set of s.
  */
-static void stdin_pre_select(struct sched *s, struct task *t)
+static void stdin_pre_select(struct sched *s, void *context)
 {
-       struct stdin_task *sit = container_of(t, struct stdin_task, task);
+       struct stdin_task *sit = context;
        int ret;
 
        ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
@@ -43,20 +36,14 @@ static void stdin_pre_select(struct sched *s, struct task *t)
        sched_request_timeout_ms(100, s);
 }
 
-/**
- * The post select function of the stdin task.
- *
- * \param s The scheduler this task was registered to.
- * \param t The task structure of the stdin task.
- *
- * This function checks if \p STDIN_FILENO was included by in the read fd set
- * of \a s during the previous pre_select call.  If yes, and \p STDIN_FILENO
- * appears to be readable, data is read from stdin and fed into the buffer
- * tree.
+/*
+ * This function checks if STDIN_FILENO was included by in the read fd set of s
+ * during the previous pre_select call. If so, and if STDIN_FILENO is readable,
+ * data is read from stdin and fed into the buffer tree.
  */
-static int stdin_post_select(struct sched *s, struct task *t)
+static int stdin_post_select(struct sched *s, void *context)
 {
-       struct stdin_task *sit = container_of(t, struct stdin_task, task);
+       struct stdin_task *sit = context;
        ssize_t ret;
        size_t sz, n;
        char *buf = NULL;
@@ -95,21 +82,25 @@ err:
 }
 
 /**
- * Initialize a stdin task structure with default values.
+ * Register a stdin task structure.
  *
- * \param sit The stdin task structure.
+ * \param sit The stdin task structure to register.
+ * \param s The task will be added to this scheduler's task list.
  *
- * This fills in the pre/post select function pointers of the task structure
- * given by \a sit and creates a buffer tree for I/O.
+ * This allocates a buffer tree pool for I/O, sets up \a sit and registers a
+ * task with \a sit as context pointer.
  */
-void stdin_set_defaults(struct stdin_task *sit)
+void stdin_task_register(struct stdin_task *sit, struct sched *s)
 {
        int ret;
+       struct task_info ti = {
+               .name = "stdin",
+               .pre_select = stdin_pre_select,
+               .post_select = stdin_post_select,
+               .context = sit,
+       };
 
-       sit->task.pre_select = stdin_pre_select;
-       sit->task.post_select = stdin_post_select;
        sit->btrp = btr_pool_new("stdin", 128 * 1024);
-       sprintf(sit->task.status, "stdin reader");
        /*
         * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file
         * description (the terminal), and thus share the same file status
@@ -124,4 +115,5 @@ void stdin_set_defaults(struct stdin_task *sit)
        }
        sit->fd_flags = ret;
        sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0;
+       sit->task = task_register(&ti, s);
 }
diff --git a/stdin.h b/stdin.h
index cdf0c67fd57a1078f38a9f175d598adf65ece520..ca33b08a1affae80e07eef5be59d78b4f35006a4 100644 (file)
--- a/stdin.h
+++ b/stdin.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -9,7 +9,7 @@
 /** The task structure used for reading from stdin. */
 struct stdin_task {
        /** The task structure. */
-       struct task task;
+       struct task *task;
        /** Stdin is always the root of a buffer tree. */
        struct btr_node *btrn;
        /** Use a buffer pool to minimize memcpy due to alignment problems. */
@@ -20,4 +20,4 @@ struct stdin_task {
        bool must_set_nonblock_flag;
 };
 
-void stdin_set_defaults(struct stdin_task *sit);
+void stdin_task_register(struct stdin_task *sit, struct sched *s);
index cf33bf6d0b67cf49625d27b4d602dec35922d32d..29db2b7e3e28f050d72577a6d8b533c18ed54be8 100644 (file)
--- a/stdout.c
+++ b/stdout.c
@@ -1,13 +1,11 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file stdout.c Functions that deal with writing to stdout. */
 
-#include <assert.h>
-
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "stdout.h"
 #include "buffer_tree.h"
 
-/**
- * The pre_select function of the stdout task.
- *
- * \param s The scheduler this task was registered to.
- * \param t The task structure of the stdout task.
- *
- * This function is always successful. If there is input data available, it
- * adds \p STDOUT_FILENO to the write fd set of \a s.
- */
-static void stdout_pre_select(struct sched *s, struct task *t)
+/* Add STDOUT_FILENO to the write fd set if there is input data available. */
+static void stdout_pre_select(struct sched *s, void *context)
 {
-       struct stdout_task *sot = container_of(t, struct stdout_task, task);
+       struct stdout_task *sot = context;
        int ret;
 
        ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
@@ -37,18 +27,13 @@ static void stdout_pre_select(struct sched *s, struct task *t)
                sched_min_delay(s);
 }
 
-/**
- * The post select function of the stdout task.
- *
- * \param s The scheduler this task was registered to.
- * \param t The task structure of the stdout task.
- *
- * This function writes input data from the buffer tree to stdout if \p
+/*
+ * This function writes input data from the buffer tree to stdout if
  * STDOUT_FILENO is writable.
  */
-static int stdout_post_select(struct sched *s, struct task *t)
+static int stdout_post_select(struct sched *s, void *context)
 {
-       struct stdout_task *sot = container_of(t, struct stdout_task, task);
+       struct stdout_task *sot = context;
        struct btr_node *btrn = sot->btrn;
        int ret;
        char *buf;
@@ -85,21 +70,24 @@ out:
        }
        return ret;
 }
+
 /**
- * Initialize a stdout task structure with default values.
+ * Register a stdout task structure.
  *
- * \param sot The stdout task structure.
+ * \param sot The stdout task structure to register.
+ * \param s The task will be added to this scheduler's task list.
  *
- * This fills in the pre/post select function pointers of the task structure
- * given by \a sot.
+ * This sets up \a sot and registers a task with \a sot as context pointer.
  */
-void stdout_set_defaults(struct stdout_task *sot)
+void stdout_task_register(struct stdout_task *sot, struct sched *s)
 {
        int ret;
-
-       sot->task.pre_select = stdout_pre_select;
-       sot->task.post_select = stdout_post_select;
-       sprintf(sot->task.status, "stdout");
+       struct task_info ti = {
+               .pre_select = stdout_pre_select,
+               .post_select = stdout_post_select,
+               .context = sot,
+               .name = "stdout",
+       };
 
        /* See stdin.c for details. */
        ret = fcntl(STDOUT_FILENO, F_GETFL);
@@ -109,4 +97,5 @@ void stdout_set_defaults(struct stdout_task *sot)
        }
        sot->fd_flags = ret;
        sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0;
+       sot->task = task_register(&ti, s);
 }
index a21b7cedfb699d9402f666a1a754a26d12cdcec1..0559fb5c1773c2ea9a55b34b1135725a2415d3d1 100644 (file)
--- a/stdout.h
+++ b/stdout.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -13,7 +13,7 @@
  */
 struct stdout_task {
        /** The task structure used by the scheduler. */
-       struct task task;
+       struct task *task;
        /** Stdout is always a leaf node in the buffer tree. */
        struct btr_node *btrn;
        /** The descriptor flags of STDOUT at startup. */
@@ -22,4 +22,4 @@ struct stdout_task {
        bool must_set_nonblock_flag;
 };
 
-void stdout_set_defaults(struct stdout_task *sot);
+void stdout_task_register(struct stdout_task *sot, struct sched *s);
index 38e25b09c5f3ff2b289d6e943a0f7bde3bfc23ad..701448e041cfa9ff86dc1a6cccf37cd088b76c20 100644 (file)
--- a/string.c
+++ b/string.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
  * i.e. there is no need to check the return value in the caller.
  *
- * \return A pointer to  the newly allocated memory, which is suitably aligned
- * for any kind of variable and may be different from \a p.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from \a p.
  *
  * \sa realloc(3).
  */
-__must_check __malloc void *para_realloc(void *p, size_t size)
+__must_check void *para_realloc(void *p, size_t size)
 {
        /*
         * No need to check for NULL pointers: If p is NULL, the call
@@ -298,24 +298,6 @@ __must_check char *para_basename(const char *name)
        return ret;
 }
 
-/**
- * Cut trailing newline.
- *
- * \param buf The string to be chopped.
- *
- * Replace the last character in \p buf by zero if it is equal to
- * the newline character.
- */
-void chop(char *buf)
-{
-       int n = strlen(buf);
-
-       if (!n)
-               return;
-       if (buf[n - 1] == '\n')
-               buf[n - 1] = '\0';
-}
-
 /**
  * Get the logname of the current user.
  *
@@ -398,14 +380,13 @@ int for_each_line(unsigned flags, char *buf, size_t size,
                char *next_cr;
 
                next_cr = memchr(start, '\n', buf + size - start);
-               next_null = memchr(start, '\0', buf + size - start);
+               next_null = memchr(start, '\0', next_cr?
+                       next_cr - start : buf + size - start);
                if (!next_cr && !next_null)
                        break;
-               if (next_cr && next_null) {
-                       end = next_cr < next_null? next_cr : next_null;
-               } else if (next_null) {
+               if (next_null)
                        end = next_null;
-               else
+               else
                        end = next_cr;
                num_lines++;
                if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
@@ -581,10 +562,18 @@ int para_atoi64(const char *str, int64_t *value)
        tmp = strtoll(str, &endptr, 10);
        if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN))
                return -E_ATOI_OVERFLOW;
-       if (errno != 0 && tmp == 0) /* other error */
-               return -E_STRTOLL;
+       /*
+        * If there were no digits at all, strtoll() stores the original value
+        * of str in *endptr.
+        */
        if (endptr == str)
                return -E_ATOI_NO_DIGITS;
+       /*
+        * The implementation may also set errno and return 0 in case no
+        * conversion was performed.
+        */
+       if (errno != 0 && tmp == 0)
+               return -E_ATOI_NO_DIGITS;
        if (*endptr != '\0') /* Further characters after number */
                return -E_ATOI_JUNK_AT_END;
        *value = tmp;
@@ -644,7 +633,7 @@ int get_loglevel_by_name(const char *txt)
                return LL_CRIT;
        if (loglevel_equal(txt, "emerg"))
                return LL_EMERG;
-       return -1;
+       return -E_BAD_LL;
 }
 
 static int get_next_word(const char *buf, const char *delim, char **word)
@@ -1084,7 +1073,7 @@ __must_check int strwidth(const char *s, size_t *result)
                return -ERRNO_TO_PARA_ERROR(errno);
        if (num_wchars == 0)
                return 0;
-       dest = para_malloc(num_wchars * sizeof(*dest));
+       dest = para_malloc((num_wchars + 1) * sizeof(*dest));
        src = s;
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
index 935c7d456c2c5b6910b356f2ddd52fd62848cf67..61bb7c25b04998ed1c6616240f2e51f6627a5360 100644 (file)
--- a/string.h
+++ b/string.h
@@ -1,10 +1,10 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file string.h exported sybmols from string.c */
+/** \file string.h exported symbols from string.c */
 
 /** Flags that change how content is printed into the buffer. */
 enum para_buffer_flags {
@@ -62,14 +62,12 @@ int for_each_line(unsigned flags, char *buf, size_t size,
   */
 #define WRITE_STATUS_ITEM(b, n, f, ...) (\
 { \
-       int _ret; \
        if ((b)->flags & PBF_SIZE_PREFIX) { \
-               _ret = para_printf((b), "%02x:" f, n, ## __VA_ARGS__); \
+               para_printf((b), "%02x:" f, n, ## __VA_ARGS__); \
        } else { \
-               _ret = para_printf((b), "%s: " f, status_item_list[(n)], \
+               para_printf((b), "%s: " f, status_item_list[(n)], \
                        ## __VA_ARGS__); \
        } \
-       _ret; \
 } \
 )
 
@@ -84,7 +82,6 @@ __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
 __must_check __malloc char *para_strcat(char *a, const char *b);
 __must_check __malloc char *para_dirname(const char *name);
 __must_check char *para_basename(const char *name);
-void chop(char *buf);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 __malloc char *para_hostname(void);
diff --git a/sync_filter.c b/sync_filter.c
new file mode 100644 (file)
index 0000000..82e86e9
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2013 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sync_filter.c Playback synchronization filter. */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include "para.h"
+#include "sync_filter.cmdline.h"
+#include "list.h"
+#include "net.h"
+#include "sched.h"
+#include "ggo.h"
+#include "buffer_tree.h"
+#include "filter.h"
+#include "string.h"
+#include "fd.h"
+#include "error.h"
+
+struct sync_buddy_info {
+       const char *url;
+       char *host;
+       int port;
+       struct addrinfo *ai;
+       bool disabled;
+};
+
+/* One active buddy */
+struct sync_buddy {
+       int fd;
+       struct sync_buddy_info *sbi;
+       bool ping_received;
+       struct list_head node;
+};
+
+/* Allocated in ->open() */
+struct sync_filter_context {
+       int listen_fd;
+       struct list_head buddies;
+       struct timeval timeout;
+       bool ping_sent;
+};
+
+/* Allocated and freed in ->parse_config() and ->free_config(). */
+struct sync_filter_config {
+       struct sync_filter_args_info *conf;
+       struct sync_buddy_info *buddy_info;
+};
+
+#define FOR_EACH_BUDDY(_buddy, _list) \
+       list_for_each_entry(_buddy, _list, node)
+#define FOR_EACH_BUDDY_SAFE(_buddy, _tmp_buddy, _list) \
+       list_for_each_entry_safe(_buddy, _tmp_buddy, _list, node)
+
+static void sync_close_buddy(struct sync_buddy *buddy)
+{
+       PARA_DEBUG_LOG("closing %s, fd %d\n", buddy->sbi->url, buddy->fd);
+       close(buddy->fd);
+       list_del(&buddy->node);
+       free(buddy);
+}
+
+static void sync_close_buddies(struct sync_filter_context *ctx)
+{
+       struct sync_buddy *buddy, *tmp;
+
+       FOR_EACH_BUDDY_SAFE(buddy, tmp, &ctx->buddies)
+               sync_close_buddy(buddy);
+}
+
+static void sync_close(struct filter_node *fn)
+{
+       struct sync_filter_context *ctx = fn->private_data;
+
+       sync_close_buddies(ctx);
+       if (ctx->listen_fd >= 0) {
+               close(ctx->listen_fd);
+               ctx->listen_fd = -1;
+       }
+       free(ctx);
+       fn->private_data = NULL;
+}
+
+static void sync_free_config(void *conf)
+{
+       struct sync_filter_config *sfc = conf;
+       int i;
+
+       for (i = 0; i < sfc->conf->buddy_given; i++) {
+               free(sfc->buddy_info[i].host);
+               freeaddrinfo(sfc->buddy_info[i].ai);
+       }
+       sync_filter_cmdline_parser_free(sfc->conf);
+       free(sfc);
+}
+
+static void sync_open(struct filter_node *fn)
+{
+       int i, ret;
+       struct sync_filter_config *sfc = fn->conf;
+       struct sync_buddy *buddy;
+       struct sync_filter_context *ctx;
+
+       assert(sfc);
+
+       ctx = fn->private_data = para_calloc(sizeof(*ctx));
+       INIT_LIST_HEAD(&ctx->buddies);
+       ctx->listen_fd = -1;
+
+       /* create socket to listen for incoming packets */
+       ret = makesock(
+               IPPROTO_UDP,
+               true /* passive */,
+               NULL /* no host required */,
+               sfc->conf->port_arg,
+               NULL /* no flowopts */
+       );
+       if (ret < 0) {
+               PARA_ERROR_LOG("could not create UDP listening socket %d\n",
+                       sfc->conf->port_arg);
+               return;
+       }
+       ctx->listen_fd = ret;
+       PARA_INFO_LOG("listening on fd %d\n", ctx->listen_fd);
+
+       for (i = 0; i < sfc->conf->buddy_given; i++) {
+               struct sync_buddy_info *sbi = sfc->buddy_info + i;
+               const char *url = sfc->conf->buddy_arg[i];
+               int fd;
+
+               /* make buddy udp socket from address info */
+               assert(sbi->ai);
+               ret = makesock_addrinfo(
+                       IPPROTO_UDP,
+                       false /* not passive */,
+                       sbi->ai,
+                       NULL /* no flowopts */
+               );
+               if (ret < 0) {
+                       PARA_WARNING_LOG("could not make socket for %s\n",
+                               url);
+                       goto fail;
+               }
+               fd = ret;
+               ret = mark_fd_nonblocking(fd);
+               if (ret < 0) {
+                       PARA_ERROR_LOG("unable to set nonblock mode for %s\n",
+                               url);
+                       close(fd);
+                       goto fail;
+               }
+               buddy = para_malloc(sizeof(*buddy));
+               buddy->fd = fd;
+               buddy->sbi = sbi;
+               buddy->ping_received = false;
+               para_list_add(&buddy->node, &ctx->buddies);
+
+               PARA_INFO_LOG("opened buddy %s on fd %d\n", url, fd);
+               continue;
+fail:
+               PARA_WARNING_LOG("%s\n", para_strerror(-ret));
+       }
+}
+
+/*
+ * At parse config time, we build an array of struct sync_buddy_info with one
+ * entry for each buddy given in the arguments. This array is not affected by
+ * sync_close(), so information stored there can be used for multiple instances
+ * (para_audiod). We store the resolved url and the ->disabled bit in this
+ * array.
+ */
+static int sync_parse_config(int argc, char **argv, void **result)
+{
+       int i, ret, n;
+       struct sync_filter_config *sfc;
+       struct sync_filter_args_info *conf = para_malloc(sizeof(*conf));
+
+       sync_filter_cmdline_parser(argc, argv, conf); /* exits on error */
+       sfc = para_calloc(sizeof(*sfc));
+       sfc->conf = conf;
+       n = conf->buddy_given;
+       sfc->buddy_info = para_malloc((n + 1) * sizeof(*sfc->buddy_info));
+       PARA_INFO_LOG("initializing buddy info array of length %d\n", n);
+       for (i = 0; i < n; i++) {
+               const char *url = conf->buddy_arg[i];
+               size_t len = strlen(url);
+               char *host = para_malloc(len + 1);
+               int port;
+               struct addrinfo *ai;
+               struct sync_buddy_info *sbi = sfc->buddy_info + i;
+
+               if (!parse_url(url, host, len, &port)) {
+                       free(host);
+                       PARA_ERROR_LOG("could not parse url %s\n", url);
+                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+                       goto fail;
+               }
+               if (port < 0)
+                       port = conf->port_arg;
+               ret = lookup_address(IPPROTO_UDP, false /* not passive */,
+                       host, port, &ai);
+               if (ret < 0) {
+                       PARA_ERROR_LOG("host lookup failure for %s\n", url);
+                       free(host);
+                       goto fail;
+               }
+               sbi->url = url;
+               sbi->host = host;
+               sbi->port = port;
+               sbi->ai = ai;
+               sbi->disabled = false;
+               PARA_DEBUG_LOG("buddy #%d: %s\n", i, url);
+       }
+       *result = sfc;
+       return 1;
+fail:
+       assert(ret < 0);
+       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       sync_free_config(sfc);
+       return ret;
+}
+
+/*
+ * True if we sent a packet to all buddies and received a packet from each
+ * enabled buddy.
+ */
+static bool sync_complete(struct sync_filter_context *ctx)
+{
+       struct sync_buddy *buddy;
+
+       if (!ctx->ping_sent)
+               return false;
+       FOR_EACH_BUDDY(buddy, &ctx->buddies)
+               if (!buddy->sbi->disabled && !buddy->ping_received)
+                       return false;
+       return true;
+}
+
+static void sync_disable_active_buddies(struct sync_filter_context *ctx)
+{
+       struct sync_buddy *buddy;
+
+       FOR_EACH_BUDDY(buddy, &ctx->buddies) {
+               if (buddy->sbi->disabled)
+                       continue;
+               if (buddy->ping_received == true)
+                       continue;
+               PARA_NOTICE_LOG("disabling %s\n", buddy->sbi->url);
+               buddy->sbi->disabled = true;
+       }
+}
+
+static void sync_set_timeout(struct sync_filter_context *ctx,
+               struct sync_filter_config *sfc)
+{
+       struct timeval to;
+
+       ms2tv(sfc->conf->timeout_arg, &to);
+       tv_add(now, &to, &ctx->timeout);
+}
+
+static void sync_pre_select(struct sched *s, void *context)
+{
+       int ret;
+       struct filter_node *fn = context;
+       struct sync_filter_context *ctx = fn->private_data;
+       struct sync_filter_config *sfc = fn->conf;
+
+       if (list_empty(&ctx->buddies))
+               return sched_min_delay(s);
+       if (ctx->listen_fd < 0)
+               return sched_min_delay(s);
+       ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
+       if (ret < 0)
+               return sched_min_delay(s);
+       para_fd_set(ctx->listen_fd, &s->rfds, &s->max_fileno);
+       if (ret == 0)
+               return;
+       if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
+               sync_set_timeout(ctx, sfc);
+               return sched_min_delay(s);
+       }
+       if (sync_complete(ctx)) /* push down what we have */
+               return sched_min_delay(s);
+       sched_request_barrier_or_min_delay(&ctx->timeout, s);
+}
+
+static struct sync_buddy *sync_find_buddy(struct sockaddr *addr,
+               struct list_head *list)
+{
+       struct sync_buddy *buddy;
+
+       FOR_EACH_BUDDY(buddy, list)
+               if (sockaddr_equal(buddy->sbi->ai->ai_addr, addr))
+                       return buddy;
+       return NULL;
+}
+
+static int sync_post_select(__a_unused struct sched *s, void *context)
+{
+       int ret;
+       struct filter_node *fn = context;
+       struct sync_filter_context *ctx = fn->private_data;
+       struct sync_filter_config *sfc = fn->conf;
+       struct sync_buddy *buddy, *tmp;
+
+       if (list_empty(&ctx->buddies))
+               goto success;
+       ret = -E_SYNC_LISTEN_FD;
+       if (ctx->listen_fd < 0)
+               goto fail;
+       ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
+       if (ret < 0)
+               goto fail;
+       if (ret == 0)
+               return 0;
+       if (ctx->timeout.tv_sec == 0)
+               sync_set_timeout(ctx, sfc);
+       else {
+               if (tv_diff(&ctx->timeout, now, NULL) < 0) {
+                       sync_disable_active_buddies(ctx);
+                       goto success;
+               }
+       }
+       if (!ctx->ping_sent) {
+               FOR_EACH_BUDDY_SAFE(buddy, tmp, &ctx->buddies) {
+                       char c = '\0';
+                       PARA_INFO_LOG("pinging %s (%s)\n",
+                               buddy->sbi->url, buddy->sbi->disabled?
+                               "disabled" : "enabled");
+                       ret = xwrite(buddy->fd, &c, 1);
+                       if (ret < 0) {
+                               PARA_WARNING_LOG("failed to write to %s: %s\n",
+                                       buddy->sbi->url, para_strerror(-ret));
+                               sync_close_buddy(buddy);
+                       }
+               }
+               ctx->ping_sent = true;
+       }
+       if (FD_ISSET(ctx->listen_fd, &s->rfds)) {
+               char c;
+               for (;;) {
+                       struct sockaddr src_addr;
+                       socklen_t len = sizeof(src_addr);
+                       ret = recvfrom(ctx->listen_fd, &c, 1, MSG_DONTWAIT,
+                               &src_addr, &len);
+                       if (ret < 0) {
+                               if (errno == EAGAIN || errno == EWOULDBLOCK)
+                                       break;
+                               ret = -ERRNO_TO_PARA_ERROR(errno);
+                               goto fail;
+                       }
+                       buddy = sync_find_buddy(&src_addr, &ctx->buddies);
+                       if (!buddy) {
+                               PARA_NOTICE_LOG("pinged by unknown\n");
+                               continue;
+                       }
+                       PARA_DEBUG_LOG("pinged by %s\n", buddy->sbi->url);
+                       if (buddy->sbi->disabled) {
+                               PARA_NOTICE_LOG("enabling %s\n",
+                                       buddy->sbi->url);
+                               buddy->sbi->disabled = false;
+                       }
+                       buddy->ping_received = true;
+               }
+       }
+       if (!sync_complete(ctx))
+               return 1;
+       /*
+        * Although all enabled buddies are in sync we do not splice out
+        * ourselves immediately. We rather wait until the timeout expires,
+        * or the buddy list has become empty. This opens a time window
+        * for disabled buddies to become enabled by sending us a packet.
+        */
+       btr_pushdown(fn->btrn);
+       return 1;
+success:
+       ret = -E_SYNC_COMPLETE; /* success */
+       goto out;
+fail:
+       PARA_WARNING_LOG("%s\n", para_strerror(-ret));
+out:
+       sync_close_buddies(ctx);
+       btr_splice_out_node(&fn->btrn);
+       assert(ret < 0);
+       return ret;
+}
+
+/**
+ * The synchronization filter.
+ *
+ * \param f Pointer to the struct to initialize.
+ */
+void sync_filter_init(struct filter *f)
+{
+       struct sync_filter_args_info dummy;
+
+       sync_filter_cmdline_parser_init(&dummy);
+       f->open = sync_open;
+       f->close = sync_close;
+       f->pre_select = sync_pre_select;
+       f->post_select = sync_post_select;
+       f->parse_config = sync_parse_config;
+       f->free_config = sync_free_config;
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(sync_filter);
+}
index 69c69bef36dcabe995a7e281ec6d790e98f5de37..15bb6859b1c2e668245bf0663bc57d7593710f83 100644 (file)
@@ -1,4 +1,4 @@
-RM ?= rm -f
+RM = rm -f
 
 results_dir := $(test_dir)/test-results
 trash_dir := $(test_dir)/trashes
@@ -6,8 +6,9 @@ trash_dir := $(test_dir)/trashes
 test_options := --executables-dir $(shell pwd)
 test_options += --results-dir $(results_dir)
 test_options += --trash-dir $(trash_dir)
-test_options += --executables "$(executables)"
+test_options += --executables "$(prefixed_executables)"
 test_options += --objects "$(basename $(all_objs))"
+test_options += --man-dir $(man_dir)
 
 ifdef V
        ifeq ("$(origin V)", "command line")
index 3c878eb77bbd61eed1fa5fb82e4e50e7596d67ca..76128512bca28b7fba427cdb4b36b446629472cd 100755 (executable)
@@ -21,7 +21,8 @@ pubkey=$privkey.pub
 serverlog=server.log
 
 get_audio_file_paths ogg
-oggs="$result"
+declare -a oggs=($result)
+declare -a oggs_base=(${oggs[@]##*/})
 
 declare -a commands=() cmdline=() required_objects=() good=() bad=()
 i=0
@@ -38,14 +39,14 @@ bad[$i]='!^successfully'
 let i++
 commands[$i]="add_ogg"
 required_objects[$i]='ogg_afh'
-cmdline[$i]="add $oggs"
+cmdline[$i]="add ${oggs[@]}"
 bad[$i]='.'
 
 let i++
 commands[$i]="ls_ogg"
 required_objects[$i]='ogg_afh'
-cmdline[$i]="ls -lv -p $oggs"
-good[$i]='^path:'
+cmdline[$i]="ls -l=v ${oggs_base[@]}"
+good[$i]='^basename:'
 
 let i++
 commands[$i]="term"
@@ -74,15 +75,15 @@ EOF
 
        $PARA_SERVER \
                --logfile "$serverlog" \
-               --config_file /dev/null \
+               --config-file /dev/null \
                --daemon \
                --loglevel $loglevel \
                --port $port \
-               --afs_database_dir "$db" \
-               --afs_socket "$sock" \
-               --user_list "$user_list" \
-               --http_port "$stream_port" \
-               --dccp_port "$stream_port"
+               --afs-database-dir "$db" \
+               --afs-socket "$sock" \
+               --user-list "$user_list" \
+               --http-port "$stream_port" \
+               --dccp-port "$stream_port"
 fi
 
 for ((i=0; i < ${#commands[@]}; i++)); do
@@ -114,9 +115,9 @@ for ((i=0; i < ${#commands[@]}; i++)); do
        test_expect_success "$command" "
        $PARA_CLIENT \
                --loglevel $loglevel \
-               --server_port $port \
-               --key_file $privkey \
-               --config_file /dev/null \
+               --server-port $port \
+               --key-file $privkey \
+               --config-file /dev/null \
                -- \
                ${cmdline[$i]} > $command.out &&
                { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out &&
diff --git a/t/t0005-man.sh b/t/t0005-man.sh
new file mode 100755 (executable)
index 0000000..fa32d41
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+
+test_description='Parse generated man pages.
+
+Simple sanity checks of some man pages.
+
+* para_audiod: Check that list of audiod commands is present
+* para_server: Check that list of server/afs commands is present
+* para_play: Check that list of play commands is present
+
+* para_{recv,filter,write,audiod}: Check for presence of
+filter/receiver/writer options as appropriate '
+
+. ${0%/*}/test-lib.sh
+
+rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer
+
+grep_man()
+{
+       local regex="$1" exe="$2"
+       tr '\n' ' ' < "$o_man_dir/para_$exe.1" | grep -q "$regex"
+}
+
+# check that options of all reveivers/filters/writers are contained
+# in the man pages
+
+regex="$rfw_regex"
+test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv"
+test_expect_success 'para_filter: filter options' "grep_man '$regex' filter"
+test_expect_success 'para_write: writer options' "grep_man '$regex' write"
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+       test_skip 'para_audiod' "missing object(s): $result"
+else
+       test_expect_success 'para_audiod: receivers' \
+               "grep_man 'Options for the http receiver' audiod"
+       test_expect_success 'para_audiod: filters' \
+               "grep_man 'Options for the compress filter' audiod"
+       test_expect_success 'para_audiod: writers' \
+               "grep_man 'Options for the file writer' audiod"
+fi
+
+# check various command lists
+
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+       test_skip 'para_audiod' "missing object(s): $result"
+else
+       regex='LIST OF AUDIOD COMMANDS.\{200,\}'
+       test_expect_success 'para_audiod: command list' \
+               "grep_man '$regex' audiod"
+fi
+
+test_require_objects "server"
+missing_objects="$result"
+if [[ -n "$missing_objects" ]]; then
+       test_skip "para_server" "missing object(s): $missing_objects"
+else
+       regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS'
+       test_expect_success 'para_server: server/afs commands' \
+               "grep_man '$regex' server"
+fi
+
+# para_play is always built
+regex='LIST OF COMMANDS.\{100,\}'
+test_expect_success 'para_play: play commands' "grep_man '$regex' play"
+test_done
index f1bb8cf65b66ff3890659f2b6009bae08d3cbd77..0e702b53c9b4c4150418dc0e2a07e55949817b04 100644 (file)
@@ -21,22 +21,22 @@ say_color()
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                export TERM=$ORIGINAL_TERM
                case "$1" in
-                       error) tput bold; tput setaf 1;;
-                       skip)  tput setaf 5;;
+                       error) tput $C_BOLD; tput $C_SETAF 1;;
+                       skip)  tput $C_SETAF 5;;
                        ok)
                                (($o_verbose == 0)) && return
-                               tput setaf 2;;
-                       pass)  tput bold; tput setaf 2;;
-                       info)  tput setaf 3;;
+                               tput $C_SETAF 2;;
+                       pass)  tput $C_BOLD; tput $C_SETAF 2;;
+                       info)  tput $C_SETAF 3;;
                        run)
                                (($o_verbose == 0)) && return
-                               tput setaf 6;;
+                               tput $C_SETAF 6;;
                esac
        fi
        shift
        printf "%s\n" "$*"
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
-               tput sgr0
+               tput $C_SGR0
                export TERM=dumb
        fi
 }
@@ -218,9 +218,21 @@ can_use_colors()
        result="false"
        [[ "$TERM" == "dumb" ]] && return
        [[ -t 1 ]] || return
-       tput bold >/dev/null 2>&1 || return
-       tput setaf 1 >/dev/null 2>&1 || return
-       tput sgr0 >/dev/null 2>&1 || return
+       C_BOLD='bold'
+       tput $C_BOLD &>/dev/null || {
+               C_BOLD='md'
+               tput $C_BOLD &>/dev/null
+       } || return
+       C_SETAF='setaf'
+       tput $C_SETAF 1 &>/dev/null || {
+               C_SETAF='AF'
+               tput $C_SETAF 1 &>/dev/null
+       } || return
+       C_SGR0='sgr0'
+       tput $C_SGR0 >/dev/null 2>&1 || {
+               C_SGR0='me'
+               tput $C_SGR0 &>/dev/null
+       } || return
        result="true"
 }
 
@@ -235,6 +247,7 @@ parse_options()
                -v=1|--verbose=1) o_verbose="1"; shift;;
                -v|--verbose|-v=2|--verbose=2) o_verbose="2"; shift;;
                --no-color) o_nocolor="true"; shift;;
+               --man-dir) export o_man_dir="$2"; shift; shift;;
                --results-dir) o_results_dir="$2"; shift; shift;;
                --trash-dir) o_trash_dir="$2"; shift; shift;;
                --executables-dir) export o_executables_dir="$2"; shift; shift;;
@@ -265,11 +278,13 @@ fixup_dirs()
        [[ -z "$o_results_dir" ]] && o_results_dir="$test_dir/test-results"
        [[ -z "$o_executables_dir" ]] && o_executables_dir="$test_dir/.."
        [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
+       [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
 
        # we want alsolute paths because relative paths become invalid
        # after changing to the trash dir
        [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
        [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
+       [[ -n "${o_man_dir##/*}" ]] && o_man_dir="$wd/$o_man_dir"
        [[ -n "${o_trash_dir##/*}" ]] && o_trash_dir="$wd/$o_trash_dir"
 
        mkdir -p "$o_results_dir"
diff --git a/time.c b/time.c
index fe431208d27130f452e0d61e7d801477730fbbfa..6bb9a50f5b32f7441943817dc22f868d0f0131cd 100644 (file)
--- a/time.c
+++ b/time.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -31,18 +31,6 @@ void ms2tv(long unsigned n, struct timeval *tv)
        tv->tv_usec = (n % 1000) * 1000;
 }
 
-/**
- * Convert a double to a struct timeval.
- *
- * \param x The value to convert.
- * \param tv Result pointer.
- */
-void d2tv(double x, struct timeval *tv)
-{
-       tv->tv_sec = x;
-       tv->tv_usec = (x - (double)tv->tv_sec) * 1000.0 * 1000.0 + 0.5;
-}
-
 /**
  * Compute the difference of two time values.
  *
index 436b298cb8f467a25bf10c270a11ee08c74d6937..b803b4976b52d0e89fdae2c3ee7edbdb8a0a311b 100644 (file)
@@ -1,13 +1,18 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 /** \file udp_recv.c Paraslash's udp receiver */
 
+#include <netinet/in.h>
 #include <regex.h>
 #include <sys/socket.h>
 #include <net/if.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
 #include "net.h"
 #include "fd.h"
 
-static void udp_recv_pre_select(struct sched *s, struct task *t)
+static void udp_recv_pre_select(struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
 
-       if (generic_recv_pre_select(s, t) <= 0)
+       if (generic_recv_pre_select(s, rn) <= 0)
                return;
        para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
 }
@@ -43,21 +48,21 @@ static int udp_check_eof(size_t sz, struct iovec iov[2])
        }
        if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, iov[0].iov_len) != 0)
                return 0;
-       if (memcmp(iov[1].iov_base, FEC_EOF_PACKET + iov[0].iov_len,
+       if (memcmp(iov[1].iov_base, &FEC_EOF_PACKET[iov[0].iov_len],
                        FEC_EOF_PACKET_LEN - iov[0].iov_len) != 0)
                return 0;
        return -E_RECV_EOF;
 }
 
-static int udp_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int udp_recv_post_select(__a_unused struct sched *s, void *context)
 {
-       struct receiver_node *rn = container_of(t, struct receiver_node, task);
+       struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
        size_t num_bytes;
        struct iovec iov[2];
        int ret, readv_ret, iovcnt;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(rn->task);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
index 7930f09234486d3448b196ba45ae3b178845d001..bbc38f0117d796ac5211bf86821244daf17baf84 100644 (file)
@@ -1,25 +1,26 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file udp_send.c Para_server's udp sender. */
 
-
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
-#include <sys/socket.h>
 #include <netinet/udp.h>
 #include <net/if.h>
-#include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "server.cmdline.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
 #include "afh.h"
-#include "afs.h"
 #include "server.h"
 #include "list.h"
 #include "send.h"
@@ -56,7 +57,7 @@ static void udp_close_target(struct sender_client *sc)
        size_t len = vss_get_fec_eof_packet(&buf);
 
        /*
-        * Ignore the return value of wirte() since we are closing the target
+        * Ignore the return value of write() since we are closing the target
         * anyway. The sole purpose of the "do_nothing" statement is to silence
         * gcc.
         */
@@ -155,11 +156,6 @@ err:
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
-static void udp_init_session(struct sender_client *sc)
-{
-       PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
-}
-
 static void udp_shutdown_targets(void)
 {
        struct sender_client *sc, *tmp;
@@ -191,14 +187,14 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
 
 static int udp_com_on(__a_unused struct sender_command_data *scd)
 {
-       sender_status = SENDER_ON;
+       sender_status = SENDER_on;
        return 1;
 }
 
 static int udp_com_off(__a_unused struct sender_command_data *scd)
 {
        udp_shutdown_targets();
-       sender_status = SENDER_OFF;
+       sender_status = SENDER_off;
        return 1;
 }
 
@@ -238,7 +234,7 @@ static int udp_init_fec(struct sender_client *sc)
 {
        int mps;
 
-       udp_init_session(sc);
+       PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
        mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
        return mps;
@@ -281,7 +277,7 @@ static void udp_send_fec(struct sender_client *sc, char *buf, size_t len)
 {
        int ret;
 
-       if (sender_status == SENDER_OFF)
+       if (sender_status == SENDER_off)
                return;
        if (len == 0)
                return;
@@ -351,7 +347,7 @@ err:
        return ret;
 }
 
-static char *udp_info(void)
+static char *udp_status(void)
 {
        struct sender_client *sc;
        char *ret, *tgts = NULL;
@@ -367,11 +363,10 @@ static char *udp_info(void)
                tgts = tmp;
        }
        ret = make_message(
-               "udp sender:\n"
-               "\tstatus: %s\n"
-               "\tport: %s\n"
-               "\ttargets: %s\n",
-               (sender_status == SENDER_ON)? "on" : "off",
+               "status: %s\n"
+               "port: %s\n"
+               "targets: %s\n",
+               (sender_status == SENDER_on)? "on" : "off",
                stringify_port(conf.udp_default_port_arg, "udp"),
                tgts? tgts : "(none)"
        );
@@ -423,22 +418,22 @@ static char *udp_help(void)
 void udp_send_init(struct sender *s)
 {
        INIT_LIST_HEAD(&targets);
-       s->info = udp_info;
+       s->status = udp_status;
        s->help = udp_help;
        s->send = NULL;
        s->pre_select = NULL;
        s->post_select = NULL;
        s->shutdown_clients = udp_shutdown_targets;
        s->resolve_target = udp_resolve_target;
-       s->client_cmds[SENDER_ON] = udp_com_on;
-       s->client_cmds[SENDER_OFF] = udp_com_off;
-       s->client_cmds[SENDER_DENY] = NULL;
-       s->client_cmds[SENDER_ALLOW] = NULL;
-       s->client_cmds[SENDER_ADD] = udp_com_add;
-       s->client_cmds[SENDER_DELETE] = udp_com_delete;
-       sender_status = SENDER_OFF;
+       s->client_cmds[SENDER_on] = udp_com_on;
+       s->client_cmds[SENDER_off] = udp_com_off;
+       s->client_cmds[SENDER_deny] = NULL;
+       s->client_cmds[SENDER_allow] = NULL;
+       s->client_cmds[SENDER_add] = udp_com_add;
+       s->client_cmds[SENDER_delete] = udp_com_delete;
+       sender_status = SENDER_off;
        udp_init_target_list();
        if (!conf.udp_no_autostart_given)
-               sender_status = SENDER_ON;
+               sender_status = SENDER_on;
        PARA_DEBUG_LOG("udp sender init complete\n");
 }
index 31ffe67b6e96f44aaabb2c32ac31ef4cfee2d468..9c751244339c34197d52229b75b4d55e5a58a43f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 5a1d69208c2720ead0c37dcadd29acc08a918ccc..f4e7661b5eb7900d43afa4ee4cd26c7771267a11 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index f8f7e6a33f69a4624f347e8b2c95f88e61111bb7..695675613398c1ae52867bbd3a04c27bc81145db 100644 (file)
--- a/version.c
+++ b/version.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2013 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -7,29 +7,16 @@
 /** \file version.c Some helpers for printing version and copyright strings. */
 
 #include "para.h"
-
-/** \file version.h Macros for printing the version string. */
-
+#include "version.h"
 #include "git-version.h"
 
-/**
- * Get the raw git version string
- *
- * \return The string generated by the GIT-VERSION-GEN script. It is passed
- * as a preprocessor define during compilation.
- */
-__a_const const char *version_git(void)
-{
-       return GIT_VERSION;
-}
-
 /**
  * Get the version string for an executable.
  *
  * \param pfx The program name (without the leading "para_").
  *
- * \return A statically allocated string which contains the program name, the
- * git version and the codename. It must not be freed by the caller.
+ * \return A statically allocated string which contains the program name and
+ * the git version. It must not be freed by the caller.
  */
 const char *version_single_line(const char *pfx)
 {
@@ -56,10 +43,10 @@ const char *version_text(const char *pfx)
        static char buf[512];
 
        snprintf(buf, sizeof(buf) - 1, "%s\n"
-               "Copyright (C) 2013 Andre Noll\n"
+               "Copyright (C) 2002-2015 Andre Noll\n"
                "This is free software with ABSOLUTELY NO WARRANTY."
                " See COPYING for details.\n"
-               "Report bugs to <maan@systemlinux.org>.\n"
+               "Report bugs to <maan@tuebingen.mpg.de>.\n"
                "build date: " BUILD_DATE ",\n"
                "build system: " UNAME_RS ",\n"
                "compiler: " CC_VERSION ".\n",
index 3dd5ba2177751e2c9716d4f0cdb99cfc5464c73a..f5282854d72f01a1f19a07185f049a05523e0594 100644 (file)
--- a/version.h
+++ b/version.h
@@ -1,6 +1,5 @@
 /** \file version.h Functions for printing the version string. */
 
-const char *version_git(void);
 const char *version_single_line(const char *pfx);
 const char *version_text(const char *pfx);
 void version_handle_flag(const char *pfx, bool flag);
diff --git a/vss.c b/vss.c
index 4c9f362344ce5bb8c6ae1e6b826fd78aaa140684..51db7686c6b008c2ac8704967d9cdfd7ea76e071 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
  * senders.
  */
 
+#include <sys/socket.h>
+#include <netinet/in.h>
 #include <regex.h>
 #include <osl.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
 
 #include "para.h"
 #include "error.h"
@@ -83,7 +89,7 @@ struct vss_task {
        /** The memory mapped audio file. */
        char *map;
        /** Used by the scheduler. */
-       struct task task;
+       struct task *task;
        /** Pointer to the header of the mapped audio file. */
        char *header_buf;
        /** Length of the audio file header. */
@@ -849,7 +855,6 @@ static void vss_eof(struct vss_task *vsst)
        mmd->afd.afhi.chunk_tv.tv_usec = 0;
        free(mmd->afd.afhi.chunk_table);
        mmd->afd.afhi.chunk_table = NULL;
-       mmd->mtime = 0;
        mmd->size = 0;
        mmd->events++;
 }
@@ -879,49 +884,11 @@ static void set_mmd_offset(void)
        mmd->offset = tv2ms(&offset);
 }
 
-/**
- * Compute the timeout for the main select-loop of the scheduler.
- *
- * \param s Pointer to the server scheduler.
- * \param t Pointer to the vss task structure.
- *
- * Before the timeout is computed, the current vss status flags are evaluated
- * and acted upon by calling appropriate functions from the lower layers.
- * Possible actions include
- *
- *     - request a new audio file from afs,
- *     - shutdown of all senders (stop/pause command),
- *     - reposition the stream (ff/jmp command).
- */
-static void vss_pre_select(struct sched *s, struct task *t)
+static void vss_pre_select(struct sched *s, void *context)
 {
        int i;
-       struct vss_task *vsst = container_of(t, struct vss_task, task);
+       struct vss_task *vsst = context;
 
-       if (!vsst->map || vss_next() || vss_paused() || vss_repos()) {
-               struct fec_client *fc, *tmp;
-               for (i = 0; senders[i].name; i++)
-                       if (senders[i].shutdown_clients)
-                               senders[i].shutdown_clients();
-               list_for_each_entry_safe(fc, tmp, &fec_client_list, node)
-                       fc->state = FEC_STATE_NONE;
-               mmd->stream_start.tv_sec = 0;
-               mmd->stream_start.tv_usec = 0;
-       }
-       if (vss_next())
-               vss_eof(vsst);
-       else if (vss_paused()) {
-               if (mmd->chunks_sent)
-                       set_eof_barrier(vsst);
-               mmd->chunks_sent = 0;
-       } else if (vss_repos()) {
-               tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
-               set_eof_barrier(vsst);
-               mmd->chunks_sent = 0;
-               mmd->current_chunk = mmd->repos_request;
-               mmd->new_vss_status_flags &= ~VSS_REPOS;
-               set_mmd_offset();
-       }
        if (need_to_request_new_audio_file(vsst)) {
                PARA_DEBUG_LOG("ready and playing, but no audio file\n");
                para_fd_set(vsst->afs_socket, &s->wfds, &s->max_fileno);
@@ -1007,7 +974,6 @@ static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
                goto err;
        }
        mmd->size = statbuf.st_size;
-       mmd->mtime = statbuf.st_mtime;
        ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
                passed_fd, 0, &vsst->map);
        if (ret < 0)
@@ -1120,11 +1086,38 @@ static void vss_send(struct vss_task *vsst)
        }
 }
 
-static int vss_post_select(struct sched *s, struct task *t)
+static int vss_post_select(struct sched *s, void *context)
 {
        int ret, i;
-       struct vss_task *vsst = container_of(t, struct vss_task, task);
+       struct vss_task *vsst = context;
 
+       if (!vsst->map || vss_next() || vss_paused() || vss_repos()) {
+               /* shut down senders and fec clients */
+               struct fec_client *fc, *tmp;
+               for (i = 0; senders[i].name; i++)
+                       if (senders[i].shutdown_clients)
+                               senders[i].shutdown_clients();
+               list_for_each_entry_safe(fc, tmp, &fec_client_list, node)
+                       fc->state = FEC_STATE_NONE;
+               mmd->stream_start.tv_sec = 0;
+               mmd->stream_start.tv_usec = 0;
+       }
+       if (vss_next())
+               vss_eof(vsst);
+       else if (vss_paused()) {
+               if (mmd->chunks_sent)
+                       set_eof_barrier(vsst);
+               mmd->chunks_sent = 0;
+       } else if (vss_repos()) { /* repositioning due to ff/jmp command */
+               tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
+               set_eof_barrier(vsst);
+               mmd->chunks_sent = 0;
+               mmd->current_chunk = afh_get_start_chunk(mmd->repos_request,
+                       &mmd->afd.afhi);
+               mmd->new_vss_status_flags &= ~VSS_REPOS;
+               set_mmd_offset();
+       }
+       /* If a sender command is pending, run it. */
        if (mmd->sender_cmd_data.cmd_num >= 0) {
                int num = mmd->sender_cmd_data.cmd_num,
                        sender_num = mmd->sender_cmd_data.sender_num;
@@ -1179,8 +1172,6 @@ void init_vss_task(int afs_socket, struct sched *s)
                        conf.autoplay_delay_arg : 0;
        vsst->header_interval.tv_sec = 5; /* should this be configurable? */
        vsst->afs_socket = afs_socket;
-       vsst->task.pre_select = vss_pre_select;
-       vsst->task.post_select = vss_post_select;
        ms2tv(announce_time, &vsst->announce_tv);
        PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
        INIT_LIST_HEAD(&fec_client_list);
@@ -1200,6 +1191,10 @@ void init_vss_task(int afs_socket, struct sched *s)
                tv_add(&vsst->autoplay_barrier, &vsst->announce_tv,
                        &vsst->data_send_barrier);
        }
-       sprintf(vsst->task.status, "vss task");
-       register_task(s, &vsst->task);
+       vsst->task = task_register(&(struct task_info) {
+               .name = "vss task",
+               .pre_select = vss_pre_select,
+               .post_select = vss_post_select,
+               .context = vsst,
+       }, s);
 }
diff --git a/vss.h b/vss.h
index bedbb8017a1c4cddf12282d53a48b71b6bb60648..d2b1ad1dd2d008420462f7f7c4b53f9ff34ab243 100644 (file)
--- a/vss.h
+++ b/vss.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
index 83b81fb2d5b4b9303feb1600cb808fe52accfa51..07423a14b6f41973e1fb391b0733837b5751d1ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -63,20 +63,19 @@ static void wav_open(struct filter_node *fn)
        *bof = 1;
 }
 
-static void wav_pre_select(struct sched *s, struct task *t)
+static void wav_pre_select(struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        size_t iqs = btr_get_input_queue_size(fn->btrn);
 
-       t->error = 0;
        if (iqs == 0)
                return;
        sched_min_delay(s);
 }
 
-static int wav_post_select(__a_unused struct sched *s, struct task *t)
+static int wav_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        int ret;
@@ -113,7 +112,7 @@ static int wav_post_select(__a_unused struct sched *s, struct task *t)
        ret = -E_WAV_SUCCESS;
 err:
        if (ret == -E_WAV_SUCCESS)
-               btr_splice_out_node(btrn);
+               btr_splice_out_node(&fn->btrn);
        else {
                btr_remove_node(&fn->btrn);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
diff --git a/web/.htaccess b/web/.htaccess
new file mode 100644 (file)
index 0000000..425c219
--- /dev/null
@@ -0,0 +1,12 @@
+<ifmodule mod_autoindex.c>
+       IndexOptions FancyIndexing IconWidth=30 IconsAreLinks
+       IndexOrderDefault Descending Date
+       IndexIgnore ..
+
+       AddIcon ../signature.png *.asc
+       AddDescription "Digital signature" *.asc
+
+       AddIcon ../tar-icon.png *.tgz *.tar.bz2
+       AddDescription "current master snapshot" -git.tar.bz2 .g*.tar.bz2 .g*.dirty.tar.bz2
+       AddDescription "release tarball" *.tgz *.tar.bz2
+</ifmodule>
diff --git a/web/about.in.html b/web/about.in.html
new file mode 100644 (file)
index 0000000..2c72f28
--- /dev/null
@@ -0,0 +1,23 @@
+<h1>About</h1>
+<hr>
+
+Paraslash is a collection of network audio streaming tools for Unix
+systems. It is written in C and released under the GPLv2.
+
+<ul>
+       <li> Runs on Linux, Mac OS, FreeBSD, NetBSD </li>
+       <li> Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma, flac and ogg/opus support </li>
+       <li> http, dccp and udp network streaming </li>
+       <li> Stand-alone decoder, player, tagger </li>
+       <li> Curses-based gui (<a href="gui.png">screenshot</a>) </li>
+       <li> Integrated volume normalizer, fader, alarm clock </li>
+       <li> Sophisticated audio file selector </li>
+       <li> Command line interface with tab-completion </li>
+       <li> Open source and well documented </li>
+</ul>
+
+<b> Author: </b> Andr&eacute; Noll,
+<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>
+<br>
+Comments and bug reports are welcome. Please provide the version of
+paraslash you are using and relevant parts of the logs.
diff --git a/web/contact.in.html b/web/contact.in.html
deleted file mode 100644 (file)
index 3e980db..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<h1>Contact</h1>
-<hr>
-
-<p> Andr&eacute; Noll, <a
-href="mailto:maan@systemlinux.org">maan@systemlinux.org</a> </p>
-
-Comments and bug reports are welcome (english, german, or spanish
-language). Please provide enough info such as the version of paraslash
-you are using and relevant parts of the logs. Including the string
-[paraslash] in the subject line is also a good idea.
diff --git a/web/dia/overview.dia b/web/dia/overview.dia
new file mode 100644 (file)
index 0000000..f9e0158
--- /dev/null
@@ -0,0 +1,3817 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#A4#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Text" version="1" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.13505,-23.1314"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.13505,-23.7264;3.15255,-22.9789"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#Overview#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.13505,-23.1314"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.1367,0.8291"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.1367,0.2341;3.9392,0.9816"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_server#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.1367,0.8291"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.225,9.8561"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.213438,9.47704;14.6275,15.9009"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#Incoming connections arrive via TCP at the dispatcher which creates a
+command handler process for each connection.
+
+After the connecting client has been authenticated, the command
+handler propagates the incoming request either to the audio file
+selector (afs) or to the virtual streaming system (vss). Results are sent
+back to the client.
+
+afs maintans the audio file database and  is responsible for selecting
+and loading audio files while vss controls the paraslash senders. When
+vss needs to stream an audio file it requests an open file descriptor from
+afs and feeds small chunks of data (e.g. mp3 frames) to the senders
+which send the chunks to all connected clients.#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.225,9.8561"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3.1702,14.0975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.1702,13.5025;3.1702,14.25"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3.1702,14.0975"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Network - Bus" version="0" id="O4">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6.3534,2.3542"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.58996,2.30409;11.989,6.1401"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="6.3534,2.3542"/>
+          <dia:point val="11.9389,2.3663"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="bus_handles">
+          <dia:point val="5.21003,3.2968"/>
+          <dia:point val="3.6399,2.3744"/>
+          <dia:point val="7.88141,3.2968"/>
+          <dia:point val="10.175,6.1401"/>
+          <dia:point val="8.92435,3.44756"/>
+          <dia:point val="6.83847,3.44756"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="2" to="O9" connection="11"/>
+          <dia:connection handle="4" to="O10" connection="11"/>
+          <dia:connection handle="5" to="O12" connection="11"/>
+          <dia:connection handle="6" to="O10" connection="3"/>
+          <dia:connection handle="7" to="O10" connection="6"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O5">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.21006,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.16003,5.6082;5.26006,6.4787"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="5.21006,6.4287"/>
+          <dia:point val="5.21003,5.6582"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O13" connection="11"/>
+          <dia:connection handle="1" to="O8" connection="1"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O6">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.0215,3.74907"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.4702,3.69907;11.8875,8.4582"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="4.0215,3.74907"/>
+          <dia:point val="3.5202,3.74907"/>
+          <dia:point val="3.5202,8.4082"/>
+          <dia:point val="11.8375,8.4082"/>
+          <dia:point val="11.8375,6.88101"/>
+          <dia:point val="11.3172,6.88101"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O9" connection="8"/>
+          <dia:connection handle="1" to="O12" connection="5"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O7">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10.175,7.62192"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.16006,7.28325;10.225,8.0832"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="10.175,7.62192"/>
+          <dia:point val="10.175,8.0332"/>
+          <dia:point val="5.21006,8.0332"/>
+          <dia:point val="5.21006,7.33325"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O12" connection="2"/>
+          <dia:connection handle="1" to="O13" connection="2"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Network - Storage" version="1" id="O8">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.7175,4.4993"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.6675,4.4493;5.75257,6.51078"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.7175,4.4993"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="0.9850649999999993"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.1588999999999992"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#cccccc"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80010001542891407"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.21003,6.25828"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O9">
+        <dia:attribute name="obj_pos">
+          <dia:point val="3.86929,3.2968"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.81929,3.2468;6.60076,4.25135"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="3.86929,3.2968"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6814705898130646"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.90454545953539145"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#dispatcher#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.21003,3.88136"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O10">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6.83847,3.2968"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6.78847,3.2468;8.97435,4.25135"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="6.83847,3.2968"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.0858823545189464"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.90454545953538812"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#senders#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="7.88141,3.88136"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O11">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.35748,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.30748,6.3787;8.45572,7.38325"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="7.35748,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.0482352956954173"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.90454545953538545"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#vss#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="7.8816,7.01326"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O12">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.8978,6.1401"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.8478,6.0901;11.5022,7.67192"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8.8978,6.1401"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.5544117662836525"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.4818181901724667"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#command
+handler#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="10.175,6.74872"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.71771,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.66771,6.3787;5.75242,7.38325"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.71771,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="0.98470588393071123"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.90454545953538301"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#afs#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.21006,7.01326"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.8816,6.4287"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.83141,4.15135;7.9316,6.4787"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="7.8816,6.4287"/>
+          <dia:point val="7.88141,4.20135"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O11" connection="11"/>
+          <dia:connection handle="1" to="O10" connection="2"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.3766,6.88097"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.3266,6.83097;9.0828,6.93101"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="8.3766,6.88097"/>
+          <dia:point val="9.0328,6.88101"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O11" connection="5"/>
+          <dia:connection handle="1" to="O12" connection="8"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.66933,6.88097"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.61933,6.83097;7.4366,6.93097"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="5.66933,6.88097"/>
+          <dia:point val="7.3866,6.88097"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O13" connection="5"/>
+          <dia:connection handle="1" to="O11" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Network - An amplifier speaker" version="1" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.8398,-22.1251"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.7898,-22.1751;11.2046,-19.4456"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.8398,-22.1251"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1.314752411332281"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.6295048226645621"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Network - General Monitor (With Stand)" version="1" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3.20424,-21.8387"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.19174,-21.8512;5.40635,-18.76"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="3.20424,-21.8387"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.1896140767718091"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1531205088256122"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.05000000074505806"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80010001542891407"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="4.29905,-19.0125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="11.7649,-16.3593"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.7144,-16.4098;12.6724,-16.2995"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="11.7649,-16.3593"/>
+        <dia:point val="12.6218,-16.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O20" connection="5"/>
+        <dia:connection handle="1" to="O35" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.08475,-16.8116"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.03475,-16.8616;11.9353,-15.8571"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.08475,-16.8116"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.8005882368718877"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.90454545953538812"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#bbe7bb"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_server#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="10.485,-16.2359"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.03812,-18.5769"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.98812,-18.6269;11.9893,-17.6608"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.03812,-18.5769"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.9011764721660049"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082624863"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#bbe7bb"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_audiod#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="10.4887,-18.0204"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.75078,-18.5644"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.70078,-18.6144;8.66755,-17.6483"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="5.75078,-18.5644"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.8667647074601228"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082624919"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_audioc#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="7.18416,-18.0079"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6.01211,-21.1907"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.96211,-21.2407;8.43123,-20.2746"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="6.01211,-21.1907"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.369117647058824"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.8660606108262402"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_gui#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="7.19667,-20.6342"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O24">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.98001,-16.8019"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.93001,-16.8519;5.65589,-15.8858"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.98001,-16.8019"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.6258823545189465"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082625097"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_client#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="4.29295,-16.2454"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6.01211,-20.7577"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.35471,-20.8094;6.06224,-20.7076"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6.01211,-20.7577"/>
+        <dia:point val="5.40484,-20.7593"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="8"/>
+        <dia:connection handle="1" to="O18" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O26">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.49446,-18.1314"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.44354,-18.1948;9.21742,-18.0805"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.49446,-18.1314"/>
+        <dia:point val="9.1665,-18.1439"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O22" connection="5"/>
+        <dia:connection handle="1" to="O21" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O27">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.18416,-18.5644"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.13381,-20.3751;7.24702,-18.514"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="7.18416,-18.5644"/>
+        <dia:point val="7.19667,-20.3247"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O22" connection="11"/>
+        <dia:connection handle="1" to="O23" connection="11"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O28">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10.4972,-19.4956"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="10.4382,-19.5461;10.5477,-18.5264"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10.4972,-19.4956"/>
+        <dia:point val="10.4887,-18.5769"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O17" connection="2"/>
+        <dia:connection handle="1" to="O21" connection="11"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O29">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.47486,-16.3689"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.42473,-16.419;9.25532,-16.3092"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="5.47486,-16.3689"/>
+        <dia:point val="9.20519,-16.3593"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#888888"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O24" connection="5"/>
+        <dia:connection handle="1" to="O20" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O30">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10.485,-16.8116"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="10.4348,-17.761;10.5389,-16.7614"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10.485,-16.8116"/>
+        <dia:point val="10.4887,-17.7108"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#888888"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O20" connection="11"/>
+        <dia:connection handle="1" to="O21" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O31">
+      <dia:attribute name="obj_pos">
+        <dia:point val="1.575,-10.6689"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.575,-11.2639;1.575,-10.5164"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="1.575,-10.6689"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O32">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3.6375,10.9686"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.6375,10.3736;3.6375,11.1211"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3.6375,10.9686"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O33">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.6375,10.6686"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.6375,10.0736;2.6375,10.8211"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="2.6375,10.6686"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O34">
+      <dia:attribute name="obj_pos">
+        <dia:point val="4.29295,-16.8019"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.24287,-19.7231;4.34745,-16.7518"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="4.29295,-16.8019"/>
+        <dia:point val="4.29737,-19.673"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O24" connection="11"/>
+        <dia:connection handle="1" to="O18" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Network - Storage" version="1" id="O35">
+      <dia:attribute name="obj_pos">
+        <dia:point val="12.6719,-16.9342"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="12.6219,-16.9842;13.7267,-15.1866"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="12.6719,-16.9342"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1.0048306811423449"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.1821537425204058"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#cccccc"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="13.1743,-15.3816"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O36">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.171225,-14.4141"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.159662,-14.7932;14.6787,-7.87542"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#The two main applications of the paraslash suite (shaded green) are
+para_server and para_audiod. Both run in the background usually.
+para_server maintains the audio file database and acts as the streaming
+source, while para_audiod is the streaming client.
+
+The two client programs, para_client and para_audioc communicate
+with para_server and para_audiod, respectively.
+
+para_gui controls para_server/audiod by executing paraslash commands.
+Command output is shown in a curses window. para_gui automatically
+executes para_audioc to obtain the state of para_audiod and para_server
+and the metadata of the current audio file.
+
+Network connections are shaded grey, local connections black.#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.171225,-14.4141"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O37">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.1003,73.0082"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.1003,72.4132;4.0578,73.1607"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_audiod#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.1003,73.0082"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O38">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.116202,82.0061"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.116202,81.627;14.5828,87.0631"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#The purpose of para_audiod is to download, decode and play an audio
+stream received from para_server. It fetches the para_server status and
+starts a suitable buffer tree (shaded blue) if an audio stream is available.
+
+The buffer tree usually consists of a receiver, any number of filters and
+a writer. The receiver downloads the audio stream from para_server and
+the filters decode or modify the received data. The writer plays the
+decoded stream.
+
+The dispatcher acts on (local) requests from para_audioc, for example to
+dump information about the current audio file.#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.116202,82.0061"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O39">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.27019,32.9375"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.258628,32.5584;13.8202,37.5006"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#The audio file selector (afs) accepts two different kinds of incoming
+connections: A bidirectional pipe shared with para_server is used for
+passing the file descriptor of the current audio file to the server
+process. The local socket is used by command handlers which query
+or update the database.
+
+To add a new file to the database, afs opens the file and locates an
+audio format handler (afh) that recognizes the file. A new database
+entry with metadata obtained from the afh is then added to the
+database.#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.27019,32.9375"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O40">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.11118,24.9782"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.11118,24.3832;7.22118,25.1307"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#The audio file selector#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.11118,24.9782"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O41">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6.8012,28.9591"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.61907,28.9091;6.85122,29.0096"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6.8012,28.9591"/>
+        <dia:point val="5.66909,28.9596"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="4"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O50" connection="8"/>
+        <dia:connection handle="1" to="O43" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O42">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.6012,28.5261"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.53094,27.1956;9.25521,28.5964"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="7.6012,28.5261"/>
+        <dia:point val="9.18495,27.2659"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O50" connection="10"/>
+        <dia:connection handle="1" to="O47" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Network - Storage" version="1" id="O43">
+      <dia:attribute name="obj_pos">
+        <dia:point val="4.61551,28.3687"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.00418,28.3187;6.23168,30.1163"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="4.61551,28.3687"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1.0048306811423449"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.1821537425204058"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#cccccc"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#audio files#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.11793,29.9213"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O44">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.2512,28.5261"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.19859,26.8922;7.30128,28.5762"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="7.2512,28.5261"/>
+        <dia:point val="7.24867,26.9423"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O50" connection="11"/>
+        <dia:connection handle="1" to="O46" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O45">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.2512,29.3922"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.20042,29.3414;7.32552,30.9457"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="7.2512,29.3922"/>
+        <dia:point val="7.27474,30.8949"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O50" connection="2"/>
+        <dia:connection handle="1" to="O49" connection="11"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O46">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.84838,26.0762"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.79838,26.0262;8.69897,26.9923"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="5.84838,26.0762"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.8005882368718877"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082624941"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#bbe7bb"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#para_server#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="7.24867,26.6327"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O47">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.04127,26.3998"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.99127,26.3498;10.0786,27.3159"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.04127,26.3998"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="0.98735294275424068"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082625274"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#afh#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="9.53495,26.9563"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Network - Storage" version="1" id="O48">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.84318,28.3795"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.65435,28.3295;10.0368,30.1271"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8.84318,28.3795"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1.0048306811423449"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.1821537425204058"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#cccccc"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#osl db#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="9.3456,29.9321"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O49">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.20738,30.8949"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.15738,30.8449;9.39209,31.811"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="5.20738,30.8949"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4.1347058839307111"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082624874"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#command handler#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="7.27474,31.4514"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="BPMN - Task" version="1" id="O50">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6.78135,28.5261"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="6.73135,28.4761;7.77105,29.4422"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="6.78135,28.5261"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="0.93970588393071131"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="0.86606061082625274"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#afs#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="7.2512,29.0826"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="subscale">
+        <dia:real val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O51">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.7012,28.9591"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.65087,28.9088;8.844,29.0167"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="7.7012,28.9591"/>
+        <dia:point val="8.79367,28.9663"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000149011612"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O50" connection="5"/>
+        <dia:connection handle="1" to="O48" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O52">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.1025,49.0569"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.1025,48.4619;5.9375,49.2094"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#The OSL database#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.1025,49.0569"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O53">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.2206,58.055"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.209037,57.6759;14.1806,68.0509"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#Metadata about all known audio files is stored in serveral tables of a
+database which is driven by libosl, the object storage layer library.
+
+The "audio files" table is the main table of the database. It contains
+path, hash and metadata of each known file.
+
+The "attributes" table maps each of the 64 possible attributes to a
+string. The attribute value of the file's metadata is translated through
+this table.
+
+The tables shown shaded are blob tables which support add, rm, mv,
+cat, ls commands. All of these are optional.
+
+The "score" table describes the subset of admissible files for the
+current playlist or mood. This table is created on demand, resides
+only in memory and is discarded on exit.
+
+When the next audio file is to be streamed, the audio file selector gets
+the entry with the highest score from the "score" table, obtains path,
+hash, and metadata for this entry from the "audio files" table, opens
+the path and verifies the hash.#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.49388889176727813"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0.2206,58.055"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Flowchart - Extract" version="1" id="O54">
+        <dia:attribute name="obj_pos">
+          <dia:point val="2.048,53.4404"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="1.88598,53.3799;6.86502,55.0782"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="2.048,53.4404"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="4.6550000029802332"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.5877777865147884"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#audio files#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.3755,54.7547"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O55">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.0846,55.0212"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.94554,54.9564;13.0687,56.659"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.0846,55.0212"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="3.8450000029802323"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.5877777865147775"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#cccccc"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#playlists#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="11.0071,56.3355"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O56">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.9317,52.7104"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.803,52.6426;9.5304,54.3482"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="5.9317,52.7104"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="3.4700000029802327"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.5877777865147873"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#cccccc"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#images#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="7.6667,54.0247"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O57">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6.8382,50.5875"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6.68191,50.5261;11.4495,52.2253"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="6.8382,50.5875"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="4.4550000029802321"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.587777786514786"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#attributes#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9.0657,51.9018"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O58">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.7265,52.7977"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.61888,52.7202;12.5141,54.4355"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.7265,52.7977"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6800000029802327"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.5877777865147884"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#cccccc"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#lyrics#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="11.0665,54.112"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O59">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.4821,55.0581"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.35938,54.9882;8.85482,56.6959"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="5.4821,55.0581"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="3.2500000029802325"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.58777778651479"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#cccccc"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#moods#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="7.1071,56.3724"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Flowchart - Extract" version="1" id="O60">
+        <dia:attribute name="obj_pos">
+          <dia:point val="2.4988,50.5557"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="2.35946,50.4909;6.49314,52.1935"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="2.4988,50.5557"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="3.855000002980232"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.587777786514786"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="4"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#score#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.4263,51.87"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O61">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.41144,52.1937"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.33898,52.1428;4.46231,53.4702"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="4.41144,52.1937"/>
+          <dia:point val="4.38985,53.4194"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O60" connection="12"/>
+          <dia:connection handle="1" to="O54" connection="12"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O62">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.03792,53.8314"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.96922,52.1555;7.74887,53.9001"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="5.03792,53.8314"/>
+          <dia:point val="7.68017,52.2242"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O54" connection="12"/>
+          <dia:connection handle="1" to="O57" connection="12"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="BPMN - Task" version="1" id="O63">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.42569,77.8748"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.37569,77.8248;7.15716,78.8293"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.42569,77.8748"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6814705898130642"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.90454545953538923"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#dispatcher#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.76643,78.4594"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O64">
+        <dia:attribute name="obj_pos">
+          <dia:point val="1.63592,76.2823"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="1.58592,76.2323;5.11916,77.2368"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="1.63592,76.2823"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="3.4332352956954173"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.9045454595353889"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#status fetcher#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.52916666975065518"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="3.35254,76.8669"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Network - An amplifier speaker" version="1" id="O65">
+        <dia:attribute name="obj_pos">
+          <dia:point val="11.9716,75.7366"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.9216,75.6866;13.0196,77.7826"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="11.9716,75.7366"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="0.99802008040072598"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.996040160801452"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#ffffff"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O66">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.31303,74.415"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.26303,74.365;7.16362,75.3311"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.31303,74.415"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.8005882368718877"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.8660606108262453"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#bbe7bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#para_server#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.71332,74.9715"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O67">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.11064,76.3016"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.06064,76.2516;9.18034,77.2177"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="7.11064,76.3016"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.0197058839307109"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.86606061082625108"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#bbbbee"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#receiver#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.12049,76.8581"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O68">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.56352,76.3016"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.51352,76.2516;11.1726,77.2177"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.56352,76.3016"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.5591176486365934"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.86606061082625141"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#bbbbee"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#filter1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="10.3431,76.8581"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O69">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.48684,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.43684,78.3514;11.2283,79.3175"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.48684,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.691470589813064"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.86606061082625141"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#bbbbee"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#filter 2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="10.3326,78.9579"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O70">
+        <dia:attribute name="obj_pos">
+          <dia:point val="11.6997,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.6497,78.3514;13.2903,79.3175"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="11.6997,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.5405882368718877"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.86606061082624797"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#bbbbee"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#writer#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="12.47,78.9579"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="BPMN - Task" version="1" id="O71">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.32709,79.7644"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.27709,79.7144;7.24386,80.6805"/>
+        </dia:attribute>
+        <dia:attribute name="meta">
+          <dia:composite type="dict"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.32709,79.7644"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.8667647074601228"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="0.86606061082624586"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:attribute name="line_colour">
+          <dia:color val="#000000"/>
+        </dia:attribute>
+        <dia:attribute name="fill_colour">
+          <dia:color val="#888888"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_style">
+          <dia:enum val="0"/>
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="padding">
+          <dia:real val="0.10000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#para_audioc#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.49388889176727813"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="5.76047,80.3209"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="flip_horizontal">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="flip_vertical">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="subscale">
+          <dia:real val="1"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O72">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.11362,74.848"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.06362,74.798;8.17049,76.3516"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="7.11362,74.848"/>
+          <dia:point val="8.12049,74.848"/>
+          <dia:point val="8.12049,76.3016"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#888888"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O66" connection="5"/>
+          <dia:connection handle="1" to="O67" connection="11"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O73">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.31303,74.848"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.26303,73.798;4.36303,76.3323"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="4.31303,74.848"/>
+          <dia:point val="3.31303,74.848"/>
+          <dia:point val="3.31303,73.848"/>
+          <dia:point val="3.35254,73.848"/>
+          <dia:point val="3.35254,76.2823"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#888888"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O66" connection="8"/>
+          <dia:connection handle="1" to="O64" connection="11"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O74">
+        <dia:attribute name="obj_pos">
+          <dia:point val="3.35254,77.1868"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.30254,77.1368;4.47569,78.3771"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="3.35254,77.1868"/>
+          <dia:point val="3.35254,78.3271"/>
+          <dia:point val="4.42569,78.3271"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O64" connection="2"/>
+          <dia:connection handle="1" to="O63" connection="8"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O75">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.10716,78.3271"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.05716,77.1177;8.17049,78.3771"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="7.10716,78.3271"/>
+          <dia:point val="8.12049,78.3271"/>
+          <dia:point val="8.12049,77.1677"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O63" connection="5"/>
+          <dia:connection handle="1" to="O67" connection="2"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O76">
+        <dia:attribute name="obj_pos">
+          <dia:point val="5.76643,78.7793"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.71017,78.729;5.81673,79.8147"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="5.76643,78.7793"/>
+          <dia:point val="5.76047,79.7644"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O63" connection="2"/>
+          <dia:connection handle="1" to="O71" connection="11"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O77">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.02049,76.7346"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.97049,76.6846;9.71808,76.7846"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="9.02049,76.7346"/>
+          <dia:point val="9.66808,76.7346"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O67" connection="5"/>
+          <dia:connection handle="1" to="O68" connection="8"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O78">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10.3431,77.1677"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="10.2822,77.1173;10.3935,78.4518"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="10.3431,77.1677"/>
+          <dia:point val="10.3326,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O68" connection="2"/>
+          <dia:connection handle="1" to="O69" connection="11"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O79">
+        <dia:attribute name="obj_pos">
+          <dia:point val="12.4706,77.7326"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="12.42,77.6826;12.5206,78.4514"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="12.4706,77.7326"/>
+          <dia:point val="12.47,78.4014"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O65" connection="2"/>
+          <dia:connection handle="1" to="O70" connection="11"/>
+        </dia:connections>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O80">
+        <dia:attribute name="obj_pos">
+          <dia:point val="11.0658,78.8344"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.0158,78.7844;11.849,78.8844"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="11.0658,78.8344"/>
+          <dia:point val="11.799,78.8344"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.10000000149011612"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O69" connection="5"/>
+          <dia:connection handle="1" to="O70" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
index 4d63a08fb9721ff0d0f824eda66ac5e4fb0b49e5..f292af25d723764f18e555e8daac714089f3d7f5 100644 (file)
@@ -30,6 +30,7 @@
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
        [<a href="para_fade.man.html">para_fade</a>]
+       [<a href="para_play.man.html">para_play</a>]
 </p>
 
 <h2> Source code documentation </h2>
index 393fce9352d65a48f751735b886f5f57473dbfe3..dd0b9c7838a0dfa3ecc85e9c8c9c731e8662d02d 100644 (file)
@@ -9,7 +9,9 @@ provided at this point. There are several ways to download the source:
 
                Clone the git repository by executing
 
-               <p> <pre> <kbd> git clone git://paraslash.systemlinux.org/git paraslash </kbd> </pre> </p>
+               <p> <pre> <kbd>
+                       git clone git://git.tuebingen.mpg.de/paraslash.git
+               </kbd> </pre> </p>
 
                <p> The repository contains the full history of the
                project since 2006, all work in progress and the source
@@ -63,7 +65,7 @@ provided at this point. There are several ways to download the source:
 
                The
 
-                       <a href="/gitweb/gitweb.cgi?p=.git;a=summary">gitweb</a>
+                       <a href="http://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
 
                page contains a snapshot link for each revision. This
                allows to get a specific revision without downloading
diff --git a/web/gitweb.css b/web/gitweb.css
deleted file mode 100644 (file)
index 08110a6..0000000
+++ /dev/null
@@ -1,506 +0,0 @@
-body {
-       font-family: sans-serif;
-       font-size: small;
-       /*border: solid #d9d8d1;*/
-       border-width: 1px;
-       margin: 0px;
-       padding: 10px;
-       background-color: black; 
-       color: #cccccc;
-       height: 100%;
-}
-
-a {
-       color: #BA3708;
-}
-
-a:hover {
-       color: #BA3708;
-       background-color: #ffff00;
-}
-
-span.cntrl {
-       border: dashed #aaaaaa;
-       border-width: 1px;
-       padding: 0px 2px 0px 2px;
-       margin:  0px 2px 0px 2px;
-}
-
-img.logo {
-       float: right;
-       border-width: 0px;
-}
-
-div.page_header {
-       /*height: 10px;*/
-       padding: 8px;
-       font-size: 150%;
-       font-weight: bold;
-       background-color: #151515;
-}
-
-div.page_header a:visited, a.header {
-       color: #BA3708;
-}
-
-div.page_header a:hover {
-       color: #880000;
-}
-
-div.page_nav {
-       padding: 8px;
-}
-
-div.page_nav a:visited {
-       color: #BA3708;
-}
-
-div.page_path {
-       padding: 8px;
-       font-weight: bold;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-}
-
-div.page_footer {
-       height: 17px;
-       padding: 4px 8px;
-       background-color: #d9d8d1;
-}
-
-div.page_footer_text {
-       float: left;
-       color: #555555;
-       font-style: italic;
-}
-
-div.page_body {
-       padding: 8px;
-       font-family: monospace;
-}
-
-div.title, a.title {
-       display: block;
-       padding: 6px 8px;
-       font-weight: bold;
-       background-color: #555555;
-       text-decoration: none;
-       color: white;
-}
-
-a.title:hover {
-       background-color: #AA3100;
-}
-
-div.title_text {
-       padding: 6px 0px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       font-family: monospace;
-}
-
-div.log_body {
-       padding: 8px 8px 8px 150px;
-}
-
-span.age {
-       position: relative;
-       float: left;
-       width: 142px;
-       font-style: italic;
-}
-
-span.signoff {
-       color: #888888;
-}
-
-div.log_link {
-       padding: 0px 8px;
-       font-size: 70%;
-       font-family: sans-serif;
-       font-style: normal;
-       position: relative;
-       float: left;
-       width: 136px;
-}
-
-div.list_head {
-       padding: 6px 8px 4px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 0px;
-       font-style: italic;
-}
-
-div.author_date {
-       padding: 8px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px 0px;
-       font-style: italic;
-}
-
-a.list {
-       text-decoration: none;
-       color: #BA3708;
-}
-
-a.subject, a.name {
-       font-weight: bold;
-}
-
-table.tags a.subject {
-       font-weight: normal;
-}
-
-a.list:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-a.text {
-       text-decoration: none;
-       color: #0000cc;
-}
-
-a.text:visited {
-       text-decoration: none;
-       color: #880000;
-}
-
-a.text:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-table {
-       padding: 8px 4px;
-}
-
-table.project_list {
-       border-spacing: 0;
-}
-
-table.diff_tree {
-       border-spacing: 0;
-       font-family: monospace;
-}
-
-table.combined.diff_tree th {
-       text-align: center;
-}
-
-table.combined.diff_tree td {
-       padding-right: 24px;
-}
-
-table.combined.diff_tree th.link,
-table.combined.diff_tree td.link {
-       padding: 0px 2px;
-}
-
-table.combined.diff_tree td.nochange a {
-       color: #6666ff;
-}
-
-table.combined.diff_tree td.nochange a:hover,
-table.combined.diff_tree td.nochange a:visited {
-       color: #d06666;
-}
-
-table.blame {
-       border-collapse: collapse;
-}
-
-table.blame td {
-       padding: 0px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-th {
-       padding: 2px 5px;
-       font-size: 100%;
-       text-align: left;
-}
-
-tr.light:hover {
-       background-color: yellow;
-       color: #BA3708;
-}
-
-tr.dark {
-       background-color: #333333;
-}
-
-tr.dark2 {
-       background-color: #333333;
-}
-
-tr.dark:hover {
-       background-color: yellow;
-       color: #BA3708;
-}
-
-td {
-       padding: 2px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-td.link, td.selflink {
-       padding: 2px 5px;
-       font-family: sans-serif;
-       font-size: 70%;
-}
-
-td.selflink {
-       padding-right: 0px;
-}
-
-td.sha1 {
-       font-family: monospace;
-}
-
-td.error {
-       color: red;
-       background-color: yellow;
-}
-
-td.current_head {
-       text-decoration: underline;
-}
-
-table.diff_tree span.file_status.new {
-       color: #008000;
-}
-
-table.diff_tree span.file_status.deleted {
-       color: #c00000;
-}
-
-table.diff_tree span.file_status.moved,
-table.diff_tree span.file_status.mode_chnge {
-       color: #777777;
-}
-
-table.diff_tree span.file_status.copied {
-  color: #70a070;
-}
-
-/* noage: "No commits" */
-table.project_list td.noage {
-       color: #808080;
-       font-style: italic;
-}
-
-/* age2: 60*60*24*2 <= age */
-table.project_list td.age2, table.blame td.age2 {
-       font-style: italic;
-}
-
-/* age1: 60*60*2 <= age < 60*60*24*2 */
-table.project_list td.age1 {
-       color: #009900;
-       font-style: italic;
-}
-
-table.blame td.age1 {
-       color: #009900;
-       background: transparent;
-}
-
-/* age0: age < 60*60*2 */
-table.project_list td.age0 {
-       color: #009900;
-       font-style: italic;
-       font-weight: bold;
-}
-
-table.blame td.age0 {
-       color: #009900;
-       background: transparent;
-       font-weight: bold;
-}
-
-td.pre, div.pre, div.diff {
-       font-family: monospace;
-       font-size: 12px;
-       white-space: pre;
-}
-
-td.mode {
-       font-family: monospace;
-}
-
-/* styling of diffs (patchsets): commitdiff and blobdiff views */
-div.diff.header,
-div.diff.extended_header {
-       white-space: normal;
-}
-
-div.diff.header {
-       font-weight: bold;
-
-       background-color: #333333;
-
-       margin-top: 4px;
-       padding: 4px 0px 2px 0px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 1px 0px;
-}
-
-div.diff.header a.path {
-       text-decoration: underline;
-}
-
-div.diff.extended_header,
-div.diff.extended_header a.path,
-div.diff.extended_header a.hash {
-       color: #cccccc;
-}
-
-div.diff.extended_header .info {
-       color: #b0b0b0;
-}
-
-div.diff.extended_header {
-       background-color: #333333;
-       padding: 2px 0px 2px 0px;
-}
-
-div.diff a.list,
-div.diff a.path,
-div.diff a.hash {
-       text-decoration: none;
-}
-
-div.diff a.list:hover,
-div.diff a.path:hover,
-div.diff a.hash:hover {
-       text-decoration: underline;
-}
-
-div.diff.to_file a.path,
-div.diff.to_file {
-       color: #007000;
-}
-
-div.diff.add {
-       color: #008800;
-}
-
-div.diff.from_file a.path,
-div.diff.from_file {
-       color: #ff0000;
-}
-
-div.diff.rem {
-       color: #cc0000;
-}
-
-div.diff.chunk_header a,
-div.diff.chunk_header {
-       color: #990099;
-}
-
-div.diff.chunk_header {
-       border: dotted #ffe0ff;
-       border-width: 1px 0px 0px 0px;
-       margin-top: 2px;
-}
-
-div.diff.chunk_header span.chunk_info {
-       background-color: #000000;
-}
-
-div.diff.chunk_header span.section {
-       color: #aa22aa;
-}
-
-div.diff.incomplete {
-       color: #cccccc;
-}
-
-div.diff.nodifferences {
-       font-weight: bold;
-       color: #600000;
-}
-
-div.index_include {
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       padding: 12px 8px;
-}
-
-div.search {
-       font-size: 100%;
-       font-weight: normal;
-       margin: 4px 8px;
-       float: right;
-       top: 56px;
-       right: 12px
-}
-
-td.linenr {
-       text-align: right;
-}
-
-a.linenr {
-       color: #999999;
-       text-decoration: none
-}
-
-a.rss_logo {
-       float: right;
-       padding: 3px 0px;
-       width: 35px;
-       line-height: 10px;
-       border: 1px solid;
-       border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
-       color: #ffffff;
-       background-color: #ff6600;
-       font-weight: bold;
-       font-family: sans-serif;
-       font-size: 70%;
-       text-align: center;
-       text-decoration: none;
-}
-
-a.rss_logo:hover {
-       background-color: #ee5500;
-}
-
-span.refs span {
-       padding: 0px 4px;
-       font-size: 70%;
-       font-weight: normal;
-       border: 1px solid;
-       background-color: #222222;
-       border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-
-span.refs span.ref {
-       background-color: #aaaaff;
-       border-color: #ccccff #0033cc #0033cc #ccccff;
-}
-
-span.refs span.tag {
-       background-color: #ffffaa;
-       border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-
-span.refs span.head {
-       background-color: #222222;
-       border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-
-span.atnight {
-       color: #cc0000;
-}
-
-span.match {
-       color: #e00000;
-}
-
-div.binary {
-       font-style: italic;
-}
diff --git a/web/gitweb_footer.html b/web/gitweb_footer.html
deleted file mode 100644 (file)
index 8beeeaf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-</td>
-</tr>
-</table>
diff --git a/web/gitweb_header.html.in b/web/gitweb_header.html.in
deleted file mode 100644 (file)
index d133827..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<table border="0" cellpadding="10" cellspacing="0">
-<tr>
-       <td>
-                       <a title="paraslash homepage" href="/">
-                       <img src="@web_root@/paraslash.png" alt="paraslash"/>
-                       </a>
-       </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">
-               <a href="@web_root@/index.html">Home</a><br></br>
-               <a href="@web_root@/NEWS.html">News</a><br></br>
-               <a href="@web_root@/FEATURES.html">Features</a><br></br>
-               <a href="@web_root@/download.html">Download</a><br></br>
-               <a href="@web_root@/screenshots.html">Screenshots</a><br></br>
-               <a href="/gitweb/gitweb.cgi?p=.git;a=summary">Changes</a><br></br>
-               <a href="@web_root@/documentation.html">Documentation</a><br></br>
-               <a href="@web_root@/license.html">License</a><br></br>
-               <a href="@web_root@/contact.html">Contact</a><br></br>
-               <a href="@web_root@/CREDITS.html">Credits</a><br></br>
-       </td>
-       <td valign="top">
index f7cb27764a18d204b5fee1e491fdddeed1aff859..a5d01204939d789cf4841499ce31119bfd5e1c35 100644 (file)
@@ -8,32 +8,24 @@
        <link rel="shortcut icon" href="paraslash.ico">
 </head>
 <body>
-       <basefont face="lucida, helvetica, arial" size="3">
-       <table border="0" cellpadding="10" cellspacing="0">
+       <table>
        <tr>
                <td>
-                       <a href="/"><IMG SRC="paraslash.png" alt="paraslash" border="0"></a><BR>
+                       <a title="paraslash homepage" href=".">
+                               <img src="paraslash.png" alt="paraslash">
+                       </a>
                </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.
+                       <h3>Paraslash network audio streaming tools</h3>
                </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="/gitweb/gitweb.cgi?p=.git;a=summary">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 valign="top">
+                       <br>
+                       <a href=".">About</a><br>
+                       <a href="news.html">News</a><br>
+                       <a href="download.html">Download</a><br>
+                       <a href="documentation.html">Documentation</a><br>
+                       <a href="devel.html">Development</a><br>
                </td>
-               <td Valign="TOP">
-       <hr>
+               <td>
index 94fbcad23938bae3e8a328feab7628f616535546..98235b8621324bc9176d2f9803ea4cf097afd83a 100644 (file)
@@ -8,32 +8,27 @@
        <link rel="shortcut icon" href="../../paraslash.ico">
 </head>
 <body>
-       <basefont face="lucida, helvetica, arial" size="3">
-       <table border="0" cellpadding="10" cellspacing="0">
+       <table>
        <tr>
                <td>
-                       <a href="../..//"><IMG SRC="../../paraslash.png" alt="paraslash" border="0"></a><BR>
+                       <a title="paraslash homepage" href="../..//">
+                               <img src="../../paraslash.png" alt="paraslash">
+                       </a>
                </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.
+                       <h3>Paraslash network audio streaming tools</h3>
                </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="../../gitweb/gitweb.cgi?p=.git;a=summary">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 valign="top">
+                       <br>
+                       <a href="../..">About</a><br>
+                       <a href="../../news.html">News</a><br>
+                       <a href="../../download.html">Download</a><br>
+                       <a href="../../documentation.html">Documentation</a><br>
+                       <a href="../../devel.html">Development</a><br>
                </td>
-               <td Valign="TOP">
-       <hr>
+               <td>
+                       <h1>API Reference</h1>
+                       <hr />
+
diff --git a/web/images/git-logo.png b/web/images/git-logo.png
deleted file mode 100644 (file)
index 16ae8d5..0000000
Binary files a/web/images/git-logo.png and /dev/null differ
index 860c5abd2c8bbe9a1557c1912666eb53f7d6394e..fd17a649265020637a64e568160f83098c42dafb 100644 (file)
Binary files a/web/images/paraslash.ico and b/web/images/paraslash.ico differ
index d4a1bd9aa42ea2eec7842754ddf08d30910b10c3..a06d6a1a9d67f4d4bf62fc81fcee3348c0d9993b 100644 (file)
Binary files a/web/images/paraslash.png and b/web/images/paraslash.png differ
diff --git a/web/images/signature.png b/web/images/signature.png
new file mode 100644 (file)
index 0000000..9524182
Binary files /dev/null and b/web/images/signature.png differ
diff --git a/web/images/tar-icon.png b/web/images/tar-icon.png
new file mode 100644 (file)
index 0000000..4b1b472
Binary files /dev/null and b/web/images/tar-icon.png differ
diff --git a/web/index.in.html b/web/index.in.html
deleted file mode 100644 (file)
index c9a82a8..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-<h1>Events</h1>
-<hr>
-<ul>
-       <li>2013-07-29: <a href="releases/paraslash-0.4.13.tar.bz2">paraslash-0.4.13</a>
-               <a href="releases/paraslash-0.4.13.tar.bz2.asc">(sig)</a>
-               "spectral gravity"
-       </li>
-       <li>2012-12-20: <a href="releases/paraslash-0.4.12.tar.bz2">paraslash-0.4.12</a>
-               <a href="releases/paraslash-0.4.12.tar.bz2.asc">(sig)</a>
-               "volatile relativity"
-       </li>
-       <li>2012-07-20: <a href="releases/paraslash-0.4.11.tar.bz2">paraslash-0.4.11</a>
-               <a href="releases/paraslash-0.4.11.tar.bz2.asc">(sig)</a>
-               "mutual diversity"
-       </li>
-       <li>2012-03-30: <a href="releases/paraslash-0.4.10.tar.bz2">paraslash-0.4.10</a>
-               <a href="releases/paraslash-0.4.10.tar.bz2.asc">(sig)</a>
-               "heterogeneous vacuum"
-       </li>
-       <li>2011-12-06: <a href="releases/paraslash-0.4.9.tar.bz2">paraslash-0.4.9</a>
-               <a href="releases/paraslash-0.4.9.tar.bz2.asc">(sig)</a>
-               "hybrid causality"
-       </li>
-       <li>2011-08-19: <a href="releases/paraslash-0.4.8.tar.bz2">paraslash-0.4.8</a>
-               <a href="releases/paraslash-0.4.8.tar.bz2.asc">(sig)</a>
-               "nested assignment"
-       </li>
-       <li>2011-06-01: <a href="releases/paraslash-0.4.7.tar.bz2">paraslash-0.4.7</a>
-               <a href="releases/paraslash-0.4.7.tar.bz2.asc">(sig)</a>
-               "infinite rollback"
-       </li>
-       <li>2011-03-31: <a href="releases/paraslash-0.4.6.tar.bz2">paraslash-0.4.6</a>
-               <a href="releases/paraslash-0.4.6.tar.bz2.asc">(sig)</a>
-               "deterministic entropy"
-       </li>
-       <li>2010-12-17: <a href="releases/paraslash-0.4.5.tar.bz2">paraslash-0.4.5</a>
-               <a href="releases/paraslash-0.4.5.tar.bz2.asc">(sig)</a>
-               "symmetric randomization"
-       </li>
-       <li>2010-08-06: <a href="releases/paraslash-0.4.4.tar.bz2">paraslash-0.4.4</a>
-               <a href="releases/paraslash-0.4.4.tar.bz2.asc">(sig)</a>
-               "persistent regularity"
-       </li>
-       <li>2010-07-05: <a href="releases/paraslash-0.4.3.tar.bz2">paraslash-0.4.3</a>
-               <a href="releases/paraslash-0.4.3.tar.bz2.asc">(sig)</a>
-               "imaginary radiation"
-       </li>
-       <li>2010-06-23: <a href="cooking.html">What's cooking page</a> online</li>
-       <li>2010-04-23: <a href="releases/paraslash-0.4.2.tar.bz2">paraslash-0.4.2</a>
-               <a href="releases/paraslash-0.4.2.tar.bz2.asc">(sig)</a>
-               "associative expansion"
-       </li>
-       <li>2009-12-22: <a href="releases/paraslash-0.4.1.tar.bz2">paraslash-0.4.1</a>
-               <a href="releases/paraslash-0.4.1.tar.bz2.asc">(sig)</a>
-               "concurrent horizon"
-       </li>
-       <li>2009-12-07: <a href="releases/paraslash-0.3.6.tar.bz2">paraslash-0.3.6</a>
-               <a href="releases/paraslash-0.3.6.tar.bz2.asc">(sig)</a>
-               "cubic continuity"
-       </li>
-       <li>2009-11-10: <a href="releases/paraslash-0.4.0.tar.bz2">paraslash-0.4.0</a>
-               <a href="releases/paraslash-0.4.0.tar.bz2.asc">(sig)</a>
-               "simultaneous independence"
-       </li>
-       <li>2009-09-21: <a href="releases/paraslash-0.3.5.tar.bz2">paraslash-0.3.5</a>
-               <a href="releases/paraslash-0.3.5.tar.bz2.asc">(sig)</a>
-               "symplectic separability"
-       </li>
-       <li>2009-05-07: <a href="releases/paraslash-0.3.4.tar.bz2">paraslash-0.3.4</a>
-               <a href="releases/paraslash-0.3.4.tar.bz2.asc">(sig)</a>
-               "elliptic inheritance"
-       </li>
-       <li>2008-12-01: <a href="releases/paraslash-0.3.3.tar.bz2">paraslash-0.3.3</a>
-               <a href="releases/paraslash-0.3.3.tar.bz2.asc">(sig)</a>
-               "axiomatic perspectivity"
-       </li>
-       <li>2008-04-11: <a href="releases/paraslash-0.3.2.tar.bz2">paraslash-0.3.2</a>
-               <a href="releases/paraslash-0.3.2.tar.bz2.asc">(sig)</a>
-               "probabilistic parity"
-       </li>
-       <li>2008-02-23: <a href="releases/paraslash-0.3.1.tar.bz2">paraslash-0.3.1</a>
-               <a href="releases/paraslash-0.3.1.tar.bz2.asc">(sig)</a>
-               "liquid interaction"
-       </li>
-       <li>2008-01-12: <a href="releases/paraslash-0.3.0.tar.bz2">paraslash-0.3.0</a>
-               <a href="releases/paraslash-0.3.0.tar.bz2.asc">(sig)</a>
-               "solar saturation"
-       </li>
-       <li>2007-11-20: <a href="releases/paraslash-0.2.17.tar.bz2">paraslash-0.2.17</a>
-               <a href="releases/paraslash-0.2.17.tar.bz2.asc">(sig)</a>
-               "isotropic threshold"
-       </li>
-       <li>2007-04-05: <a href="releases/paraslash-0.2.16.tar.bz2">paraslash-0.2.16</a>
-               <a href="releases/paraslash-0.2.16.tar.bz2.asc">(sig)</a>
-               "neural discharge"
-       </li>
-       <li>2007-02-16: <a href="releases/paraslash-0.2.15.tar.bz2">paraslash-0.2.15</a>
-               <a href="releases/paraslash-0.2.15.tar.bz2.asc">(sig)</a>
-               "inductive resonance"
-       </li>
-       <li>2006-10-15: <a href="releases/paraslash-0.2.14.tar.bz2">paraslash-0.2.14</a>
-               <a href="releases/paraslash-0.2.14.tar.bz2.asc">(sig)</a>
-               "transient singularity"
-       </li>
-       <li>2006-07-14: <a href="releases/paraslash-0.2.13.tar.bz2">paraslash-0.2.13</a>
-               <a href="releases/paraslash-0.2.13.tar.bz2.asc">(sig)</a>
-               "sonic convolution"
-       </li>
-       <li>2006-05-12: <a href="releases/paraslash-0.2.12.tar.bz2">paraslash-0.2.12</a>
-               <a href="releases/paraslash-0.2.12.tar.bz2.asc">(sig)</a>
-               "oriented abstraction"
-       </li>
-       <li>2006-03-11: <a href="releases/paraslash-0.2.11.tar.bz2">paraslash-0.2.11</a>
-               <a href="releases/paraslash-0.2.11.tar.bz2.asc">(sig)</a>
-               "atomic duality"
-       </li>
-       <li>2006-02-22: <a href="/gitweb/gitweb.cgi?p=.git;a=shortlog">browsable changelog</a> (gitweb)</li>
-       <li>2006-02-17: <a href="releases/paraslash-0.2.10.tar.bz2">paraslash-0.2.10</a>
-       <a href="releases/paraslash-0.2.10.tar.bz2.asc">(sig)</a> "cyclic attractor"</li>
-       <li>2006-02-16: <a href="doxygen/html/index.html">API Reference</a> online</li>
-       <li>2006-01-24: paraslash-0.2.9 "progressive turbulence"</li>
-       <li>2006-01-02: paraslash-0.2.8 "dynamic accumulation"</li>
-       <li>2005-12-27: paraslash-0.2.7 "transparent invariance"</li>
-       <li>2005-11-12: new web pages</li>
-       <li>2005-10-29: paraslash-0.2.6 "recursive compensation"</li>
-       <li>2005-10-27: <a href="documentation.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>
diff --git a/web/license.in.html b/web/license.in.html
deleted file mode 100644 (file)
index c732a07..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<h1>License</h1>
-<hr>
-
-<p> Paraslash is licensed under the <a
-href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU
-General Public License, Version 2</a>, which is generally just
-abbreviated as the GPL license, or just the GPL.</p>
-
-<p> Check out the <a
-href="http://www.gnu.org/cgi-bin/license-quiz.cgi">GPL
-License Quiz</a> or take a look at the <a
-href="http://www.gnu.org/licenses/gpl-faq.html">Frequently Asked
-Questions about the GNU GPL</a>. </p>
-
-<p> As of this writing (June 2007) the author
-does not see any benefits in switching to the new <a
-href="http://www.gnu.org/licenses/gpl-3.0.txt"> version 3 of the
-GPL</a> just released. However, this may change once version 3 becomes
-widely used.</p>
index 3f5f148fba7819ac03395792ebabec54d84ed6a4..d63b07e8cbb69067439db6bca4875f9867908831 100644 (file)
@@ -112,7 +112,7 @@ can be used by any scripting language to produce user interfaces with
 little programming effort.
 
 All connections between para_server and para_client are encrypted
-with a symmetric RC4 session key. For each user of paraslash you must
+with a symmetric session key. For each user of paraslash you must
 create a public/secret RSA key pair for authentication.
 
 If para_client is started without non-option arguments, an interactive
@@ -209,11 +209,12 @@ For the impatient:
        sudo apt-get install autoconf libssl-dev help2man gengetopt \
               libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
               libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev \
-              libasound2-dev libao-dev libreadline-dev libncurses-dev
+              libasound2-dev libao-dev libreadline-dev libncurses-dev \
+              libopus-dev
 
 Detailed description: In any case you'll need
 
-       - XREFERENCE(http://systemlinux.org/~maan/osl/, libosl).
+       - XREFERENCE(http://people.tuebingen.mpg.de/maan/osl/, libosl).
        The _object storage layer_ library is used by para_server. To
        clone the source code repository, execute
 
@@ -232,14 +233,6 @@ Detailed description: In any case you'll need
        scripts which run during compilation require the EMPH(Bourne
        again shell).  It is most likely already installed.
 
-       - XREFERENCE(http://www.openssl.org/, openssl) or
-       XREFERENCE(ftp://ftp.gnupg.org/gcrypt/libgcrypt/, libgcrypt).
-       At least one of these two libraries is needed as the backend
-       for cryptographic routines on both the server and the client
-       side. Both openssl and libgcrypt are usually shipped with the
-       distro, but you might have to install the development package
-       (libssl-dev or libgcrypt-dev on debian systems) as well.
-
        - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/gengetopt/, gengetopt)
        is needed to generate the C code for the command line parsers
        of all paraslash executables.
@@ -249,6 +242,14 @@ Detailed description: In any case you'll need
 
 Optional:
 
+       - XREFERENCE(http://www.openssl.org/, openssl) or
+       XREFERENCE(ftp://ftp.gnupg.org/gcrypt/libgcrypt/, libgcrypt).
+       At least one of these two libraries is needed as the backend
+       for cryptographic routines on both the server and the client
+       side. Both openssl and libgcrypt are usually shipped with the
+       distro, but you might have to install the development package
+       (libssl-dev or libgcrypt-dev on debian systems) as well.
+
        - XREFERENCE(http://www.underbit.com/products/mad/, libmad).
        To compile in MP3 support for paraslash, the development
        package must be installed. It is called libmad0-dev on
@@ -258,7 +259,8 @@ Optional:
        - XREFERENCE(http://www.underbit.com/products/mad/,
        libid3tag). For version-2 ID3 tag support, you'll need
        the libid3tag development package libid3tag0-dev. Without
-       libid3tag, only version one tags are recognized.
+       libid3tag, only version-1 tags are recognized. The mp3 tagger
+       also needs this library for modifying (id3v1 and id3v2) tags.
 
        - XREFERENCE(http://www.xiph.org/downloads/, ogg vorbis).
        For ogg vorbis streams you'll need libogg, libvorbis,
@@ -297,35 +299,14 @@ Optional:
 
 Installation
 ~~~~~~~~~~~~
+To build the sources from a tarball, execute
 
-First make sure all non-optional packages listed in the section on
-REFERENCE(Requirements, required software) are installed on your
-system.
-
-You don't need everything listed there. In particular, MP3, OGG/Vorbis,
-OGG/Speex and AAC support are all optional. The configure script will
-detect what is installed on your system and will only try to build
-those executables that can be built with your setup.
-
-Note that no special decoder library (not even the MP3 decoding library
-libmad) is needed for para_server if you only want to stream MP3 or WMA
-files. Also, it's fine to use para_server on a box without sound card.
-
-Next, install the paraslash package on all machines, you'd like this
-software to run on. If you compile from a released tarball, execute
+       ./configure && make
 
-       (./configure && make) > /dev/null
-
-When compiling from git or from snapshots downloaded via gitweb,
-the above command will not work because the configure script is not
-included in the git repository. In this case the following command
-should be used instead:
+To build from git or a gitweb snapshot, run this command instead:
 
        ./autogen.sh
 
-This runs autoconf to generate the configure script, then runs it as
-above. Therefore you'll need autoconf for this to work.
-
 There should be no errors but probably some warnings about missing
 packages which usually implies that not all audio formats will be
 supported. If headers or libs are installed at unusual locations you
@@ -373,18 +354,11 @@ following commands:
 Next, change to the "bar" account on client_host and generate the
 key pair with the commands
 
-       ssh-keygen -t rsa -b 2048
-       # hit enter twice to create a key with no passphrase
+       ssh-keygen -q -t rsa -b 2048 -N '' -f $key
 
-This generates the two files id_rsa and id_rsa.pub in ~/.ssh. Note
-that paraslash can also read keys generated by the "openssl genrsa"
-command. However, since keys created with ssh-keygen can also be used
-for ssh, this method is recommended.
-
-Note that para_server refuses to use a key if it is shorter than 2048
-bits. In particular, the RSA keys of paraslash 0.3.x will not work
-with version 0.4.x. Moreover, para_client refuses to use a (private)
-key which is world-readable.
+This generates the two files id_rsa and id_rsa.pub in ~/.ssh.  Note
+that para_server won't accept keys shorter than 2048 bits. Moreover,
+para_client rejects private keys which are world-readable.
 
 para_server only needs to know the public key of the key pair just
 created. Copy this public key to server_host:
@@ -401,14 +375,6 @@ Finally, tell para_client to connect to server_host:
 
 *Step 2*: Start para_server
 
-Before starting the server make sure you have write permissions to
-the directory /var/paraslash that has been created during installation:
-
-       sudo chown $LOGNAME /var/paraslash
-
-Alternatively, use the --afs_socket Option to specify a different
-location for the AFS command socket.
-
 For this first try, we'll use the info loglevel to make the output
 of para_server more verbose.
 
@@ -453,19 +419,8 @@ You may print the list of all known audio files with
 
 *Step 4*: Configure para_audiod
 
-para_audiod needs to create a "well-known" socket for the clients to
-connect to. The default path for this socket is
-
-       /var/paraslash/audiod_socket.$HOSTNAME
-
-In order to make this directory writable for para_audiod, execute
-as bar@client_host
-
-       sudo chown $LOGNAME /var/paraslash
-
-
-We will also have to tell para_audiod that it should receive the
-audio stream from server_host via http:
+We will have to tell para_audiod that it should receive the audio
+stream from server_host via http:
 
        para_audiod -l info -r '.:http -i server_host'
 
@@ -482,9 +437,16 @@ streamed in which order.
 
 *Troubleshooting*
 
-It did not work? To find out why, try to receive, decode and play the
-stream manually using para_recv, para_filter and para_write as follows.
+If you receive a socket related error on server or audiod startup,
+make sure you have write permissions to the /var/paraslash directory:
+
+       sudo chown $LOGNAME /var/paraslash
+
+Alternatively, use the --afs-socket (para_server) or --socket
+(para_audiod) option to specify a different socket pathname.
 
+To identify streaming problems try to receive, decode and play the
+stream manually using para_recv, para_filter and para_write as follows.
 For simplicity we assume that you're running Linux/ALSA and that only
 MP3 files have been added to the database.
 
@@ -505,9 +467,9 @@ User management
 para_server uses a challenge-response mechanism to authenticate
 requests from incoming connections, similar to ssh's public key
 authentication method. Authenticated connections are encrypted using
-the RC4 stream cipher.
+a stream cipher, either RC4 or AES in integer counter mode.
 
-In this chapter we briefly describe RSA and RC4 and sketch the
+In this chapter we briefly describe RSA, RC4 and AES, and sketch the
 REFERENCE(Client-server authentication, authentication handshake)
 between para_client and para_server. User management is discussed
 in the section on REFERENCE(The user_list file, the user_list file).
@@ -517,8 +479,8 @@ in a REFERENCE(Connecting para_audiod, separate section).
 
 
 
-RSA and RC4
-~~~~~~~~~~~
+RSA, RC4, AES
+~~~~~~~~~~~~~
 
 RSA is an asymmetric block cipher which is used in many applications,
 including ssh and gpg. An RSA key consists in fact of two keys,
@@ -537,6 +499,15 @@ strong encryption by today's standards. Since the same key must never
 be used twice, a different, randomly-generated key is used for every
 new connection.
 
+AES, the advanced encryption standard, is a well-known symmetric block
+cipher, i.e. a transformation operating on fixed-length blocks which
+is determined by a single key for both encryption and decryption. Any
+block cipher can be turned into a stream cipher by generating
+a pseudo-random key stream by encrypting successive values of a
+counter. The AES_CTR128 stream cipher used in paraslash is obtained
+in this way from the AES block cipher with a 128 bit block size.
+
+
 Client-server authentication
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -557,7 +528,7 @@ as follows:
        fixed-length buffer with random bytes, encrypts that buffer
        using the public key and sends the encrypted buffer to the
        client. The first part of the buffer is the challenge which
-       is used for authentication while the second part is the RC4
+       is used for authentication while the second part is the
        session key.
 
        - para_client receives the encrypted buffer and decrypts it
@@ -574,12 +545,12 @@ as follows:
 
        - Otherwise the user is considered authenticated and the client
        is allowed to proceed by sending a command to be executed. From
-       this point on the communication is encrypted using the RC4
-       stream cipher with the session key known to both peers.
+       this point on the communication is encrypted using the stream
+       cipher with the session key known to both peers.
 
 paraslash relies on the quality of the pseudo-random bytes provided
 by the crypto library (openssl or libgcrypt), on the security of
-the implementation of the RSA and RC4 crypto routines and on the
+the implementation of the RSA, RC4 and AES crypto routines and on the
 infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their own. This
@@ -592,7 +563,7 @@ The user_list file
 
 At startup para_server reads the user list file which contains one
 line per user. The default location of the user list file may be
-changed with the --user_list option.
+changed with the --user-list option.
 
 There should be at least one user in this file. Each user must have
 an RSA key pair. The public part of the key is needed by para_server
@@ -639,7 +610,7 @@ restricted on platforms that support UNIX socket credentials which
 allow para_audiod to obtain the Unix credentials of the connecting
 process.
 
-Use para_audiod's --user_allow option to allow connections only for
+Use para_audiod's --user-allow option to allow connections only for
 a limited set of users.
 
 -----------------------
@@ -658,7 +629,7 @@ known audio files to those which satisfy certain criteria.  It also
 maintains tables containing images (e.g. album cover art) and lyrics
 that can be associated with one or more audio files.
 
-AFS uses XREFERENCE(http://systemlinux.org/~maan/osl/, libosl), the
+AFS uses XREFERENCE(http://people.tuebingen.mpg.de/maan/osl/, libosl), the
 object storage layer library, as the backend library for storing
 information on audio files, playlists, etc. This library offers
 functionality similar to a relational database, but is much more
@@ -781,7 +752,7 @@ is applied to all audio files matching this pattern:
 
 The command
 
-       para_client -- ls -lv
+       para_client -- ls -l=v
 
 gives you a verbose listing of your audio files also showing which
 attributes are set.
@@ -1071,17 +1042,18 @@ It is possible to change the behaviour of the add command by using the
 Troubleshooting
 ~~~~~~~~~~~~~~~
 
-Use the debug loglevel (option -l debug for most commands) to show
-debugging info. Almost all paraslash executables have a brief online
-help which is displayed by using the -h switch. The --detailed-help
-option prints the full help text.
+Use the debug loglevel (-l debug) to show debugging info. All paraslash
+executables have a brief online help which is displayed when -h is
+given. The --detailed-help option prints the full help text.
 
 If para_server crashed or was killed by SIGKILL (signal 9), it
 may refuse to start again because of "dirty osl tables". In this
 case you'll have to run the oslfsck program of libosl to fix your
-database. It might be necessary to use --force (even if your name
-isn't Luke). However, make sure para_server isn't running before
-executing oslfsck --force.
+database:
+
+       oslfsck -fd ~/.paraslash/afs_database-0.4
+
+However, make sure para_server isn't running before executing oslfsck.
 
 If you don't mind to recreate your database you can start
 from scratch by removing the entire database directory, i.e.
@@ -1099,6 +1071,14 @@ care about the table contents. To check for invalid table contents, use
 This prints out references to missing audio files as well as invalid
 playlists and mood definitions.
 
+Similarly, para_audiod refuses to start if its socket file exists, since
+this indicates that another instance of para_audiod is running. After
+a crash a stale socket file might remain and you must run
+
+       para_audiod --force
+
+once to fix it up.
+
 ---------------------------------------
 Audio formats and audio format handlers
 ---------------------------------------
@@ -1135,7 +1115,17 @@ Excited Linear Prediction) coding. It is designed for voice
 over IP applications, has modest complexity and a small memory
 footprint. Wideband and narrowband (telephone quality) speech are
 supported. As for Vorbis audio, Speex bit-streams are often stored
-in OGG files.
+in OGG files. As of 2012 this codec is considered obsolete since the
+Oppus codec, described below, surpasses its performance in all areas.
+
+*OGG/Opus*
+
+Opus is a lossy audio compression format standardized through RFC
+6716 in 2012. It combines the speech-oriented SILK codec and the
+low-latency CELT (Constrained Energy Lapped Transform) codec. Like
+OGG/Vorbis and OGG/Speex, Opus data is usually encapsulated in OGG
+containers. All known software patents which cover Opus are licensed
+under royalty-free terms.
 
 *AAC*
 
@@ -1159,7 +1149,7 @@ is composed of superframes, each containing one or more frames of
 The Free Lossless Audio Codec (FLAC) compresses audio without quality
 loss. It gives better compression ratios than a general purpose
 compressor like zip or bzip2 because FLAC is designed specifically
-for audio. A FLAC-encoded file consits of frames of varying size, up
+for audio. A FLAC-encoded file consists of frames of varying size, up
 to 16K. Each frame starts with a header that contains all information
 necessary to decode the frame.
 
@@ -1217,10 +1207,9 @@ chunk table and reads the meta data.
 
 The audio format handler code is linked into para_server and executed
 via the _add_ command. The same code is also available as a stand-alone
-tool, para_afh, which can be used to print the technical data, the
-chunk table and the meta data of a file. Furthermore, one can use
-para_afh to cut an audio file, i.e. to select some of its chunks to
-produce a new file containing only these chunks.
+tool, para_afh, which prints the technical data, the chunk table
+and the meta data of a file. Moreover, all audio format handlers are
+combined in the afh receiver which is part of para_recv and para_play.
 
 ----------
 Networking
@@ -1301,10 +1290,10 @@ to the database, so they need to transfer data the other way round,
 from the client to the server.
 
 There is no knowledge about the server commands built into para_client,
-so it does not know about addblob commands. Instead, it inspects the
-first data package sent by the server for a magic string. If this
-string was found, it sends STDIN to the server, otherwise it dumps
-data from the server to STDOUT.
+so it does not know about addblob commands. Instead, the server sends
+a special "awaiting data" packet for these commands. If the client
+receives this packet, it sends STDIN to the server, otherwise it
+dumps data from the server to STDOUT.
 
 Streaming protocols
 ~~~~~~~~~~~~~~~~~~~
@@ -1424,15 +1413,21 @@ the stream.
 Examples
 ~~~~~~~~
 
-The sender command of para_server allows to (de-)activate senders
-and to change the access permissions senders at runtime. The "si"
-(server info) command is used to list the streaming options of the
-currently running server as well as the various sender access lists.
+The "si" (server info) command lists some information about the
+currently running server process.
 
--> Show client/target/access lists:
+-> Show PIDs, number of connected clients, uptime, and more:
 
        para_client si
 
+The sender command of para_server prints information about senders,
+like the various access control lists, and it allows to (de-)activate
+senders and to change the access permissions at runtime.
+
+-> List all senders
+
+       para_client sender
+
 -> Obtain general help for the sender command:
 
        para_client help sender
@@ -1442,6 +1437,10 @@ currently running server as well as the various sender access lists.
        s=http # or dccp or udp
        para_client sender $s help
 
+-> Show status of the http sender
+
+       para_client sender http status
+
 By default para_server activates both the HTTP and th DCCP sender on
 startup. This can be changed via command line options or para_server's
 config file.
@@ -1506,10 +1505,10 @@ the output to STDOUT, the filter modules of para_audiod are always
 connected to a receiver which produces the input stream and a writer
 which absorbs the output stream.
 
-Some filters depend on a specific library being installed and are
-not compiled in if this library was not found at compile time. To
-see the list of supported filters, run para_filter and para_audiod
-with the --help option. The output looks similar to the following:
+Some filters depend on a specific library and are not compiled in
+if this library was not found at compile time. To see the list of
+supported filters, run para_filter and para_audiod with the --help
+option. The output looks similar to the following:
 
        Available filters:
                compress wav amp fecdec wmadec prebuffer oggdec aacdec mp3dec
@@ -1553,7 +1552,7 @@ can be directly sent to the sound device or any other software that
 operates on undecoded PCM data (visualizers, equalizers etc.). Such
 filters are called _decoders_ in general, and xxxdec is the name of
 the paraslash decoder for the audio format xxx. For example, the mp3
-decoder filter is called mp3dec.
+decoder is called mp3dec.
 
 Note that the output of the decoder is about 10 times larger than
 its input. This means that filters that operate on the decoded audio
@@ -1562,8 +1561,7 @@ the audio stream before it is fed to the decoder.
 
 Paraslash relies on external libraries for most decoders, so these
 libraries must be installed for the decoder to be included in the
-para_filter and para_audiod executables. The oggdec filter depends
-on the libogg and libvorbis libraries for example.
+executables. For example, the mp3dec filter depends on the mad library.
 
 Forward error correction
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1770,7 +1768,7 @@ a curses window. By default the command
 
        para_audioc -- stat -p
 
-is executed, but this can be customized via the --stat_cmd option. In
+is executed, but this can be customized via the --stat-cmd option. In
 particular it possible to use
 
        para_client -- stat -p
@@ -2012,21 +2010,19 @@ Doxygen
 ~~~~~~~
 
 Doxygen is a documentation system for various programming
-languages. The paraslash project uses Doxygen for generating the API
-reference on the web pages, but good source code documentation is
-also beneficial to people trying to understand the code structure
-and the interactions between the various source files.
+languages. The API reference on the paraslash web page is generated
+by doxygen.
 
 It is more illustrative to look at the source code for examples than
-to describe the conventions for documenting the source in this manual,
-so we only describe which parts of the code need doxygen comments,
-but leave out details on documentation conventions.
+to describe the conventions in this manual, so we only describe which
+parts of the code need doxygen comments, but leave out details on
+documentation conventions.
 
 As a rule, only the public part of the C source is documented with
 Doxygen. This includes structures, defines and enumerations in header
 files as well as public (non-static) C functions.  These should be
-documented completely. For example each parameter and the return
-value of a public function should get a descriptive comment.
+documented completely. For example, each parameter and the return
+value of a public function should get a descriptive doxygen comment.
 
 No doxygen comments are necessary for static functions and for
 structures and enumerations in C files (which are used only within
@@ -2135,6 +2131,35 @@ maintain state for each listening receiver, multicast often implies
 connectionless transport, which is the reason that it is currently
 only available via UDP.
 
+Abstract socket namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~
+UNIX domain sockets are a traditional way to communicate between
+processes on the same machine. They are always reliable (see above)
+and don't reorder datagrams. Unlike TCP and UDP, UNIX domain sockets
+support passing open file descriptors or process credentials to
+other processes.
+
+The usual way to set up a UNIX domain socket (as obtained from
+socket(2)) for listening is to first bind the socket to a file system
+pathname and then call listen(2), then accept(2). Such sockets are
+called _pathname sockets_ because bind(2) creates a special socket
+file at the specified path. Pathname sockets allow unrelated processes
+to communicate with the listening process by binding to the same path
+and calling connect(2).
+
+There are two problems with pathname sockets:
+
+       * The listing process must be able to (safely) create the
+       socket special in a directory which is also accessible to
+       the connecting process.
+
+       * After an unclean shutdown of the listening process, a stale
+       socket special may reside on the file system.
+
+The abstract socket namespace is a non-portable Linux feature which
+avoids these problems. Abstract sockets are still bound to a name,
+but the name has no connection with file system pathnames.
+
 License
 ~~~~~~~
 
@@ -2181,11 +2206,14 @@ RFCs
        Congestion Control ID 2: TCP-like Congestion Control
        - XREFERENCE(http://www.ietf.org/rfc/rfc4342.txt, RFC 4342) (2006):
        Congestion Control ID 3: TCP-Friendly Rate Control (TFRC)
+       - XREFERENCE(http://www.ietf.org/rfc/rfc6716.txt, RFC 6716) (2012):
+       Definition of the Opus Audio Codec
 
 Application web pages
 ~~~~~~~~~~~~~~~~~~~~~
 
-       - XREFERENCE(http://paraslash.systemlinux.org/, paraslash)
+       - XREFERENCE(http://people.tuebingen.mpg.de/maan/paraslash/, paraslash)
+       - XREFERENCE(http://paraslash.systemlinux.org/, paraslash (alternative page))
        - XREFERENCE(http://xmms2.org/wiki/Main_Page, xmms)
        - XREFERENCE(http://www.mpg123.de/, mpg123)
        - XREFERENCE(http://gstreamer.freedesktop.org/, gstreamer)
index f103abd349068a03c8b84d0aa3c17ddbc7b9f4ac..e8c43489f4681bcbd8bb859db80582b5fa83c068 100644 (file)
@@ -1,29 +1,50 @@
-BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
+body,h1,h2,h3,h4,h5,h6,p,center,td,th,ul,dl,div {
        font-family: sans-serif;
-       font-size: small;
 }
 
-/*
-BODY,TD {
-       font-size: 90%;
+body {
+       background-color: black;
+       color: #bbbbbb;
+       margin: 0px;
 }
-H1 {
-       text-align: center;
-       font-size: 160%;
+
+table {
+       padding: 8px 4px;
 }
-H2 {
-       font-size: 120%;
+
+th {
+       padding: 2px 5px;
+       font-size: 100%;
+       text-align: left;
 }
-H3 {
+
+td {
+       padding: 2px 5px;
        font-size: 100%;
+       vertical-align: top;
+}
+
+a {
+       color: #cc3322;
+}
+
+hr {
+       height: 1px;
+       border: none;
+       border-top: 1px solid yellow;
+}
+
+img {
+       float: right;
+       border-width: 0px;
 }
-*/
-CAPTION { font-weight: bold }
+
+caption { font-weight: bold }
 
 /* doxgen */
 
 /* Data structure index. Box with clickable letters */
-DIV.qindex {
+div.qindex {
        width: 100%;
        background-color: #000055;
        border: 1px solid #ffff00;
@@ -32,78 +53,61 @@ DIV.qindex {
        padding: 2px;
        line-height: 140%;
 }
-/*
-DIV.nav {
-       width: 100%;
-       background-color: #48ee02;
-       border: 1px solid #ffff00;
-       text-align: center;
-       margin: 2px;
-       padding: 2px;
-       line-height: 140%;
-}
-DIV.navtab {
-       background-color: #48ee02;
-       border: 1px solid #ffff00;
-       text-align: center;
-       margin: 2px;
-       margin-right: 15px;
-       padding: 2px;
-}
-TD.navtab {
-       font-size: 70%;
-}
-*/
 
 /* Data structure index, clickable letters */
-A.qindex {
+a.qindex {
        text-decoration: none;
        font-weight: bold;
        color: #cc3322;
 }
-A.qindex:visited {
+
+a.qindex:visited {
        text-decoration: none;
        font-weight: bold;
        color: #cc3322
 }
-A.qindex:hover {
+
+a.qindex:hover {
        text-decoration: none;
        background-color: #ffff00;
 }
 
-/*
-A.qindexHL {
+a.el {
        text-decoration: none;
-       font-weight: bold;
-       background-color: #cccc00;
-       color: #000000;
-       border: 1px double #929502;
+       font-weight: bold
 }
-A.qindexHL:hover {
+
+a.elRef { font-weight: bold }
+
+a.code:link {
        text-decoration: none;
-       background-color: #ffff00;
-       color: #000000;
+       font-weight: normal;
+       color: #BA3708
 }
-A.qindexHL:visited {
+
+a.code:visited {
        text-decoration: none;
-       background-color: #e6c60c;
-       color: #000000
-}
-*/
-
-A.el { text-decoration: none; font-weight: bold }
-A.elRef { font-weight: bold }
-A.code:link { text-decoration: none; font-weight: normal; color: #BA3708}
-A.code:visited { text-decoration: none; font-weight: normal; color: #BA3708}
-A.codeRef:link { font-weight: normal; color: #BA3708}
-A.codeRef:visited { font-weight: normal; color: #BA3708}
-A:hover { text-decoration: none; background-color: #ffff00 }
-DL.el { margin-left: -1cm }
+       font-weight: normal;
+       color: #BA3708
+}
+
+a.codeRef:link {
+       font-weight: normal;
+       color: #BA3708
+}
+
+a.codeRef:visited {
+       font-weight: normal;
+       color: #BA3708
+}
+
+dl.el { margin-left: -1cm }
 .fragment {
        font-family: Fixed, monospace;
        font-size: 95%;
 }
-PRE.fragment {
+
+pre.fragment {
        border: 1px solid #CCCCCC;
        background-color: #351505;
        margin-top: 4px;
@@ -117,49 +121,29 @@ PRE.fragment {
 }
 
 /* data structures non-clickable letters */
-DIV.ah {
+div.ah {
        background-color: black;
        font-weight: bold;
        color: #cccccc;
        margin-bottom: 3px;
        margin-top: 3px
 }
-/*
-TD.md { background-color: #ff0000; font-weight: bold; }
 
-TD.mdPrefix {
-       background-color: #ffff00;
-       color: #606060;
-       font-size: 80%;
-}
-TD.mdname1 {
-       background-color: #000055;
-       font-weight: bold;
-       color: #ff0000;
-}
-TD.mdname {
-       background-color: #000055;
-       font-weight: bold;
-       color: #00CC00;
-       width: 600px;
-}
-*/
-DIV.groupHeader {
+div.groupHeader {
        margin-left: 16px;
        margin-top: 12px;
        margin-bottom: 6px;
        font-weight: bold;
 }
-DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% }
-BODY {
-       background: black;
-       color: #bbbbbb;
-       margin-right: 20px;
-       margin-left: 20px;
+
+div.groupText {
+       margin-left: 16px;
+       font-style: italic;
+       font-size: 90%
 }
 
 /* File list */
-TD.indexkey {
+td.indexkey {
        background-color: #000055;
        font-weight: bold;
        padding-right  : 10px;
@@ -173,7 +157,7 @@ TD.indexkey {
        border: 1px solid #ffff00;
 }
 
-TD.indexvalue {
+td.indexvalue {
        background-color: #000055;
        font-style: italic;
        padding-right  : 10px;
@@ -187,22 +171,12 @@ TD.indexvalue {
        border: 1px solid #ffff00;
 }
 
-TR.memlist {
-   background-color: #112244; 
+tr.memlist {
+   background-color: #112244;
 }
-P.formulaDsp { text-align: center; }
-IMG.formulaDsp { }
-IMG.formulaInl { vertical-align: middle; }
-/*
-SPAN.keyword       { color: #ffff00 }
-SPAN.keywordtype   { color: #ffff00 }
-SPAN.keywordflow   { color: #108000 }
-SPAN.comment       { color: #00CCCC }
-SPAN.preprocessor  { color: #CC00CC }
-SPAN.stringliteral { color: #e0e020 }
-SPAN.charliteral   { color: #0000ff }
-*/
-
+p.formulaDsp { text-align: center; }
+img.formulaDsp { }
+img.formulaInl { vertical-align: middle; }
 
 /* Overwiew on top of page for *.c file */
 
@@ -210,12 +184,14 @@ SPAN.charliteral   { color: #0000ff }
        padding: 0px 8px 4px 8px;
         margin: 0px;
 }
+
 .mdescRight { /* Short description */
        padding: 0px 8px 4px 8px;
         font-size: 100%;
         font-style: italic;
         margin: 0px;
 }
+
 .memItemLeft { /* return value */
        color: #ffffff;
         border-top-color: #ffff00;
@@ -228,6 +204,7 @@ SPAN.charliteral   { color: #0000ff }
         border-left-style: none;
         font-size: 100%;
 }
+
 .memItemRight { /* function declaration */
        color: #ffffff;
         border-top-color: #ffff00;
@@ -241,154 +218,228 @@ SPAN.charliteral   { color: #0000ff }
         font-size: 100%;
 }
 
-/*
-.memTemplItemLeft {
-       padding: 1px 0px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E000;
-       border-right-color: #E0E000;
-       border-bottom-color: #E0E000;
-       border-left-color: #E0E000;
-       border-top-style: none;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #000000;
-       font-size: 80%;
-}
-.memTemplItemRight {
-       padding: 1px 8px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E000;
-       border-right-color: #E0E000;
-       border-bottom-color: #E0E000;
-       border-left-color: #E0E000;
-       border-top-style: none;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #000000;
-       font-size: 80%;
-}
-.memTemplParams {
-       padding: 1px 0px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E000;
-       border-right-color: #E0E000;
-       border-bottom-color: #E0E000;
-       border-left-color: #E0E000;
-       border-top-style: solid;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       color: #606060;
-       background-color: #3A3A1A;
-       font-size: 80%;
-}
-*/
-.search     { color: #f03309;
-              font-weight: bold;
-}
-FORM.search {
-              margin-bottom: 0px;
-              margin-top: 0px;
-}
-INPUT.search { font-size: 75%;
-               color: #800000;
-               font-weight: normal;
-               background-color: #000000;
-}
-TD.tiny      {
-       font-size: 75%;
+.search {
+       color: #f03309;
+       font-weight: bold;
 }
 
-/* links */
-a {
-       color: #cc3322;
-/*     color: #BA4108; */
+form.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
 }
 
-a:visited {
-/*     color: #BA3708; */
-       color: #cc3322;
+input.search {
+       font-size: 75%;
+       color: #800000;
+       font-weight: normal;
+       background-color: #000000;
 }
+
+td.tiny {
+       font-size: 75%;
+}
+
 .dirtab { padding: 4px;
           border-collapse: collapse;
           border: 1px solid #84b007;
 }
-TH.dirtab { background: #080e02;
-            font-weight: bold;
-}
-HR { height: 1px;
-     border: none;
-     border-top: 1px solid yellow;
+
+th.dirtab {
+       background: #080e02;
+       font-weight: bold;
 }
 
 /* Style for detailed member documentation */
 .memtemplate {
-  font-size: 100%;
-  color: #cccccc;
-  font-weight: normal;
-}
-/*
-.memnav {
-  border: 5px solid #845047;
-  text-align: center;
-  margin: 2px;
-  margin-right: 15px;
-  padding: 2px;
-}
-*/
+       font-size: 100%;
+       color: #cccccc;
+       font-weight: normal;
+}
+
 .memitem { /* outer border around function */
-  padding: 6px;
-  background-color: #000000;
-  border-width: 2px;
-  border-style: solid;
-  border-color: #ffff00;
-/*  -moz-border-radius: 8px 8px 8px 8px; */
+       padding: 6px;
+       background-color: #000000;
+       border-width: 2px;
+       border-style: solid;
+       border-color: #ffff00;
 }
+
 .memname {
-  color: #ffffff; /* name of the function */
-  white-space: nowrap;
-  font-weight: bold;
+       color: #ffffff; /* name of the function */
+       white-space: nowrap;
+       font-weight: bold;
 }
+
 .memdoc{
-  padding-left: 10px;
+       padding-left: 10px;
 }
 
 /* inner box containing function definition */
 .memproto {
-  background-color: #000055;
-  width: 100%;
-  border-width: 2px;
-  border-style: solid;
-  border-color: #ffff00;
-  font-weight: bold;
-/*  -moz-border-radius: 8px 8px 8px 8px;*/
+       background-color: #000055;
+       width: 100%;
+       border-width: 2px;
+       border-style: solid;
+       border-color: #ffff00;
+       font-weight: bold;
 }
 
 .paramkey {
-  color: #ff0000;
-  text-align: right;
+       color: #ff0000;
+       text-align: right;
 }
+
 .paramtype { /* type of function parameter, e.g. "int" */
-  color: #00ff00;
-  white-space: nowrap;
+       color: #00ff00;
+       white-space: nowrap;
 }
+
 .paramname { /* name of function parameter */
-  color: #ffff00;
-  font-style: italic;
+       color: #ffff00;
+       font-style: italic;
+}
+
+span.cntrl {
+       border: dashed #aaaaaa;
+       border-width: 1px;
+       padding: 0px 2px 0px 2px;
+       margin:  0px 2px 0px 2px;
+}
+
+div.page_header {
+       padding: 8px;
+       font-size: 120%;
+       font-weight: bold;
+       background-color: #151515;
+}
+
+div.page_header a:visited, a.header {
+       color: #cc3322;
+}
+
+div.page_header a:hover {
+       color: #880000;
+}
+
+div.page_nav {
+       padding: 8px;
+}
+
+div.page_nav a:visited {
+       color: #cc3322;
+}
+
+div.page_path {
+       padding: 8px;
+       font-weight: bold;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+}
+
+div.page_footer {
+       height: 17px;
+       padding: 4px 8px;
+       background-color: #d9d8d1;
+}
+
+div.page_footer_text {
+       float: left;
+       color: #555555;
+       font-style: italic;
+}
+
+div.page_body {
+       padding: 8px;
+       font-family: monospace;
+       font-size: 120%;
+}
+
+a.title:hover {
+       background-color: #AA3100;
+}
+
+div.title_text {
+       padding: 6px 0px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px;
+       font-family: monospace;
+}
+
+div.log_body {
+       padding: 8px 8px 8px 150px;
+       font-family: monospace;
+       font-size: 120%;
+}
+
+div.log {
+       font-family: monospace;
+       font-size: 120%;
+}
+
+span.age {
+       position: relative;
+       float: left;
+       width: 142px;
+       font-style: italic;
 }
 
+span.signoff {
+       color: #888888;
+}
+
+div.log_link {
+       padding: 0px 8px;
+       font-size: 70%;
+       font-family: sans-serif;
+       font-style: normal;
+       position: relative;
+       float: left;
+       width: 136px;
+}
+
+div.list_head {
+       padding: 6px 8px 4px;
+       border: solid #d9d8d1;
+       border-width: 1px 0px 0px;
+       font-style: italic;
+}
+
+div.author_date {
+       padding: 8px;
+       border: solid #d9d8d1;
+       border-width: 0px 0px 1px 0px;
+       font-style: italic;
+}
+
+a.list {
+       text-decoration: none;
+       color: #cc3322;
+}
+
+a.subject, a.name {
+       font-weight: bold;
+}
+
+table.tags a.subject {
+       font-weight: normal;
+}
+
+a.list:hover {
+       text-decoration: underline;
+       color: #880000;
+}
+
+a.text {
+       text-decoration: none;
+       color: #0000cc;
+}
+
+a.text:visited {
+       text-decoration: none;
+       color: #880000;
+}
+
+a.text:hover {
+       text-decoration: underline;
+       color: #880000;
+}
diff --git a/web/screenshots.in.html b/web/screenshots.in.html
deleted file mode 100644 (file)
index 04dac51..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<h1>Screenshots</h1>
-<hr>
-
-<p> The main part of paraslash is a client-server application,
-so it doesn't have much to take a screenshot of. However, here's a
-screenshot of
-       <a href="gui.png">para_gui</a>,
-and a text file containing the startup part of
-       <a href="server.log">para_server's logfile</a>
-and a similar thing for the logfile of
-       <a href="audiod.log">para_audiod</a>.
-Both log files show log output at maximal verbosity.
-</p>
diff --git a/web/screenshots/audiod.log b/web/screenshots/audiod.log
deleted file mode 100644 (file)
index c9e4366..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-Dec 08 19:23:27 meins 2 log_welcome: welcome to para_audiod git (Sat Dec  8 13:42:52 MET 2007)
-Dec 08 19:23:27 meins 2 init_writers: maximal number of writers: 3
-Dec 08 19:23:27 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:23:27 meins 2 init_writers: mp3 writer #0: alsa
-Dec 08 19:23:27 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:23:27 meins 2 init_writers: ogg writer #0: alsa
-Dec 08 19:23:27 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:23:27 meins 2 init_writers: aac writer #0: alsa
-Dec 08 19:23:27 meins 2 init_receivers: initializing http receiver
-Dec 08 19:23:27 meins 2 init_receivers: initializing dccp receiver
-Dec 08 19:23:27 meins 2 init_receivers: initializing ortp receiver
-Dec 08 19:23:27 meins 2 init_filters: maximal number of filters: 6
-Dec 08 19:23:27 meins 2 add_filter: mp3 filter 1: mp3dec
-Dec 08 19:23:27 meins 2 add_filter: mp3 filter 2: compress
-Dec 08 19:23:27 meins 2 add_filter: ogg filter 1: oggdec
-Dec 08 19:23:27 meins 2 add_filter: ogg filter 2: compress
-Dec 08 19:23:27 meins 2 add_filter: aac filter 1: aacdec
-Dec 08 19:23:27 meins 2 add_filter: aac filter 2: compress
-Dec 08 19:23:27 meins 2 clear_slot: clearing slot 0
-Dec 08 19:23:27 meins 2 clear_slot: clearing slot 1
-Dec 08 19:23:27 meins 2 clear_slot: clearing slot 2
-Dec 08 19:23:27 meins 2 clear_slot: clearing slot 3
-Dec 08 19:23:27 meins 2 clear_slot: clearing slot 4
-Dec 08 19:23:27 meins 2 init_grabbing: grab init
-Dec 08 19:23:27 meins 2 setup_signal_handling: signal pipe: fd 4
-Dec 08 19:23:27 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins
-Dec 08 19:23:27 meins 2 daemon_init: daemonizing
-Dec 08 19:23:27 meins 2 init_sched: initializing scheduler
-Dec 08 19:23:27 meins 2 register_task: registering signal task (0x80631bc)
-Dec 08 19:23:27 meins 2 register_task: registering command task (0xbfec3ec4)
-Dec 08 19:23:27 meins 2 register_task: registering status task (0x80632e8)
-Dec 08 19:23:27 meins 2 register_task: registering audiod task (0xbfec3fe8)
-Dec 08 19:23:27 meins 2 status_pre_select: clock diff count: 5
-Dec 08 19:23:27 meins 2 client_open: loglevel: 5
-Dec 08 19:23:27 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:27 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:27 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:27 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:27 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:27 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:27 meins 2 client_post_select: --> 1499570993
-Dec 08 19:23:27 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:27 meins 2 enable_crypt: rc4 encryption activated for fd 7
-Dec 08 19:23:27 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:28 meins 2 handle_connect: connection from user 409, buf: stat
-Dec 08 19:23:28 meins 2 handle_connect: argv[0]: stat, argc= 1
-Dec 08 19:23:28 meins 2 com_stat: mask: 0xffffffff
-Dec 08 19:23:28 meins 2 stat_client_add: adding client on fd 7
-Dec 08 19:23:28 meins 2 dump_stat_client_list: stat client on fd 7
-Dec 08 19:23:28 meins 2 status_pre_select: clock diff count: 4
-Dec 08 19:23:28 meins 2 client_open: loglevel: 5
-Dec 08 19:23:28 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:28 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:28 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:28 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:28 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:28 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:28 meins 2 client_post_select: --> 1400991892
-Dec 08 19:23:28 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:28 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:28 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms
-Dec 08 19:23:28 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:29 meins 2 status_pre_select: clock diff count: 3
-Dec 08 19:23:29 meins 2 client_open: loglevel: 5
-Dec 08 19:23:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:29 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:29 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:29 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:29 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:29 meins 2 client_post_select: --> 1772412478
-Dec 08 19:23:29 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:29 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:29 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms
-Dec 08 19:23:29 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:30 meins 2 status_pre_select: clock diff count: 2
-Dec 08 19:23:30 meins 2 client_open: loglevel: 5
-Dec 08 19:23:30 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:30 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:30 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:30 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:30 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:30 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:30 meins 2 client_post_select: --> 1882631692
-Dec 08 19:23:30 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:30 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:30 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms
-Dec 08 19:23:30 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:31 meins 2 status_pre_select: clock diff count: 1
-Dec 08 19:23:31 meins 2 client_open: loglevel: 5
-Dec 08 19:23:31 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:31 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:31 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:31 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:31 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:31 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:31 meins 2 client_post_select: --> 1630455651
-Dec 08 19:23:31 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:31 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:31 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms
-Dec 08 19:23:31 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:36 meins 2 client_open: loglevel: 5
-Dec 08 19:23:36 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:36 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:36 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:36 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:36 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:36 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:36 meins 2 client_post_select: --> 759215314
-Dec 08 19:23:36 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:36 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:36 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:23:36 meins 2 clear_slot: clearing slot 0
-Dec 08 19:23:36 meins 3 open_receiver: started ogg: dccp receiver in slot 0
-Dec 08 19:23:36 meins 2 register_task: registering dccp receiver node (0x806a324)
-Dec 08 19:23:37 meins 2 open_filters: opening ogg filters
-Dec 08 19:23:37 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0
-Dec 08 19:23:37 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0
-Dec 08 19:23:37 meins 2 register_task: registering filter chain (0x807460c)
-Dec 08 19:23:37 meins 3 ogg_convert: input buffer: 17032, opening ov callbacks
-Dec 08 19:23:37 meins 3 ogg_convert: 2 channels, 44100 Hz
-Dec 08 19:23:37 meins 2 open_writers: opening ogg writers
-Dec 08 19:23:37 meins 2 open_writers: samplerate: 44100
-Dec 08 19:23:37 meins 3 wng_open: opening wng 0x80a5660 with 1 writer(s)
-Dec 08 19:23:37 meins 2 register_task: registering  (0x80a5684)
-Dec 08 19:23:37 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:23:37 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:23:37 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:23:43 meins 3 rn_event_handler: dccp_recv: end of file
-Dec 08 19:23:43 meins 2 unregister_task: unregistering dccp receiver node (0x806a324)
-Dec 08 19:23:43 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering writer node group in slot 0
-Dec 08 19:23:43 meins 2 unregister_task: unregistering writer node group (0x80a5684)
-Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering filter chain in slot 0
-Dec 08 19:23:43 meins 2 unregister_task: unregistering filter chain (0x807460c)
-Dec 08 19:23:43 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:23:43 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:23:43 meins 2 alsa_close: closing writer node 0x80a57b0
-Dec 08 19:23:43 meins 3 close_filters: closing filter chain 0x80745e0
-Dec 08 19:23:43 meins 2 close_filters: closing oggdec filter
-Dec 08 19:23:43 meins 2 close_filters: closing compress filter
-Dec 08 19:23:43 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1)
-Dec 08 19:23:43 meins 2 clear_slot: clearing slot 0
-Dec 08 19:23:48 meins 2 client_open: loglevel: 5
-Dec 08 19:23:48 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:23:48 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:23:48 meins 3 client_open: connecting localhost:2990
-Dec 08 19:23:48 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:23:48 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:23:48 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:23:48 meins 2 client_post_select: --> 960122352
-Dec 08 19:23:48 meins 2 client_post_select: decrypting session key
-Dec 08 19:23:48 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:23:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:23:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:23:58 meins 2 clear_slot: clearing slot 0
-Dec 08 19:23:58 meins 3 open_receiver: started mp3: http receiver in slot 0
-Dec 08 19:23:58 meins 2 register_task: registering http receiver node (0x806a204)
-Dec 08 19:23:58 meins 2 http_recv_post_select: sending http request
-Dec 08 19:23:58 meins 2 http_recv_post_select: received ok msg, streaming
-Dec 08 19:23:59 meins 2 open_filters: opening mp3 filters
-Dec 08 19:23:59 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0
-Dec 08 19:23:59 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0
-Dec 08 19:23:59 meins 2 register_task: registering filter chain (0x807260c)
-Dec 08 19:23:59 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 173 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 147 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 121 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 95 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 69 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 43 ms left
-Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 17 ms left
-Dec 08 19:23:59 meins 2 open_writers: opening mp3 writers
-Dec 08 19:23:59 meins 2 open_writers: samplerate: 44100
-Dec 08 19:23:59 meins 3 wng_open: opening wng 0x80a3c18 with 1 writer(s)
-Dec 08 19:23:59 meins 2 register_task: registering  (0x80a3c3c)
-Dec 08 19:23:59 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:23:59 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:23:59 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:24:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN
-Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN
-Dec 08 19:25:33 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:25:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:25:51 meins 3 rn_event_handler: http_recv: end of file
-Dec 08 19:25:51 meins 2 unregister_task: unregistering http receiver node (0x806a204)
-Dec 08 19:25:51 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering writer node group in slot 0
-Dec 08 19:25:51 meins 2 unregister_task: unregistering writer node group (0x80a3c3c)
-Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering filter chain in slot 0
-Dec 08 19:25:51 meins 2 unregister_task: unregistering filter chain (0x807260c)
-Dec 08 19:25:51 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:25:51 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:25:51 meins 2 alsa_close: closing writer node 0x806a440
-Dec 08 19:25:51 meins 3 close_filters: closing filter chain 0x80725e0
-Dec 08 19:25:51 meins 2 close_filters: closing mp3dec filter
-Dec 08 19:25:51 meins 2 close_filters: closing compress filter
-Dec 08 19:25:51 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1)
-Dec 08 19:25:51 meins 2 clear_slot: clearing slot 0
-Dec 08 19:25:56 meins 2 client_open: loglevel: 5
-Dec 08 19:25:56 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:25:56 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:25:56 meins 3 client_open: connecting localhost:2990
-Dec 08 19:25:56 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:25:56 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:25:56 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:25:56 meins 2 client_post_select: --> 563263924
-Dec 08 19:25:56 meins 2 client_post_select: decrypting session key
-Dec 08 19:25:56 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:25:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:26:06 meins 2 clear_slot: clearing slot 0
-Dec 08 19:26:06 meins 3 open_receiver: started mp3: http receiver in slot 0
-Dec 08 19:26:06 meins 2 register_task: registering http receiver node (0x806a29c)
-Dec 08 19:26:06 meins 2 http_recv_post_select: sending http request
-Dec 08 19:26:06 meins 2 http_recv_post_select: received ok msg, streaming
-Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:26:06 meins 2 open_filters: opening mp3 filters
-Dec 08 19:26:06 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0
-Dec 08 19:26:06 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0
-Dec 08 19:26:06 meins 2 register_task: registering filter chain (0x80726dc)
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 173 ms left
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 147 ms left
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 121 ms left
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 95 ms left
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 69 ms left
-Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 43 ms left
-Dec 08 19:26:07 meins 2 audiod_pre_select: initial delay: 17 ms left
-Dec 08 19:26:07 meins 2 open_writers: opening mp3 writers
-Dec 08 19:26:07 meins 2 open_writers: samplerate: 44100
-Dec 08 19:26:07 meins 3 wng_open: opening wng 0x80a3cb8 with 1 writer(s)
-Dec 08 19:26:07 meins 2 register_task: registering  (0x80a3cdc)
-Dec 08 19:26:07 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:26:07 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:26:07 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:26:51 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:26:58 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0
-Dec 08 19:26:58 meins 2 unregister_task: unregistering writer node group (0x80a3cdc)
-Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0
-Dec 08 19:26:58 meins 2 unregister_task: unregistering filter chain (0x80726dc)
-Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering receiver_node in slot 0
-Dec 08 19:26:58 meins 2 unregister_task: unregistering http receiver node (0x806a29c)
-Dec 08 19:26:58 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:26:58 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:26:58 meins 2 alsa_close: closing writer node 0x80a3e08
-Dec 08 19:26:58 meins 3 close_filters: closing filter chain 0x80726b0
-Dec 08 19:26:58 meins 2 close_filters: closing mp3dec filter
-Dec 08 19:26:58 meins 2 close_filters: closing compress filter
-Dec 08 19:26:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1)
-Dec 08 19:26:58 meins 2 clear_slot: clearing slot 0
-Dec 08 19:27:04 meins 2 client_open: loglevel: 5
-Dec 08 19:27:04 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:04 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:04 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:04 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:04 meins 5 client_open: Connection refused
-Dec 08 19:27:09 meins 2 client_open: loglevel: 5
-Dec 08 19:27:09 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:09 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:09 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:09 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:09 meins 5 client_open: Connection refused
-Dec 08 19:27:14 meins 2 client_open: loglevel: 5
-Dec 08 19:27:14 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:14 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:14 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:14 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:14 meins 5 client_open: Connection refused
-Dec 08 19:27:19 meins 2 client_open: loglevel: 5
-Dec 08 19:27:19 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:19 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:19 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:19 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:19 meins 5 client_open: Connection refused
-Dec 08 19:27:24 meins 2 client_open: loglevel: 5
-Dec 08 19:27:24 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:24 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:24 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:24 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:24 meins 5 client_open: Connection refused
-Dec 08 19:27:29 meins 2 client_open: loglevel: 5
-Dec 08 19:27:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:29 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:29 meins 5 makesock: can not create TCP socket localhost#2990.
-Dec 08 19:27:29 meins 5 client_open: Connection refused
-Dec 08 19:27:34 meins 2 client_open: loglevel: 5
-Dec 08 19:27:34 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:27:34 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:27:34 meins 3 client_open: connecting localhost:2990
-Dec 08 19:27:34 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:27:34 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:27:34 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:27:34 meins 2 client_post_select: --> 1725057215
-Dec 08 19:27:34 meins 2 client_post_select: decrypting session key
-Dec 08 19:27:34 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:27:34 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:27:45 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:27:45 meins 2 clear_slot: clearing slot 0
-Dec 08 19:27:45 meins 3 open_receiver: started mp3: http receiver in slot 0
-Dec 08 19:27:45 meins 2 register_task: registering http receiver node (0x806a7b4)
-Dec 08 19:27:45 meins 2 http_recv_post_select: sending http request
-Dec 08 19:27:45 meins 2 http_recv_post_select: received ok msg, streaming
-Dec 08 19:27:46 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:27:46 meins 2 open_filters: opening mp3 filters
-Dec 08 19:27:46 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0
-Dec 08 19:27:46 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0
-Dec 08 19:27:46 meins 2 register_task: registering filter chain (0x80729f4)
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 173 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 147 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 121 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 95 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 69 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 43 ms left
-Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 17 ms left
-Dec 08 19:27:46 meins 2 open_writers: opening mp3 writers
-Dec 08 19:27:46 meins 2 open_writers: samplerate: 44100
-Dec 08 19:27:46 meins 3 wng_open: opening wng 0x80a4000 with 1 writer(s)
-Dec 08 19:27:46 meins 2 register_task: registering  (0x80a4024)
-Dec 08 19:27:46 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:27:46 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:27:46 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:28:30 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:28:58 meins 3 rn_event_handler: http_recv: end of file
-Dec 08 19:28:58 meins 2 unregister_task: unregistering http receiver node (0x806a7b4)
-Dec 08 19:28:58 meins 2 unregister_task: unregistering client (0x8067878)
-Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0
-Dec 08 19:28:58 meins 2 unregister_task: unregistering writer node group (0x80a4024)
-Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0
-Dec 08 19:28:58 meins 2 unregister_task: unregistering filter chain (0x80729f4)
-Dec 08 19:28:58 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:28:58 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:28:58 meins 2 alsa_close: closing writer node 0x80a4150
-Dec 08 19:28:58 meins 3 close_filters: closing filter chain 0x80729c8
-Dec 08 19:28:58 meins 2 close_filters: closing mp3dec filter
-Dec 08 19:28:58 meins 2 close_filters: closing compress filter
-Dec 08 19:28:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1)
-Dec 08 19:28:58 meins 2 clear_slot: clearing slot 0
-Dec 08 19:29:03 meins 2 client_open: loglevel: 5
-Dec 08 19:29:03 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:29:03 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:29:03 meins 3 client_open: connecting localhost:2990
-Dec 08 19:29:03 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:29:03 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:29:03 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:29:03 meins 2 client_post_select: --> 705434988
-Dec 08 19:29:03 meins 2 client_post_select: decrypting session key
-Dec 08 19:29:03 meins 2 enable_crypt: rc4 encryption activated for fd 8
-Dec 08 19:29:03 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:29:13 meins 2 clear_slot: clearing slot 0
-Dec 08 19:29:13 meins 3 open_receiver: started ogg: dccp receiver in slot 0
-Dec 08 19:29:13 meins 2 register_task: registering dccp receiver node (0x806a804)
-Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:29:13 meins 2 open_filters: opening ogg filters
-Dec 08 19:29:13 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0
-Dec 08 19:29:13 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0
-Dec 08 19:29:13 meins 2 register_task: registering filter chain (0x8074a44)
-Dec 08 19:29:14 meins 3 ogg_convert: input buffer: 17167, opening ov callbacks
-Dec 08 19:29:14 meins 3 ogg_convert: 2 channels, 44100 Hz
-Dec 08 19:29:14 meins 2 open_writers: opening ogg writers
-Dec 08 19:29:14 meins 2 open_writers: samplerate: 44100
-Dec 08 19:29:14 meins 3 wng_open: opening wng 0x80c8d20 with 1 writer(s)
-Dec 08 19:29:14 meins 2 register_task: registering  (0x80c8d44)
-Dec 08 19:29:14 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:29:14 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:29:14 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:29:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:30:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms
-Dec 08 19:30:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:31:48 meins 2 compute_time_diff: time diff (cur/avg): -2ms/+1ms
-Dec 08 19:31:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:32:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:32:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:33:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:33:56 meins 3 rn_event_handler: dccp_recv: end of file
-Dec 08 19:33:56 meins 2 unregister_task: unregistering dccp receiver node (0x806a804)
-Dec 08 19:33:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:33:56 meins 3 filter_event_handler: filter chain: eof
-Dec 08 19:33:56 meins 2 unregister_task: unregistering filter chain (0x8074a44)
-Dec 08 19:33:56 meins 2 wng_event_handler: wng: end of file
-Dec 08 19:33:56 meins 2 unregister_task: unregistering writer node group (0x80c8d44)
-Dec 08 19:33:56 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:33:56 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:33:56 meins 2 alsa_close: closing writer node 0x80c8e70
-Dec 08 19:33:56 meins 3 close_filters: closing filter chain 0x8074a18
-Dec 08 19:33:56 meins 2 close_filters: closing oggdec filter
-Dec 08 19:33:56 meins 2 close_filters: closing compress filter
-Dec 08 19:33:56 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1)
-Dec 08 19:33:56 meins 2 clear_slot: clearing slot 0
-Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:33:58 meins 2 clear_slot: clearing slot 0
-Dec 08 19:33:58 meins 3 open_receiver: started ogg: dccp receiver in slot 0
-Dec 08 19:33:58 meins 2 register_task: registering dccp receiver node (0x80d0dc4)
-Dec 08 19:33:58 meins 2 open_filters: opening ogg filters
-Dec 08 19:33:58 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0
-Dec 08 19:33:58 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0
-Dec 08 19:33:58 meins 2 register_task: registering filter chain (0x807481c)
-Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms
-Dec 08 19:33:58 meins 3 ogg_convert: input buffer: 16947, opening ov callbacks
-Dec 08 19:33:58 meins 3 ogg_convert: 2 channels, 44100 Hz
-Dec 08 19:33:58 meins 2 open_writers: opening ogg writers
-Dec 08 19:33:58 meins 2 open_writers: samplerate: 44100
-Dec 08 19:33:58 meins 3 wng_open: opening wng 0x809d860 with 1 writer(s)
-Dec 08 19:33:58 meins 2 register_task: registering  (0x809d884)
-Dec 08 19:33:58 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:33:58 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:33:58 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:34:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:34:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:35:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:35:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:36:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms
-Dec 08 19:36:54 meins 3 rn_event_handler: dccp_recv: end of file
-Dec 08 19:36:54 meins 2 unregister_task: unregistering dccp receiver node (0x80d0dc4)
-Dec 08 19:36:54 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:36:54 meins 3 filter_event_handler: filter chain: eof
-Dec 08 19:36:54 meins 2 unregister_task: unregistering filter chain (0x807481c)
-Dec 08 19:36:54 meins 2 wng_event_handler: wng: end of file
-Dec 08 19:36:54 meins 2 unregister_task: unregistering writer node group (0x809d884)
-Dec 08 19:36:54 meins 2 try_to_close_slot: closing slot 0 
-Dec 08 19:36:54 meins 3 wng_close: closing wng with 1 writer(s)
-Dec 08 19:36:54 meins 2 alsa_close: closing writer node 0x809d9b0
-Dec 08 19:36:54 meins 3 close_filters: closing filter chain 0x80747f0
-Dec 08 19:36:54 meins 2 close_filters: closing oggdec filter
-Dec 08 19:36:54 meins 2 close_filters: closing compress filter
-Dec 08 19:36:54 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1)
-Dec 08 19:36:54 meins 2 clear_slot: clearing slot 0
-Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:36:56 meins 2 clear_slot: clearing slot 0
-Dec 08 19:36:56 meins 3 open_receiver: started mp3: http receiver in slot 0
-Dec 08 19:36:56 meins 2 register_task: registering http receiver node (0x80d14ec)
-Dec 08 19:36:56 meins 2 http_recv_post_select: sending http request
-Dec 08 19:36:56 meins 2 http_recv_post_select: received ok msg, streaming
-Dec 08 19:36:56 meins 2 open_filters: opening mp3 filters
-Dec 08 19:36:56 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0
-Dec 08 19:36:56 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0
-Dec 08 19:36:56 meins 2 register_task: registering filter chain (0x80d16ac)
-Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 174 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 148 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 122 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 96 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 70 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 44 ms left
-Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 18 ms left
-Dec 08 19:36:56 meins 2 open_writers: opening mp3 writers
-Dec 08 19:36:56 meins 2 open_writers: samplerate: 44100
-Dec 08 19:36:56 meins 3 wng_open: opening wng 0x80743a0 with 1 writer(s)
-Dec 08 19:36:56 meins 2 register_task: registering  (0x80743c4)
-Dec 08 19:36:56 meins 2 alsa_open: 2 channel(s), 44100Hz
-Dec 08 19:36:56 meins 2 alsa_open: buffer time: 170658
-Dec 08 19:36:56 meins 2 alsa_open: buffer size: 7526, period_size: 940
-Dec 08 19:36:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:37:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms
-Dec 08 19:37:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:38:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:38:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms
-Dec 08 19:39:06 meins 7 signal_event_handler: terminating on signal 15
-Dec 08 19:39:06 meins 7 clean_exit: caught deadly signal
-Dec 08 19:39:06 meins 2 log_welcome: welcome to para_audiod git (Sat Dec  8 13:42:52 MET 2007)
-Dec 08 19:39:06 meins 1 log_welcome: using loglevel 1
-Dec 08 19:39:06 meins 2 init_writers: maximal number of writers: 3
-Dec 08 19:39:06 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:39:06 meins 2 init_writers: mp3 writer #0: alsa
-Dec 08 19:39:06 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:39:06 meins 2 init_writers: ogg writer #0: alsa
-Dec 08 19:39:06 meins 2 check_writer_arg: checking  alsa -d plug:swmix
-Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2
-Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0
-Dec 08 19:39:06 meins 2 init_writers: aac writer #0: alsa
-Dec 08 19:39:06 meins 2 init_receivers: initializing http receiver
-Dec 08 19:39:06 meins 2 init_receivers: initializing dccp receiver
-Dec 08 19:39:06 meins 2 init_receivers: initializing ortp receiver
-Dec 08 19:39:06 meins 1 check_receiver_arg: checking http -i 127.0.0.1
-Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i 127.0.0.1
-Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: http
-Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost
-Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost
-Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp
-Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost
-Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost
-Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp
-Dec 08 19:39:06 meins 2 init_filters: maximal number of filters: 6
-Dec 08 19:39:06 meins 2 add_filter: mp3 filter 1: mp3dec
-Dec 08 19:39:06 meins 2 add_filter: mp3 filter 2: compress
-Dec 08 19:39:06 meins 2 add_filter: ogg filter 1: oggdec
-Dec 08 19:39:06 meins 2 add_filter: ogg filter 2: compress
-Dec 08 19:39:06 meins 2 add_filter: aac filter 1: aacdec
-Dec 08 19:39:06 meins 2 add_filter: aac filter 2: compress
-Dec 08 19:39:06 meins 2 clear_slot: clearing slot 0
-Dec 08 19:39:06 meins 2 clear_slot: clearing slot 1
-Dec 08 19:39:06 meins 2 clear_slot: clearing slot 2
-Dec 08 19:39:06 meins 2 clear_slot: clearing slot 3
-Dec 08 19:39:06 meins 2 clear_slot: clearing slot 4
-Dec 08 19:39:06 meins 2 init_grabbing: grab init
-Dec 08 19:39:06 meins 2 setup_signal_handling: signal pipe: fd 4
-Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 2
-Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 15
-Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 1
-Dec 08 19:39:06 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins
-Dec 08 19:39:06 meins 2 daemon_init: daemonizing
-Dec 08 19:39:06 meins 2 init_sched: initializing scheduler
-Dec 08 19:39:06 meins 2 register_task: registering signal task (0x80631bc)
-Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80631c0
-Dec 08 19:39:06 meins 1 register_task: post_select: 0x80631c0
-Dec 08 19:39:06 meins 2 register_task: registering command task (0xbfa7e274)
-Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e278
-Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e278
-Dec 08 19:39:06 meins 2 register_task: registering status task (0x80632e8)
-Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80632ec
-Dec 08 19:39:06 meins 1 register_task: post_select: 0x80632ec
-Dec 08 19:39:06 meins 2 register_task: registering audiod task (0xbfa7e398)
-Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e39c
-Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e39c
-Dec 08 19:39:06 meins 2 status_pre_select: clock diff count: 5
-Dec 08 19:39:06 meins 2 client_open: loglevel: 5
-Dec 08 19:39:06 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf
-Dec 08 19:39:06 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan
-Dec 08 19:39:06 meins 3 client_open: connecting localhost:2990
-Dec 08 19:39:06 meins 2 register_task: registering client (0x8067878)
-Dec 08 19:39:06 meins 1 register_task: pre_select: 0x806787c
-Dec 08 19:39:06 meins 1 register_task: post_select: 0x806787c
-Dec 08 19:39:06 meins 2 client_post_select: --> auth rc4 maan
-Dec 08 19:39:06 meins 2 client_post_select: <-- [challenge]
-Dec 08 19:39:06 meins 2 client_post_select: --> 153889019
-Dec 08 19:39:06 meins 1 client_post_select: ++++ server info ++++
-
-Proceed.
-
-++++ end of server info ++++
-Dec 08 19:39:06 meins 2 client_post_select: decrypting session key
-Dec 08 19:39:06 meins 2 enable_crypt: rc4 encryption activated for fd 7
-Dec 08 19:39:06 meins 1 client_post_select: --> 
-stat
-1
-End of Command.
index 2a6c7443d1b1852481eb0a7b92cba4e576c6afd0..9166de768a76adb05f73a5984325184f557c9734 100644 (file)
Binary files a/web/screenshots/gui.png and b/web/screenshots/gui.png differ
diff --git a/web/screenshots/server.log b/web/screenshots/server.log
deleted file mode 100644 (file)
index f84ede8..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-Dec 08 19:28:58 2: (21285) log_welcome: welcome to para_server git (Sat Dec  8 13:42:52 MET 2007)
-Dec 08 19:28:58 1: (21285) log_welcome: using loglevel 1
-Dec 08 19:28:58 1: (21285) populate_user_list: found entry for maan
-Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries
-Dec 08 19:28:58 1: (21285) populate_user_list: found entry for install
-Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries
-Dec 08 19:28:58 1: (21285) populate_user_list: found entry for www
-Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries
-Dec 08 19:28:58 2: (21285) daemon_init: daemonizing
-Dec 08 19:28:58 3: (21286) server_init: initializing audio format handlers
-Dec 08 19:28:58 2: (21286) afh_init: supported audio formats: mp3 ogg aac
-Dec 08 19:28:58 3: (21286) afh_init: initializing mp3 handler
-Dec 08 19:28:58 3: (21286) afh_init: initializing ogg handler
-Dec 08 19:28:58 3: (21286) afh_init: initializing aac handler
-Dec 08 19:28:58 3: (21286) server_init: initializing virtual streaming system
-Dec 08 19:28:58 2: (21286) vss_init: announce timeval: 300ms
-Dec 08 19:28:58 3: (21286) vss_init: initializing http sender
-Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 8000, fd 4
-Dec 08 19:28:58 1: (21286) http_send_init: http sender init complete
-Dec 08 19:28:58 3: (21286) vss_init: initializing dccp sender
-Dec 08 19:28:58 2: (21286) para_listen: listening on DCCP port 5001, fd 5
-Dec 08 19:28:58 3: (21286) vss_init: initializing ortp sender
-Dec 08 19:28:58 1: (21286) ortp_send_init: ortp sender init complete
-Dec 08 19:28:58 3: (21286) setup_signal_handling: setting up signal handlers
-Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 2
-Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 15
-Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 1
-Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 17
-Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 10
-Dec 08 19:28:58 3: (21286) server_init: initializing the audio file selector
-Dec 08 19:28:58 2: (21287) get_database_dir: afs_database dir /home/maan/.paraslash/afs_database
-Dec 08 19:28:58 3: (21287) open_afs_tables: opening 7 osl tables in /home/maan/.paraslash/afs_database
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table audio_files
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'audio_files' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 32
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'audio_files' (index: /home/maan/.paraslash/afs_database/audio_files/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/index: size 218667
-Dec 08 19:28:58 1: (21287) read_table_desc: 5 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'audio_files' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/2346ad27d7568ba9896f1b7da6b5991251debdf2: size 143430
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/3150ecd5e0294534a81ae047ddac559de481d774: size 436636
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/94ea39e309f3f31357ab60b190b6b8c32f21620b: size 225390
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/b063ad096f9d142a388ca2a10d46b56904e26cda: size 1174409
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 6830
-Dec 08 19:28:58 2: (21286) init_afs: afs_socket: 8, afs_socket_cookie: 1363241225
-Dec 08 19:28:58 3: (21286) server_init: initializing tcp command socket
-Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 2990, fd 9
-Dec 08 19:28:58 3: (21286) server_init: server init complete
-Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left
-Dec 08 19:28:58 1: (21286) status_refresh: 0 events, forcing status update
-Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10
-Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left
-Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10
-Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left
-Dec 08 19:28:58 2: (21287) aft_open: audio file table contains 6830 files
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table attributes
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'attributes' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'attributes' (index: /home/maan/.paraslash/afs_database/attributes/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/index: size 396
-Dec 08 19:28:58 1: (21287) read_table_desc: 2 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'attributes' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/fd1e48caeff7212c45fc08608b7187feb10a7a2d: size 42
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 141
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 21
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table score
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'score' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 0
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 0
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table moods
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'moods' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'moods' (index: /home/maan/.paraslash/afs_database/moods/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/index: size 235
-Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'moods' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/87ea5dfc8b8e384d848979496e706390b497e547: size 50
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 67
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 10
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table lyrics
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'lyrics' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'lyrics' (index: /home/maan/.paraslash/afs_database/lyrics/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/index: size 347
-Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'lyrics' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/87ea5dfc8b8e384d848979496e706390b497e547: size 85
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 478
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 17
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table images
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'images' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'images' (index: /home/maan/.paraslash/afs_database/images/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/index: size 7227
-Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'images' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/87ea5dfc8b8e384d848979496e706390b497e547: size 2235
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 12950
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 447
-Dec 08 19:28:58 2: (21287) osl_open_table: opening table playlists
-Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'playlists' from table description
-Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16
-Dec 08 19:28:58 1: (21287) map_table: mapping table 'playlists' (index: /home/maan/.paraslash/afs_database/playlists/index)
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/index: size 107
-Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns
-Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'playlists' matches on-disk data, good
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/87ea5dfc8b8e384d848979496e706390b497e547: size 10
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 7
-Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 2
-Dec 08 19:28:58 2: (21287) afs_init: server_socket: 9, afs_socket_cookie: 1363241225
-Dec 08 19:28:58 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94
-Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94: size 31
-Dec 08 19:28:58 1: (21287) parse_mood_line: accept entry added, method: 0x806c0f4
-Dec 08 19:28:58 3: (21287) change_current_mood: computing statistics of admissible files
-Dec 08 19:28:58 2: (21287) log_statistics: last_played mean: 1193159581, last_played sigma: 4161524
-Dec 08 19:28:58 2: (21287) log_statistics: num_played mean: 32, num_played sigma: 21
-Dec 08 19:28:58 2: (21287) change_current_mood: 26 admissible files 
-Dec 08 19:28:58 3: (21287) change_current_mood: loaded mood gulp
-Dec 08 19:28:58 2: (21287) register_signal_task: signal pipe: fd 8
-Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 2
-Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 15
-Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 13
-Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 1
-Dec 08 19:28:58 2: (21287) init_sched: initializing scheduler
-Dec 08 19:28:58 2: (21287) register_task: registering signal task (0x806f244)
-Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f248
-Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f248
-Dec 08 19:28:58 2: (21287) setup_command_socket_or_die: listening on socket /var/paraslash/afs_command_socket (fd 1)
-Dec 08 19:28:58 2: (21287) register_task: registering command task (0x806f118)
-Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f11c
-Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f11c
-Dec 08 19:28:59 2: (21286) main: got connection from ::ffff:127.0.0.1#42344, forking
-Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 9
-Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 8
-Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 6
-Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 5
-Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 4
-Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13507ms left
-Dec 08 19:28:59 1: (21356) handle_connect: received rc4 request for user maan
-Dec 08 19:28:59 1: (21356) handle_connect: sending 64 byte challenge
-Dec 08 19:28:59 2: (21356) handle_connect: good auth for maan (1081806111)
-Dec 08 19:28:59 1: (21356) init_rc4_keys: rc4 keys initialized (84:105)
-Dec 08 19:28:59 2: (21356) enable_crypt: rc4 encryption activated for fd 10
-Dec 08 19:28:59 3: (21356) handle_connect: invalid command
-Dec 08 19:28:59 1: (21286) para_next_signal: next signal: 17
-Dec 08 19:28:59 1: (21286) para_reap_child: child 21356 exited. Exit status: 1
-Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13498ms left
-Dec 08 19:29:03 2: (21286) main: got connection from ::ffff:127.0.0.1#42345, forking
-Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 9
-Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 8
-Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 6
-Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 5
-Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 4
-Dec 08 19:29:03 1: (21286) chk_barrier: autoplay_delay barrier: 9951ms left
-Dec 08 19:29:03 1: (21362) handle_connect: received rc4 request for user maan
-Dec 08 19:29:03 1: (21362) handle_connect: sending 64 byte challenge
-Dec 08 19:29:03 2: (21362) handle_connect: good auth for maan (705434988)
-Dec 08 19:29:03 1: (21362) init_rc4_keys: rc4 keys initialized (18:65)
-Dec 08 19:29:03 2: (21362) enable_crypt: rc4 encryption activated for fd 10
-Dec 08 19:29:03 1: (21362) check_perms: checking permissions
-Dec 08 19:29:03 3: (21362) handle_connect: calling com_stat() for maan@::ffff:127.0.0.1#42345
-Dec 08 19:29:13 1: (21286) chk_barrier: autoplay_delay barrier: 3ms left
-Dec 08 19:29:13 1: (21286) vss_preselect: ready and playing, but no audio file
-Dec 08 19:29:13 3: (21286) vss_post_select: requesting new fd from afs
-Dec 08 19:29:13 1: (21287) execute_server_command: received: new
-Dec 08 19:29:13 3: (21287) open_next_audio_file: getting next audio file
-Dec 08 19:29:13 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593
-Dec 08 19:29:13 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593: size 4532
-Dec 08 19:29:13 1: (21287) mmap_full_file: /home/mp3/checked/dvd_07/cd_46/The_G.U.L.P.__Scheiss_Krieg.ogg: size 5274482
-Dec 08 19:29:13 1: (21287) mood_update_audio_file: score: 6
-Dec 08 19:29:13 1: (21287) mood_update_audio_file: moving from rank 26 to 35%
-Dec 08 19:29:13 1: (21287) score_update: new score: 0, rank 9/26
-Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score
-Dec 08 19:29:13 1: (21287) score_update: new score: -65, rank 1/26
-Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score
-Dec 08 19:29:13 1: (21287) save_afd: size: 8932
-Dec 08 19:29:13 1: (21287) pass_afd: passing 8 bytes and fd 12
-Dec 08 19:29:13 1: (21286) recv_afs_result: fd: 10, code: 0, shmid: 28442631
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left
-Dec 08 19:29:13 1: (21286) status_refresh: 1 events, forcing status update
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left
-Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left
-Dec 08 19:29:13 3: (21286) dccp_post_select: connection from ::ffff:127.0.0.1#46539
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left
-Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left
-Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10
-Dec 08 19:29:13 1: (21286) status_refresh: 2 events, forcing status update
-Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10
-Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10
-Dec 08 19:29:14 1: (21286) cq_enqueue: 4446 bytes queued for 0x8072eb0
diff --git a/wma.h b/wma.h
index f0ba7f631703b35878606facde8f2980de621bce..15b9c5d492560f2c6532935460cc7ca4f38ca571 100644 (file)
--- a/wma.h
+++ b/wma.h
@@ -20,8 +20,14 @@ struct asf_header_info {
        uint32_t bit_rate;
        /** Further decoding information (ignored). */
        uint32_t flags1;
-       /** Whether to use exp_vlc, bit reservoir, variable block len. */
+       /** Encodes exp_vlc, bit reservoir, vbl, number of block sizes. */
        uint16_t flags2;
+       /** Whether exponents are coded with VLC codes. */
+       bool use_exp_vlc;
+       /** If false, a frame is equal to a superframe. */
+       bool use_bit_reservoir;
+       /** Whether blocks are of variable or of constant size. */
+       bool use_variable_block_len;
 };
 
 /* wma_common.c */
index 207b0ba8cae25c024172023d58d3691e7c4f5e9b..0b6081cfdb9ea919537532d19dadab004a5fa825 100644 (file)
--- a/wma_afh.c
+++ b/wma_afh.c
@@ -1,20 +1,14 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file wma_afh.c The audio format handler for WMA files. */
 
-#include <stdlib.h>
-#include <inttypes.h>
 #include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
 #include <regex.h>
+#include <iconv.h>
 
 #include "para.h"
 #include "error.h"
@@ -22,6 +16,7 @@
 #include "portable_io.h"
 #include "string.h"
 #include "wma.h"
+#include "fd.h"
 
 #define FOR_EACH_FRAME(_f, _buf, _size, _ba) for (_f = (_buf); \
        _f + (_ba) + WMA_FRAME_SKIP < (_buf) + (_size); \
@@ -107,7 +102,7 @@ static char *get_str16(const char *in, int len)
        return out;
 }
 
-static const char comment_header[] = {
+static const char content_description_header[] = {
        0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
        0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
 };
@@ -132,12 +127,12 @@ static const char album_tag_header[] = { /* WM/AlbumTitle */
 static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
 {
        const char *p, *end = buf + buf_size, *q;
-       uint16_t len1, len2, len3, len4, len5;
+       uint16_t len1, len2, len3, len4;
 
-       p = search_pattern(comment_header, sizeof(comment_header),
-               buf, buf_size);
+       p = search_pattern(content_description_header,
+               sizeof(content_description_header), buf, buf_size);
        if (!p || p + 34 >= end) {
-               PARA_NOTICE_LOG("comment header not found\n");
+               PARA_NOTICE_LOG("content description header not found\n");
                goto next;
        }
        p += 24;
@@ -149,7 +144,7 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
        p += 2;
        len4 = read_u16(p);
        p += 2;
-       len5 = read_u16(p);
+       /* ignore length of the rating information */
        p += 2;
        if (p + len1 >= end)
                goto next;
@@ -158,10 +153,10 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
        if (p + len2 >= end)
                goto next;
        ti->artist = get_str16(p, len2);
-       p += len2 + len3 + len4;
-       if (p + len5 >= end)
+       p += len2 + len3;
+       if (p + len4 >= end)
                goto next;
-       ti->comment = get_str16(p, len5);
+       ti->comment = get_str16(p, len4);
 next:
        p = search_pattern(extended_content_header, sizeof(extended_content_header),
                buf, buf_size);
@@ -257,12 +252,396 @@ static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        afhi->frequency = ahi.sample_rate;
        afhi->channels = ahi.channels;
        afhi->header_len = ahi.header_len;
+
+       afhi->techinfo = make_message("%s%s%s%s%s",
+               ahi.use_exp_vlc? "exp vlc" : "",
+               (ahi.use_bit_reservoir && ahi.use_exp_vlc)? ", " : "",
+               ahi.use_bit_reservoir? "bit reservoir" : "",
+               (ahi.use_variable_block_len &&
+                       (ahi.use_exp_vlc || ahi.use_bit_reservoir)? ", " : ""),
+               ahi.use_variable_block_len? "vbl" : ""
+       );
        wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len,
                ahi.block_align, afhi);
        read_asf_tags(map, ahi.header_len, &afhi->tags);
        return 0;
 }
 
+struct asf_object {
+       char *ptr;
+       uint64_t size;
+};
+
+struct tag_object_nums {
+       int content_descr_obj_num;
+       int extended_content_descr_obj_num;
+};
+
+struct afs_top_level_header_object {
+       uint64_t size;
+       uint32_t num_objects;
+       uint8_t reserved1, reserved2;
+       struct asf_object *objects;
+};
+
+#define CHECK_HEADER(_p, _h) (memcmp((_p), (_h), sizeof((_h))) == 0)
+
+static int read_asf_objects(const char *src, size_t size, uint32_t num_objects,
+               struct asf_object *objs, struct tag_object_nums *ton)
+{
+       int i;
+       const char *p;
+
+       for (i = 0, p = src; i < num_objects; p += objs[i++].size) {
+               if (p + 24 > src + size)
+                       return -E_NO_WMA;
+               objs[i].ptr = (char *)p;
+               objs[i].size = read_u64(p + 16);
+               if (p + objs[i].size > src + size)
+                       return -E_NO_WMA;
+
+               if (CHECK_HEADER(p, content_description_header))
+                       ton->content_descr_obj_num = i;
+               else if (CHECK_HEADER(p, extended_content_header))
+                       ton->extended_content_descr_obj_num = i;
+       }
+       return 1;
+}
+
+static const char top_level_header_object_guid[] = {
+       0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11,
+       0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
+};
+
+static int convert_utf8_to_utf16(char *src, char **dst)
+{
+       /*
+        * Without specifying LE (little endian), iconv includes a byte order
+        * mark (e.g. 0xFFFE) at the beginning.
+        */
+       iconv_t cd = iconv_open("UTF-16LE", "UTF-8");
+       size_t sz, inbytes, outbytes, inbytesleft, outbytesleft;
+       char *inbuf, *outbuf;
+       int ret;
+
+       if (!src || !*src) {
+               *dst = para_calloc(2);
+               ret = 0;
+               goto out;
+       }
+       if (cd == (iconv_t) -1)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       inbuf = src;
+       /* even though src is in UTF-8, strlen() should DTRT */
+       inbytes = inbytesleft = strlen(src);
+       outbytes = outbytesleft = 4 * inbytes + 2; /* hope that's enough */
+       *dst = outbuf = para_malloc(outbytes);
+       sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
+       if (sz == (size_t)-1) {
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               goto out;
+       }
+       assert(outbytes >= outbytesleft);
+       assert(outbytes - outbytesleft < INT_MAX - 2);
+       ret = outbytes - outbytesleft;
+       outbuf = para_realloc(*dst, ret + 2);
+       outbuf[ret] = outbuf[ret + 1] = '\0';
+       ret += 2;
+       *dst = outbuf;
+       PARA_INFO_LOG("converted %s to %d UTF-16 bytes\n", src, ret);
+out:
+       if (ret < 0)
+               free(*dst);
+       if (iconv_close(cd) < 0)
+               PARA_WARNING_LOG("iconv_close: %s\n", strerror(errno));
+       return ret;
+}
+
+/* The content description object contains artist, title, comment. */
+static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
+               struct asf_object *result)
+{
+       const char *cr, *rating; /* orig data */
+       uint16_t orig_title_bytes, orig_artist_bytes, orig_cr_bytes,
+               orig_comment_bytes, orig_rating_bytes;
+       /* pointers to new UTF-16 tags */
+       char *artist = NULL, *title = NULL, *comment = NULL;
+       /* number of bytes in UTF-16 for the new tags */
+       int artist_bytes, title_bytes, comment_bytes, ret;
+       char *p, null[2] = "\0\0";
+
+       result->ptr = NULL;
+       result->size = 0;
+       ret = convert_utf8_to_utf16(tags->artist, &artist);
+       if (ret < 0)
+               return ret;
+       artist_bytes = ret;
+       ret = convert_utf8_to_utf16(tags->title, &title);
+       if (ret < 0)
+               goto out;
+       title_bytes = ret;
+       ret = convert_utf8_to_utf16(tags->comment, &comment);
+       if (ret < 0)
+               goto out;
+       comment_bytes = ret;
+
+       if (cdo) {
+               /*
+                * Sizes of the five fields (stored as 16-bit numbers) are
+                * located after the header (16 bytes) and the cdo size (8
+                * bytes).
+                */
+               orig_title_bytes = read_u16(cdo->ptr + 24);
+               orig_artist_bytes = read_u16(cdo->ptr + 26);
+               orig_cr_bytes = read_u16(cdo->ptr + 28);
+               orig_comment_bytes = read_u16(cdo->ptr + 30);
+               orig_rating_bytes = read_u16(cdo->ptr + 32);
+               cr = cdo->ptr + 34 + orig_title_bytes + orig_artist_bytes;
+               rating = cr + orig_cr_bytes + orig_comment_bytes;
+       } else {
+               orig_title_bytes = 2;
+               orig_artist_bytes = 2;
+               orig_cr_bytes = 2;
+               orig_comment_bytes = 2;
+               orig_rating_bytes = 2;
+               cr = null;
+               rating = null;
+       }
+
+       /* compute size of result cdo */
+       result->size = 16 + 8 + 5 * 2 + title_bytes + artist_bytes
+               + orig_cr_bytes + comment_bytes + orig_rating_bytes;
+       PARA_DEBUG_LOG("cdo is %zu bytes\n", (size_t)result->size);
+       p = result->ptr = para_malloc(result->size);
+       memcpy(p, content_description_header, 16);
+       p += 16;
+       write_u64(p, result->size);
+       p += 8;
+       write_u16(p, title_bytes);
+       p += 2;
+       write_u16(p, artist_bytes);
+       p += 2;
+       write_u16(p, orig_cr_bytes);
+       p += 2;
+       write_u16(p, comment_bytes);
+       p += 2;
+       write_u16(p, orig_rating_bytes);
+       p += 2;
+       memcpy(p, title, title_bytes);
+       p += title_bytes;
+       memcpy(p, artist, artist_bytes);
+       p += artist_bytes;
+       memcpy(p, cr, orig_cr_bytes);
+       p += orig_cr_bytes;
+       memcpy(p, comment, comment_bytes);
+       p += comment_bytes;
+       memcpy(p, rating, orig_rating_bytes);
+       p += orig_rating_bytes;
+       assert(p - result->ptr == result->size);
+       ret = 1;
+out:
+       free(artist);
+       free(title);
+       free(comment);
+       return ret;
+}
+
+/* The extended content description object contains album and year. */
+static int make_ecdo(struct taginfo *tags, struct asf_object *result)
+{
+       int ret;
+       char *p, *album = NULL, *year = NULL, null[2] = "\0\0";
+       int album_bytes, year_bytes;
+
+       result->ptr = NULL;
+       result->size = 0;
+       ret = convert_utf8_to_utf16(tags->album, &album);
+       if (ret < 0)
+               return ret;
+       album_bytes = ret;
+       ret = convert_utf8_to_utf16(tags->year, &year);
+       if (ret < 0)
+               goto out;
+       year_bytes = ret;
+       result->size = 16 + 8 + 2; /* GUID, size, count */
+       /* name_length + name + null + data type + val length + val */
+       result->size += 2 + sizeof(album_tag_header) + 2 + 2 + 2 + album_bytes;
+       result->size += 2 + sizeof(year_tag_header) + 2 + 2 + 2 + year_bytes;
+
+       p = result->ptr = para_malloc(result->size);
+       memcpy(p, extended_content_header, 16);
+       p += 16;
+       write_u64(p, result->size);
+       p += 8;
+       write_u16(p, 2); /* count */
+       p += 2;
+
+       /* album */
+       write_u16(p, sizeof(album_tag_header) + 2);
+       p += 2;
+       memcpy(p, album_tag_header, sizeof(album_tag_header));
+       p += sizeof(album_tag_header);
+       memcpy(p, null, 2);
+       p += 2;
+       write_u16(p, 0); /* data type (UTF-16) */
+       p += 2;
+       write_u16(p, album_bytes);
+       p += 2;
+       memcpy(p, album, album_bytes);
+       p += album_bytes;
+
+       /* year */
+       write_u16(p, sizeof(year_tag_header));
+       p += 2;
+       memcpy(p, year_tag_header, sizeof(year_tag_header));
+       p += sizeof(year_tag_header);
+       memcpy(p, null, 2);
+       p += 2;
+       write_u16(p, 0); /* data type (UTF-16) */
+       p += 2;
+       write_u16(p, year_bytes);
+       p += 2;
+       memcpy(p, year, year_bytes);
+       p += year_bytes;
+       assert(p - result->ptr == result->size);
+       ret = 1;
+out:
+       free(album);
+       free(year);
+       return ret;
+}
+
+static int write_output_file(int fd, const char *map, size_t mapsize,
+               struct afs_top_level_header_object *top, struct tag_object_nums *ton,
+               struct asf_object *cdo, struct asf_object *ecdo)
+{
+       int i, ret;
+       uint64_t sz; /* of the new header object */
+       uint32_t num_objects;
+       char tmp[8];
+
+       sz = 16 + 8 + 4 + 1 + 1; /* top-level header object */
+       for (i = 0; i < top->num_objects; i++) {
+               if (i == ton->content_descr_obj_num)
+                       continue;
+               if (i == ton->extended_content_descr_obj_num)
+                       continue;
+               sz += top->objects[i].size;
+       }
+       sz += cdo->size;
+       sz += ecdo->size;
+       num_objects = top->num_objects;
+       if (ton->content_descr_obj_num < 0)
+               num_objects++;
+       if (ton->extended_content_descr_obj_num < 0)
+               num_objects++;
+       ret = xwrite(fd, top_level_header_object_guid, 16);
+       if (ret < 0)
+               goto out;
+       write_u64(tmp, sz);
+       ret = xwrite(fd, tmp, 8);
+       if (ret < 0)
+               goto out;
+       write_u32(tmp, num_objects);
+       ret = xwrite(fd, tmp, 4);
+       if (ret < 0)
+               goto out;
+       write_u8(tmp, top->reserved1);
+       ret = xwrite(fd, tmp, 1);
+       if (ret < 0)
+               goto out;
+       write_u8(tmp, top->reserved2);
+       ret = xwrite(fd, tmp, 1);
+       if (ret < 0)
+               goto out;
+       /*
+        * Write cto and ecto as objects 0 and 1 if they did not exist in the
+        * original file.
+        */
+       if (ton->content_descr_obj_num < 0) {
+               ret = xwrite(fd, cdo->ptr, cdo->size);
+               if (ret < 0)
+                       goto out;
+       }
+       if (ton->extended_content_descr_obj_num < 0) {
+               ret = xwrite(fd, ecdo->ptr, ecdo->size);
+               if (ret < 0)
+                       goto out;
+       }
+
+       for (i = 0; i < top->num_objects; i++) {
+               char *buf = top->objects[i].ptr;
+               sz = top->objects[i].size;
+               if (i == ton->content_descr_obj_num) {
+                       buf = cdo->ptr;
+                       sz = cdo->size;
+               } else if (i == ton->extended_content_descr_obj_num) {
+                       buf = ecdo->ptr;
+                       sz = ecdo->size;
+               }
+               ret = xwrite(fd, buf, sz);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = xwrite(fd, map + top->size, mapsize - top->size);
+out:
+       return ret;
+}
+
+static int wma_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int fd,
+               __a_unused const char *filename)
+{
+       struct afs_top_level_header_object top;
+       struct tag_object_nums ton = {-1, -1};
+       const char *p = map;
+       /* (extended) content description object */
+       struct asf_object cdo = {.ptr = NULL}, ecdo = {.ptr = NULL};
+       int ret;
+
+       /* guid + size + num_objects + 2 * reserved */
+       if (mapsize < 16 + 8 + 4 + 1 + 1)
+               return -E_NO_WMA;
+       if (memcmp(map, top_level_header_object_guid, 16))
+               return -E_NO_WMA;
+       p += 16;
+       top.size = read_u64(p);
+       PARA_INFO_LOG("header_size: %lu\n", (long unsigned)top.size);
+       if (top.size >= mapsize)
+               return -E_NO_WMA;
+       p += 8;
+       top.num_objects = read_u32(p);
+       PARA_NOTICE_LOG("%u header objects\n", top.num_objects);
+       if (top.num_objects > top.size / 24)
+               return -E_NO_WMA;
+       p += 4;
+       top.reserved1 = read_u8(p);
+       p++;
+       top.reserved2 = read_u8(p);
+       if (top.reserved2 != 2)
+               return -E_NO_WMA;
+       p++; /* objects start at p */
+       top.objects = para_malloc(top.num_objects * sizeof(struct asf_object));
+       ret = read_asf_objects(p, top.size - (p - map), top.num_objects,
+               top.objects, &ton);
+       if (ret < 0)
+               goto out;
+       ret = make_cdo(tags, ton.content_descr_obj_num >= 0?
+               top.objects + ton.content_descr_obj_num : NULL, &cdo);
+       if (ret < 0)
+               goto out;
+       ret = make_ecdo(tags, &ecdo);
+       if (ret < 0)
+               goto out;
+       ret = write_output_file(fd, map, mapsize, &top, &ton, &cdo,
+               &ecdo);
+out:
+       free(cdo.ptr);
+       free(ecdo.ptr);
+       free(top.objects);
+       return ret;
+}
+
 static const char* wma_suffixes[] = {"wma", NULL};
 
 /**
@@ -274,4 +653,5 @@ void wma_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = wma_get_file_info;
        afh->suffixes = wma_suffixes;
+       afh->rewrite_tags = wma_rewrite_tags;
 }
index b101d0a6e88e921d814e630113d59a263a657fd1..8886a061751caaa6c16375e244ea92a8f18a212d 100644 (file)
@@ -1,18 +1,12 @@
 /*
- * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file wma_common.c Functions used by both the WMA afh and decoder. */
 
-#include <inttypes.h>
 #include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
 
 #include "para.h"
 #include "error.h"
@@ -115,6 +109,9 @@ int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi)
        ahi->flags2 = read_u16(start + 60);
        PARA_INFO_LOG("read_asf_header: flags1: %d, flag2: %d\n",
                ahi->flags1, ahi->flags2);
+       ahi->use_exp_vlc = ahi->flags2 & 0x0001;
+       ahi->use_bit_reservoir = ahi->flags2 & 0x0002;
+       ahi->use_variable_block_len = ahi->flags2 & 0x0004;
        return 1;
 }
 
index 8b751f04592eff0f7d6b3e5fe73ade1938c17382..6b7251ee06338d1c1b50b4cd5eb674a6958ee0c0 100644 (file)
 
 #define _XOPEN_SOURCE 600
 
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <math.h>
-#include <string.h>
 #include <regex.h>
 #include <sys/select.h>
 
@@ -62,17 +58,11 @@ struct private_wmadec_data {
        /** Information contained in the audio file header. */
        struct asf_header_info ahi;
        struct getbit_context gb;
-       /** Whether to use the bit reservoir. */
-       int use_bit_reservoir;
-       /** Whether to use variable block length. */
-       int use_variable_block_len;
-       /** Whether to use exponent coding. */
-       int use_exp_vlc;
        /** Whether perceptual noise is added. */
        int use_noise_coding;
        /** Depends on number of the bits per second and the frame length. */
        int byte_offset_bits;
-       /** Only used if use_exp_vlc is true. */
+       /** Only used if ahi->use_exp_vlc is true. */
        struct vlc exp_vlc;
        uint16_t exponent_bands[BLOCK_NB_SIZES][25];
        /** The index of the first coef in high band. */
@@ -96,7 +86,7 @@ struct private_wmadec_data {
        int frame_len;
        /** log2 of frame_len. */
        int frame_len_bits;
-       /** Number of block sizes. */
+       /** Number of block sizes, one if !ahi->use_variable_block_len. */
        int nb_block_sizes;
        /* block info */
        int reset_block_lengths;
@@ -149,6 +139,8 @@ struct private_wmadec_data {
 #define VLCBITS 9
 #define VLCMAX DIV_ROUND_UP(22, VLCBITS)
 
+/** \cond sine_winows */
+
 #define SINE_WINDOW(x) static float sine_ ## x[x] __a_aligned(16)
 
 SINE_WINDOW(128);
@@ -161,6 +153,7 @@ SINE_WINDOW(4096);
 static float *sine_windows[6] = {
        sine_128, sine_256, sine_512, sine_1024, sine_2048, sine_4096
 };
+/** \endcond sine_windows */
 
 /* Generate a sine window. */
 static void sine_window_init(float *window, int n)
@@ -177,7 +170,7 @@ static void wmadec_cleanup(struct private_wmadec_data *pwd)
 
        for (i = 0; i < pwd->nb_block_sizes; i++)
                imdct_end(pwd->mdct_ctx[i]);
-       if (pwd->use_exp_vlc)
+       if (pwd->ahi.use_exp_vlc)
                free_vlc(&pwd->exp_vlc);
        if (pwd->use_noise_coding)
                free_vlc(&pwd->hgain_vlc);
@@ -311,7 +304,7 @@ static int wma_init(struct private_wmadec_data *pwd)
        else
                pwd->frame_len_bits = 11;
        pwd->frame_len = 1 << pwd->frame_len_bits;
-       if (pwd->use_variable_block_len) {
+       if (pwd->ahi.use_variable_block_len) {
                int nb_max, nb;
                nb = ((flags2 >> 3) & 3) + 1;
                if ((ahi->bit_rate / ahi->channels) >= 32000)
@@ -392,7 +385,7 @@ static int wma_init(struct private_wmadec_data *pwd)
                pwd->frame_len, bps, bps1,
                high_freq, pwd->byte_offset_bits);
        PARA_INFO_LOG("use_noise_coding=%d use_exp_vlc=%d nb_block_sizes=%d\n",
-               pwd->use_noise_coding, pwd->use_exp_vlc, pwd->nb_block_sizes);
+               pwd->use_noise_coding, pwd->ahi.use_exp_vlc, pwd->nb_block_sizes);
 
        compute_scale_factor_band_sizes(pwd, high_freq);
        /* init MDCT windows : simple sinus window */
@@ -407,7 +400,7 @@ static int wma_init(struct private_wmadec_data *pwd)
 
        if (pwd->use_noise_coding) {
                /* init the noise generator */
-               if (pwd->use_exp_vlc)
+               if (pwd->ahi.use_exp_vlc)
                        pwd->noise_mult = 0.02;
                else
                        pwd->noise_mult = 0.04;
@@ -481,10 +474,6 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
                return ret;
        }
 
-       pwd->use_exp_vlc = pwd->ahi.flags2 & 0x0001;
-       pwd->use_bit_reservoir = pwd->ahi.flags2 & 0x0002;
-       pwd->use_variable_block_len = pwd->ahi.flags2 & 0x0004;
-
        ret = wma_init(pwd);
        if (ret < 0)
                return ret;
@@ -501,7 +490,7 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
                        wma_hgain_huffcodes, 2);
        }
 
-       if (pwd->use_exp_vlc) {
+       if (pwd->ahi.use_exp_vlc) {
                PARA_INFO_LOG("using exp_vlc\n");
                init_vlc(&pwd->exp_vlc, EXPVLCBITS, sizeof(wma_scale_huffbits),
                        wma_scale_huffbits, wma_scale_huffcodes, 4);
@@ -847,7 +836,7 @@ static int wma_decode_block(struct private_wmadec_data *pwd)
        int nb_coefs[MAX_CHANNELS];
 
        /* compute current block length */
-       if (pwd->use_variable_block_len) {
+       if (pwd->ahi.use_variable_block_len) {
                n = wma_log2(pwd->nb_block_sizes - 1) + 1;
 
                if (pwd->reset_block_lengths) {
@@ -924,7 +913,7 @@ static int wma_decode_block(struct private_wmadec_data *pwd)
        if ((pwd->block_len_bits == pwd->frame_len_bits) || get_bit(&pwd->gb)) {
                for (ch = 0; ch < pwd->ahi.channels; ch++) {
                        if (pwd->channel_coded[ch]) {
-                               if (pwd->use_exp_vlc) {
+                               if (pwd->ahi.use_exp_vlc) {
                                        ret = decode_exp_vlc(pwd, ch);
                                        if (ret < 0)
                                                return ret;
@@ -1093,7 +1082,7 @@ static int wma_decode_superframe(struct private_wmadec_data *pwd, void *data,
        buf_size = pwd->ahi.block_align;
        samples = data;
        init_get_bits(&pwd->gb, buf, buf_size);
-       if (pwd->use_bit_reservoir) {
+       if (pwd->ahi.use_bit_reservoir) {
                int i, nb_frames, bit_offset, pos, len;
                uint8_t *q;
 
@@ -1210,9 +1199,9 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result)
 
 #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
 
-static int wmadec_post_select(__a_unused struct sched *s, struct task *t)
+static int wmadec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        int ret, converted, out_size;
        struct private_wmadec_data *pwd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
diff --git a/write.c b/write.c
index 6799db4175282b779c2fd454d11988be31b933d8..b3f103e97d4ae1c5e1f84c6fe2de8bb1bac70df3 100644 (file)
--- a/write.c
+++ b/write.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -71,19 +71,19 @@ static void setup_writer_node(const char *arg, struct btr_node *parent,
 }
 
 struct write_task {
-       struct task task;
+       struct task *task;
        struct check_wav_context *cwc;
 };
 
-static void write_pre_select(struct sched *s, struct task *t)
+static void write_pre_select(struct sched *s, void *context)
 {
-       struct write_task *wt = container_of(t, struct write_task, task);
+       struct write_task *wt = context;
        check_wav_pre_select(s, wt->cwc);
 }
 
-static int write_post_select(__a_unused struct sched *s, struct task *t)
+static int write_post_select(__a_unused struct sched *s, void *context)
 {
-       struct write_task *wt = container_of(t, struct write_task, task);
+       struct write_task *wt = context;
        return check_wav_post_select(wt->cwc);
 }
 
@@ -94,22 +94,20 @@ static int setup_and_schedule(void)
        struct writer_node *wns;
        static struct sched s;
        struct wav_params wp;
-       struct write_task wt = {
-               .task = {
-                       .pre_select = write_pre_select,
-                       .post_select = write_post_select,
-                       .status = "write task",
-               },
-       };
+       struct write_task wt;
 
        sit.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdin"));
-       stdin_set_defaults(&sit);
-       register_task(&s, &sit.task);
+       stdin_task_register(&sit, &s);
 
        COPY_WAV_PARMS(&wp, &conf);
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
-       register_task(&s, &wt.task);
+       wt.task = task_register(&(struct task_info) {
+               .name = "write",
+               .pre_select = write_pre_select,
+               .post_select = write_post_select,
+               .context = &wt,
+       }, &s);
        if (!conf.writer_given) {
                wns = para_calloc(sizeof(*wns));
                setup_writer_node(NULL, cw_btrn, wns, &s);
@@ -125,16 +123,17 @@ static int setup_and_schedule(void)
        s.default_timeout.tv_usec = 50000;
        ret = schedule(&s);
        if (ret >= 0) {
-               int j;
+               int j, ts;
                for (j = 0; j < i; j++) {
-                       struct task *t = &wns[j].task;
-                       assert(t->error < 0);
-                       if (t->error != -E_WRITE_COMMON_EOF
-                                       && t->error != -E_BTR_EOF) {
-                               PARA_ERROR_LOG("%s: %s\n", t->status,
-                                       para_strerror(-t->error));
+                       struct writer_node *wn = wns + j;
+                       ts = task_status(wn->task);
+                       assert(ts < 0);
+                       if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) {
+                               const char *name = writer_names[wn->writer_num];
+                               PARA_ERROR_LOG("%s: %s\n", name,
+                                       para_strerror(-ts));
                                if (ret >= 0)
-                                       ret = t->error;
+                                       ret = ts;
                        }
                }
        }
@@ -149,6 +148,7 @@ static int setup_and_schedule(void)
        }
        free(wns);
        check_wav_shutdown(wt.cwc);
+       sched_shutdown(&s);
        return ret;
 }
 
diff --git a/write.h b/write.h
index 829216088c3c2ae16f1be035bd75a490e1989076..c7c227eefb146db15160b70d8cb25618adb1f0b3 100644 (file)
--- a/write.h
+++ b/write.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -22,7 +22,7 @@ struct writer_node {
        /** The buffer tree node associated with this writer node. */
        struct btr_node *btrn;
        /** The task of this writer node. */
-       struct task task;
+       struct task *task;
        /** The minimal input queue size (size of one audio sample). */
        size_t min_iqs;
 };
@@ -48,7 +48,7 @@ struct writer {
         */
        void *(*parse_config_or_die)(int argc, char **argv);
        /**
-        * Dellocate all configuration resources.
+        * Deallocate all configuration resources.
         *
         * This should free whatever was allocated by \ref parse_config_or_die().
         */
@@ -59,13 +59,13 @@ struct writer {
         * This is called from scheduler. It may use the sched pointer to add
         * any file descriptors or to decrease the select timeout.
         */
-       void (*pre_select)(struct sched *s, struct task *t);
+       void (*pre_select)(struct sched *s, void *context);
        /**
         * Write audio data.
         *
         * Called from the post_select function of the writer node's task.
         */
-       int (*post_select)(struct sched *s, struct task *t);
+       int (*post_select)(struct sched *s, void *context);
        /**
         * Close one instance of the writer.
         *
index 33ef8be60d0a5fa04e2acd4d21f9b9ad7fc75d2a..cdb67e58fd65bf4438522fd0bed4ef9092aaaeaf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -16,6 +16,7 @@
 #include "buffer_tree.h"
 #include "write.h"
 #include "error.h"
+#include "write_common.h"
 
 /** the array containing the names of all supported writers */
 const char *writer_names[] ={WRITER_NAMES};
@@ -106,16 +107,16 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s)
 {
        struct writer *w = writers + wn->writer_num;
-       char *name = make_message("%s writer", writer_names[wn->writer_num]);
 
        wn->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = name, .parent = parent,
+               EMBRACE(.name = writer_names[wn->writer_num], .parent = parent,
                .handler = w->execute, .context = wn));
-       strcpy(wn->task.status, name);
-       free(name);
-       wn->task.pre_select = w->pre_select;
-       wn->task.post_select = w->post_select;
-       register_task(s, &wn->task);
+       wn->task = task_register(&(struct task_info) {
+               .name = writer_names[wn->writer_num],
+               .pre_select = w->pre_select,
+               .post_select = w->post_select,
+               .context = wn,
+       }, s);
 }
 
 /**
index ecad2d18eb2fe37b9e9d39691238b9acefebb3a2..1828875317fa7edafef76ad748c767fd702adbd4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */