]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 31 Dec 2016 20:12:33 +0000 (21:12 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Sat, 31 Dec 2016 20:12:33 +0000 (21:12 +0100)
A trivial conflict in configure.ac and remove/modify conflicts in
web/index.in.html and NEWS. The last file has been renamed to NEWS.md
in master. The conflict was resolved by copying the new text of the
NEWS file from maint (the release notes for v0.4.14) to NEWS.md and
to adjust the formatting to markdown syntax.

238 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 [deleted file]
NEWS.md [new file with mode: 0644]
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
base64.c [new file with mode: 0644]
base64.h [new file with mode: 0644]
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.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
gcc-compat.h
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 [deleted file]
web/manual.md [new file with mode: 0644]
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..10a7a8a91d570d5d9d16a18c1c1d896334adc7b4 100644 (file)
@@ -14,15 +14,13 @@ 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
-error2.h
+*.rej
+*~
 web_sync
 confdefs.h
 conftest
 conftest.c
 git-version.h
-*_completion.h
index 04d8f32621edd1426e2651aab711dd11516d9080..c607b8eab59985d84cc8ac382536c4d45e76890d 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,1557 @@ 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
+EXCLUDE                =
 
-# 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 +1886,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 +2016,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..85b4fab1ff04219e136ea00fe6cfb2d1a5ea97ab 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,13 +1,36 @@
 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://paraslash.systemlinux.org/manual.html
+       http://people.tuebingen.mpg.de/maan/paraslash/manual.html
index f82afd7b88f65a9ad2d75032ca401d0445025272..ec55c8e388a78a4fb88164aa0030320738e62c6e 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@
+M4 := @M4@
+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@
 
-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@
+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@
+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..9d10e18
--- /dev/null
@@ -0,0 +1,375 @@
+# 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
+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)
+COPYRIGHT_YEAR := 2016
+
+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
+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: all clean clean2 distclean maintainer-clean install man tarball
+all: $(prefixed_executables) $(man_pages)
+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) $(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 += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
+CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
+CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
+CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
+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
+STRICT_CFLAGS += -Wdeclaration-after-statement
+
+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
+
+cc-option = $(shell \
+       $(CC) $(1) -Werror -c -x c /dev/null -o /dev/null > /dev/null 2>&1 \
+       && echo "$(1)" \
+)
+
+STRICT_CFLAGS += $(call cc-option, -Wformat-signedness)
+
+# 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 $@
+
+$(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 | $(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_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
+       $(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
deleted file mode 100644 (file)
index d99d55d..0000000
--- a/NEWS
+++ /dev/null
@@ -1,1140 +0,0 @@
--------------------------------------------
-0.4.14 (2016-12-31) "branching oscillation"
--------------------------------------------
-
-Many fixes for nasty bugs have accumulated over the years since 0.4.13
-was released in 2013. This release contains all of them. It is the
-last v0.4.x release, so v0.4.x is now end of life. The maint branch
-of the git repository has been forwarded to v0.5.7 and will start to
-receive important fixes for 0.5.x, which is now in maintenance mode.
-
-       - Fix for the "double encryption bug" with gcrypt, which caused
-         garbage to be sent to stat clients.
-       - The "prev" command of para_play now works as expected.
-       - Invalid parameters for the begin and end chunk no longer cause
-         the afh receiver of para_recv to segfault.
-       - A fix for an assertion that triggered in the FEC slice setup code.
-       - Closing the terminal with a running para_gui causes the process to
-         die, rather than using 100% of CPU time.
-       - para_gui no longer shows stale information about the previous
-         audio file.
-       - A fix for a bug in the fec decoder which caused aborts if the buffer
-         pool filled up.
-       - The assertion in the buffer tree code no longer triggers.
-       - The "afs_event: table moods, event 0: key already exists in rbtree"
-         error no longer occurs on attribute changes.
-       - A NULL pointer dereference in com_sender() has been fixed.
-       - The -i option of lsatt works as advertised.
-       - Various fixes for the build system.
-
---------------------------------------
-0.4.13 (2013-07-29) "spectral gravity"
---------------------------------------
-
-One more 0.4.x release before the API-breaking changes for 0.5.0 go
-in. The main features of this release are the ogg/opus audio format,
-and UTF-8 support, but it includes also tons of other improvements
-and fixes all over the place.
-
-       - New audio format: ogg/opus.
-       - UTF8 support for para_gui and the mp3 audio format handler.
-       - Scheduler improvements and fixes.
-       - The obsolete gettimeofday() function has been replaced
-         by clock_gettime() on systems which support it.
-       - Speed and usability improvements for para_gui.
-       - para_client now restores the fd flags of stdin and stdout
-         on shutdown.
-       - Improved manual pages.
-       - Consistent version strings for all executables.
-       - Reduced dependencies on generated files result in fewer
-         recompilations on changes.
-       - Performance improvements for the compress filter.
-       - Improved downloads web page.
-
------------------------------------------
-0.4.12 (2012-12-20) "volatile relativity"
------------------------------------------
-
-The new command line player, the resample filter, ALSA support for
-para_fade, and the improved build system are the highlights of this
-release which probably marks the end of the 0.4.x series.
-
-       - The afh receiver and the para_play executable.
-       - The resample filter: A sample rate converter based on
-         libsamplerate.
-       - The "versions" directory has been removed from the master
-         branch. The tarballs of the old releases are now available
-         in the new "releases" branch.
-       - Overhaul of the build system: All generated files are now
-         written to the "build" directory.
-       - The modular mixer API and the alsa mixer.
-       - Minor fixes for the osx writer.
-
---------------------------------------
-0.4.11 (2012-07-20) "mutual diversity"
---------------------------------------
-
-The major feature in this release is the new sideband API for
-client-server communication. This API will be used exclusively starting
-with 0.5.0, which breaks backward compatibility but allows to get rid
-of quite some compatibility code. Other noteworthy changes include
-decoder latency improvements and a long-standing bug fix for the
-ALSA writer.
-
-       - Sideband connections: If both para_server and para_client
-         support this feature, data is sent as a multiplexed stream.
-       - The --no_default_filters option of para_filter has been
-         removed.
-       - Several fixes and latency improvements to various decoders.
-       - The ALSA writer now limits the prebuffer time to 500ms.
-       - Documentation improvements.
-       - Overhaul of the command_util.sh script.
-       - Fixes for some minor problems found by the clang analyzer.
-       - Compiles (almost) without warnings on gcc-3.
-       - Robustness improvements of the buffer tree code.
-
-------------------------------------------
-0.4.10 (2012-03-30) "heterogeneous vacuum"
-------------------------------------------
-
-Nothing earth-shaking in this release, but quite a few usability
-improvements and the usual mix of cleanups and fixes.
-
-       - The --no_default_filters option of para_filter has been
-         deprecated. It still works but has no effect and will be
-         removed in the next version.
-       - para_gui now prints also the stderr output of the executing
-         command in the bottom window.
-       - Cleanup and consolidation of the various wrappers for
-         write(), writev(), send() and friends.
-       - The obscure error messages on mmap() failures have been
-         replaced by meaningful messages. This affects mainly
-         para_afh.
-       - para_audioc: Cleanups and memory leak fixes.
-       - Test 0004-server no longer fails if para_server is not
-         being built.
-       - New configure options: --with-id3tag-{headers,libs}.
-
--------------------------------------
-0.4.9 (2011-12-06) "hybrid causality"
--------------------------------------
-
-Support for another audio format, interactive mode for para_client
-and para_audiod and many small improvements/fixes all over the place.
-
-       - Support for flac, the free lossless audio codec.
-       - Fix for an endless loop in the mp3 decoder for certain
-         (corrupt) mp3 files.
-       - When executed without specifying a command, para_client
-         and para_audioc start an interactive shell (requires
-         libreadline being installed). The interactive mode offers
-         full tab completion and command line history.
-       - autogen.sh now detects a distcc setup and adjusts the
-         parameter for the -j option of make accordingly.
-       - Shared memory areas are no longer restricted to 64K. We now
-         detect the maximal size of a shared memory area at runtime.
-       - cleanup of the internal uptime API.
-       - para_server prefaults the mmapped audio file to avoid
-         delays on slow media.
-       - A new test for the test-suite that exercises the
-         communication between para_server and para_audiod.
-       - The alsa writer eats up less CPU cycles when configured to
-         use the DMIX plugin.
-       - Simplified and unified receiver code.
-       - Makefile cleanups.
-       - Commands which print a list of matching audio files now
-         emit a meaningful error message if no audio file matched the
-         given pattern(s).
-
---------------------------------------
-0.4.8 (2011-08-19) "nested assignment"
---------------------------------------
-
-Gcrypt support, the overhauled osx writer and regex format specifiers
-are the highlights of this release.
-
-       - support for libgcrypt as a drop-in replacement for openssl.
-         Run configure --enable-cryptolib=gcrypt to link against
-         libgcrypt. The two crypto implementations are compatible to
-         each other, i.e. a para_client executable linked against
-         gcrypt can connect to para_server linked against libssl
-         and vice versa.
-       - Rewrite of the osx writer (output plugin for Mac OS).
-       - audiod: The format specifier for receivers, filters and
-         writers is now treated as a regular expression. This allows
-         to replace 5 lines in the config file (one for each audio
-         format) by one single line. See the manual for details.
-       - The *.cmdline.[ch] files are no longer contained in the released
-         tarballs. This reduces the size of the tarballs but requires
-         gengetopt to build the tarball.
-       - Compiles cleanly also with llvm/clang.
-       - Corrupt mp3 files are handled more gracefully.
-       - The alsa writer uses poll fds instead of computing timeouts.
-       - Cleanup of the generic writer API.
-       - sched: Optimized zero timeouts.
-       - vss timeout cleanups.
-       - oggdec fixes and improvements.
-
---------------------------------------
-0.4.7 (2011-06-01) "infinite rollback"
---------------------------------------
-
-The new ao writer, support for ssh RSA keys and a couple of other
-enhancements.
-
-       - Support for ESD, Pulseaudio, AIX, Solaris, IRIX and other
-         platforms through the libao audio library.
-       - Support for RSA keys generated with ssh-keygen.
-       - configure: improved options for ogg/vorbis/speex.
-       - The git version reported by --version always matches HEAD.
-       - The autogen script detects the number of processors and
-         runs a parallel make if possible.
-       - Major cleanup of the crypto API.
-       - Documentation updates.
-
-------------------------------------------
-0.4.6 (2011-03-31) "deterministic entropy"
-------------------------------------------
-
-Lots of ogg/vorbis improvements, the new test suite, enhancements
-for para_gui and a fair amount of other bug fixes.
-
-       - For DCCP/OGG streams the audio file header is only sent once
-         at the beginning of the stream rather than periodically
-         every five seconds. This reduces network traffic and the
-         FEC group size.
-       - The vorbis comment header is replaced by an empty dummy header
-         before the header is sent over the network. This also results in
-         less network traffic and smaller FEC groups.
-       - The new "test" make target allows to perform some sanity checks prior
-         to installing the package.
-       - ogg timing fixes and performance improvements
-       - Scheduler improvements
-       - Proper exit codes for para_write
-       - para_gui: New option --theme to select a startup theme. Several
-         other improvements and fixes.
-       - aacdec error message cleanups
-       - simplified color error handling
-
---------------------------------------------
-0.4.5 (2010-12-17) "symmetric randomization"
---------------------------------------------
-
-Bug fixes, internal cleanups and variable-sized FEC slices.
-
-       - Contains a fix for an invalid-free-bug in the ogg audio format
-         handler code.
-       - Switching off the DCCP sender works again.
-       - para_audiod handles crashes of para_server more robustly.
-       - Internal scheduler and writer cleanups.
-       - Reduced latency due to variable-sized FEC slices.
-       - Improved documentation and error diagnostics.
-       - The build of para_server is now optional, allowing the build
-         to succeed in case libosl is not installed.
-
-------------------------------------------
-0.4.4 (2010-08-06) "persistent regularity"
-------------------------------------------
-
-Support for yet another audio format, para_write improvements and
-bug fixes.
-
-       - Support for the speex codec.
-       - Support for sample formats other than 16 bit little endian.
-       - error2.h is now created by a perl script which speeds up configure
-         considerably.
-       - Fix a bug in the aac decoder which could lead to segfaults in
-         para_filter/para_audiod.
-       - Fixes for autoconf-2.66.
-
-----------------------------------------
-0.4.3 (2010-07-05) "imaginary radiation"
-----------------------------------------
-
-Many improvements for the DCCP and the UDP transport, the new user
-manual and the usual mix of bug fixes and internal improvements.
-
-       - FEC support for the DCCP sender (Gerrit Renker). The new
-         --dccp_max_slice_size, --dccp_data_slices_per_group and
-         --dccp_slices_per_group options can be used to set the FEC
-         parameters for the DCCP transport.
-       - DNS lookups for UDP targets (Gerrit Renker).
-       - The new user manual replaces the README, README.afs, REQUIREMENTS
-         and INSTALL documents.
-       - Fix an end-of-file detection bug in the oggdec filter.
-       - The new nonblock API.
-       - Both options of the oggdec filter have been removed.
-       - New debug mode for the internal scheduler.
-
-------------------------------------------
-0.4.2 (2010-04-23) "associative expansion"
-------------------------------------------
-
-It's been some time since the last release, but finally here is
-paraslash-0.4.2. The bulk of the changes comes from the new buffer
-tree API, but there are changes all over the tree. Mainly performance
-and usability improvements, but also quite some bug fixes.
-
-       - The new buffer tree API.
-       - DCCP: Support for CCID negotiation (Gerrit Renker).
-       - UDP robustness fixes.
-       - The --bufsize option for mp3dec is gone as it no longer makes sense
-         for the new buffer tree API.
-       - Fix audible buffer underruns for wma streams.
-       - The alsa writer no longer prints meaningless underrun durations.
-       - audiod: Defaults work also for udp streams. If no filter is
-         given for an audio format that is received via upd, fecdec is
-         automatically added as the first filter (along with the decoder).
-
----------------------------------------
-0.4.1 (2009-12-22) "concurrent horizon"
----------------------------------------
-
-Support for another audio format, minor feature enhancements and lots of bug
-fixes. All fixes that have been accumulated in the maint branch (in particular
-those mentionened in the 0.3.6 release notes) appear in this release as well.
-
-       - wma support.
-       - new afh option: --human to activate human-readable output.
-       - new server/audiod option: --log-timing to print timing information.
-       - build system improvements.
-       - source code documentation updates.
-
--------------------------------------
-0.3.6 (2009-12-07) "cubic continuity"
--------------------------------------
-
-Quite a few bugs have been found and fixed since 0.3.5, so here's
-another 0.3.x release. No new features.
-
-       - Always check return value of malloc().
-       - ogg vorbis/FEC: Do not write garbage after the audio file header.
-       - exit if root privileges could not be dropped.
-       - FEC: Fix computation of extra slices.
-       - oss: Fix check for empty input buffer.
-       - Avoid buffer underruns due to filter chain output buffer constraints.
-       - server: Fix assignment of afs_pid.
-       - Don't panic if the afs database contains unknown audio formats.
-       - http/dccp: Do not send the audio file header twice.
-       - FEC: Timing improvements.
-
-----------------------------------------------
-0.4.0 (2009-11-10) "simultaneous independence"
-----------------------------------------------
-
-Two significant changes which require the new version number: The
-improved authentication dialog and the fact that the database code
-has been moved to a library, libosl. To use the new version, you have
-to generate new RSA keys, see INSTALL for details. A shell script is
-provided for conversion of the 0.3 database to the new 0.4 format.
-
-       - stronger crypto for client authentication
-       - the database code has been moved to a library
-       - improved status item handling
-       - cleanup of the build system
-       - The "-V" option now also prints the git version
-       - the new parser-friendly listing mode for the ls and stat commands
-       - mandatory rc4 encryption
-       - major audio format handler cleanups
-       - (id3,...) tags are no longer stored as a combined string in the database
-       - new mood methods: artist_matches, title_matches, comment_matches,
-         album_matches, year_maches, year.
-
---------------------------------------------
-0.3.5 (2009-09-21) "symplectic separability"
---------------------------------------------
-
-Full client support for *BSD Unixes, complete re-write of the ogg
-vorbis audio format handler, various improvements all over the place
-and the usual mix of bugfixes. This release marks the end of the 0.3
-series if no serious problems show up.
-
-       - the new oss writer (supported on *BSD and Linux)
-       - rewrite of the ogg vorbis audio format handler. It's
-         recommended to replace the chunk tables of existing ogg
-         vorbis files in the afs database by re-adding these files
-         with "add -f".
-       - support for netmask subsets (Gerrit Renker)
-       - the new prebuffer filter
-       - improved signal handling
-       - variable fec output buffer size
-       - improved FEC timing fixes audible buffer underruns in UDP mode
-       - --log_color actually works
-       - new ls option: -d (print dates as seconds after the epoch)
-       - update to gengetopt 2.22.2
-       - support for RSA keys of size > 512 bits
-       - new option "mixer_channel" for para_fade
-
------------------------------------------
-0.3.4 (2009-05-07) "elliptic inheritance"
------------------------------------------
-
-The new udp sender, forward error correction, colored logs and various
-other improvements. As the udp sender does not depend on any special
-libraries, it is built unconditionally.
-
-       - The udp sender replaces the ortp sender. The new code uses forward
-         error correction to protect against packet losses. Many thanks to
-         Gerrit Renker for providing ipv6 support.
-       - The default port for udp streaming now defaults to 8000, like
-         for the http and the dccp senders/receivers.
-       - Loglevels are now specified as symbolic names, e.g.
-         "--loglevel info".
-       - improved ipv4 and ipv6 URI parser (Gerrit Renker).
-       - para_server/para_audiod: Color support for log messages.
-       - new options for mp3dec: --ignore-crc, --bufsize
-       - new audiod option: --config-file.
-       - gengetopt cleanups.
-       - Improved help/man pages: The documentation of para_audiod,
-         para_recv, para_filter and para_write now also contains
-         all options of the available receivers/filters/writers. The
-         man page of para_fade contains a description of the different
-         modes of operation.
-       - More source code documentation.
-       - vss timing fixes.
-
---------------------------------------------
-0.3.3 (2008-12-01) "axiomatic perspectivity"
---------------------------------------------
-
-Internal code cleanups, bug fixes, improved tag handling and the new
-amplification filter.
-
-       - para_server uses the generic scheduling code.
-       - overhaul of the virtual streaming system.
-       - mp3: id3 version 2 support via libid3tag (optional)
-       - ogg: vorbis comment support.
-       - aac meta info support.
-       - mp3 audio format handler cleanups.
-       - new filter: "amp" to amplify the amplitude of the audio stream
-       - new status item/database entry: amplification. It is
-         used by the amp filter to pre-amplify the audio stream.
-       - fix a close-without-open bug in para_write.
-       - fix a bug in com_init() which was introduced in 0.3.2.
-       - better error diagnostics for para_client.
-
------------------------------------------
-0.3.2 (2008-04-11) "probabilistic parity"
------------------------------------------
-
-The new para_afh executable, scheduling and documentation improvements.
-
-       - new ls option: -lc (list chunk table)
-       - new executable: para_afh, the stand-alone audio file handler tool
-       - afs commands can send output more than SHMMAX (32MB on Linux). This
-         also reduces the memory usage of commands that produce large amounts
-         of output.
-       - major scheduler and audiod cleanups.
-       - more detailed and much nicer man pages.
-
----------------------------------------
-0.3.1 (2008-02-23) "liquid interaction"
----------------------------------------
-
-A mix of cleanups, bug fixes, improvements, and some new features. No
-significant changes to the new database (osl) code, which is generally
-a good sign.
-
-       - Share some similar/duplicate code between the http and the
-         dccp sender.
-       - Generic access control lists for paraslash senders.
-       - dccp sender: Access control lists, connection limiting and support
-         for the allow,deny,on,off,help sender commands.
-       - The default dccp port changed from 5001 to 8000 (suggested by
-         Gerrit Renker).
-       - para_server starts even if not all public keys could be loaded.
-       - Audiod performance improvements.
-       - fix a bug in the "off" command of the http sender.
-       - fix some fd and memory leaks.
-       - Update to gengetopt-2.22.
-
--------------------------------------
-0.3.0 (2008-01-12) "solar saturation"
--------------------------------------
-
-paraslash.0.3.0 -- 'WWDBND --what would databases never do?'.
-
-
-Usually one might expect lots of new features AND a big increase in size
-for a major release like this.
-
-However, paraslash-0.3.0.tar.bz2 is the smallest paraslash tarball
-ever. The decrease in size is mostly due to the removal of some
-graphical tools (which were only quick hacks anyway). But also the
-fact that the mysql code is gone cuts down the size a bit.
-
-Being independent of mysql comes at a cost: The fact that paraslash
-now contains its own database (the object storage layer, osl) increases
-the (stripped) binary size of para_server by ~50K on i386.
-
-       - no more restrictions on unique basenames.
-       - independent of mysql: The new self-contained object
-         storage layer (osl) replaces the mysql database.
-       - New executable para_fsck: Check integrity of osl tables.
-       - Lyrics support.
-       - Reliable audio file move/rename detection.
-       - More portable than ever: Tested on Linux (x86_32, x86_64, sparc64),
-         MacOS (ppc32, x86_32), FreeBSD (x86_32), NetBSD (x86_32) and
-         Solaris (sparc64).
-       - the new osl-based audio file selector (afs) replaces the random,
-         the playlist and the mysql selector of paraslash-0.2.x.
-       - IPv6 support (thanks to Gerrit Renker).
-       - paraslash-0.2.x streams are now called "moods". Writing
-         0.3.x-mood definitions should be both easier and more
-         powerful than writing 0.2.x-stream definitions.
-       - para_krell, para_slider, para_para_sdl_gui, para_dbadm have
-         been removed. The world is a better place without them. However,
-         para_gui is still there.
-       - afs tracks audio file selection also in playlist mode.
-       - few easy-to-use afs commands replace the many not-so-easy-to-use
-         mysql commands (and are available also in playlist mode).
-       - Improved error subsystem.
-       - The earth-shaking new logo.
-
------------------------------------------
-0.2.17 (2007-11-20) "isotropic threshold"
------------------------------------------
-
-Mainly bugfixes and cleanups in this version which marks the end of
-the 0.2.x series if no serious bugs show up after the release.
-
-       - mysql_selector: fix a locking bug.
-       - universal chunk queueing.
-       - dccp sender uses chunk queueing if write() returns EAGAIN (thanks
-         to Gerrit Renker).
-       - be more carful wrt. signed vs. unsigned argument passing.
-       - cleanup error.h and fix some references to invalid error
-         codes.
-       - update to gengetopt-2.21.
-       - update to ortp-0.13.1.
-       - autoconf: extend checks for headers, library functions and
-         compiler characteristics.
-       - Fix streaming of large mp3 files.
-       - Fix an off-by-one bug in playlist handling.
-
---------------------------------------
-0.2.16 (2007-04-05) "neural discharge"
---------------------------------------
-
-The main change in this release is the major audio format handler
-cleanup which removes some similar/duplicate code and makes it easier
-to implement plugins for other audio formats. Of course, the usual mix
-of other improvements/changes/bugfixes also made it into the release.
-
-       - simplified audio format handlers (most of the handling functions
-         were moved one layer up to the virtual streaming system).
-       - para_server uses mmap to read audio files
-       - repositioning of mp3 streams is much faster, in particular for
-         jumping near the end of large mp3 files.
-       - permission flags DB_READ,DB_WRITE have been renamed to AFS_READ
-         and AFS_WRITE.
-       - fix a bug in para_filter that caused decoding of aac files
-         to start only after a few seconds.
-       - fix osx_writer hangs
-       - simplified dccp code (thanks to Gerrit Renker)
-       - the compress filter works also on big endian systems (ppc)
-
------------------------------------------
-0.2.15 (2007-02-16) "inductive resonance"
------------------------------------------
-
-Minor improvements, more documentation and a bunch of bug fixes.
-
-       - para_server: The server.users file is only read once on server
-         startup rather than for each connection
-       - mp3dec: Fix decoding of corrupt mp3 files
-       - afs (audio file sender) is now called vss (virtual streaming
-         system). Consequently, the permission flags specified in
-         ~/.paraslash/server.users have also changed: AFS_READ and AFS_WRITE
-         become VSS_READ and VSS_WRITE respectively.
-       - para_audiod/para_filter: Fix a bug that caused the last chunk
-         of audio data not being written under certain circumstances
-       - audiod: compute the difference of server time and local time
-         correctly
-       - para_server/para_audiod: Fix some memory leaks
-       - documentation improvements
-       - configure.ac: fix checks for para_krell
-       - new man pages
-
--------------------------------------------
-0.2.14 (2006-10-15) "transient singularity"
--------------------------------------------
-
-The only major enhancement of this version is the osx writer which completes
-the Mac OS Port and was originally planned already for 0.2.13 but had to wait
-until now for reasons beyond the scope of this changelog entry.
-
-       - new output plugin for Mac Os: the osx writer
-       - rename configure command line options from --enable-xxx-headers to
-         --with-xxx-headers and  --enable-xxx-libs to --with-xxx-libs
-       - configure: new command line options: --with-mad-headers,
-         --with-mad-libs, --with-oggvorbis-headers, and --with-oggvorbis-libs
-       - some robustness fixes
-       - dymamic audio format recognition for audiod
-       - para_server: new command line option: --autoplay_delay
-       - para_audiod: new command line option: --clock_diff_count
-
----------------------------------------
-0.2.13 (2006-07-14) "sonic convolution"
----------------------------------------
-
-A bunch of new features and core changes.
-
-       - the new paraslash scheduler, short and sweet.
-       - Support for m4a/mp4 files via the new aac audio format
-         handler/filter (requires libfaad).
-       - each writer has its own command line parser, just like
-         para_recv and para_filter.
-       - para_client and para_audioc use the error subsystem
-       - writers are integrated in para_audiod (currently linux-only)
-       - para_client is integrated in para_audiod
-       - random/playlist selector: improved info strings
-       - new audiod commands: tasks, kill
-       - update to libortp-0.10.1
-       - para_fade: wake time defaults to 8 hours from now
-       - update to autoconf-2.60
-
-------------------------------------------
-0.2.12 (2006-05-12) "oriented abstraction"
-------------------------------------------
-
-Many user-visible changes in this release and lots of new
-features:
-
-
-       - the new optional dccp sender/receiver. It uses the datagram
-         congestion control protocol. You'll need a fairly new kernel
-         for this.
-       - paraslash works on Mac OS X (thanks to Gerd Becker)
-       - para_play renamed to para_write
-       - modular output plugin design (writers) for para_write
-       - new file_writer output plugin for para_write
-       - compress filter speed improvements
-       - update to libortp-0.9.1
-       - update to gengetopt-2.17rc
-       - para_client no longer depends on libreadline (as the
-         code for the interactive mode was removed).
-       - gcc-2-95 is no longer a supported compiler. It may still
-         work, but it gets no more testing.
-       - the tarball no longer contains the screenshot images which
-         reduces its size quite a bit.
-       - configure: new command line options: --enable-mysql-headers
-         and --enable-mysql-libs
-
-------------------------------------
-0.2.11 (2006-03-11) "atomic duality"
-------------------------------------
-
-Here it is, the first paraslash release developed with git. There
-are fairly many user-visible changes in this release. As two out of
-the three "database tools" of paraslash don't use a database at all,
-they are now called "audio file selectors" instead.
-
-
-       - the cdt command (change database tool) becomes chs (change
-         selector)
-       - no more colon separators: The syntax of some options of
-         para_audiod and para_filter have changed. Use --help for
-         more info (and some examples).
-       - update to gengetopt-2.16 (thanks to Lorenzo Bettini)
-       - switch from cvs to git (should've done that earlier)
-       - the new ipc subsystem
-       - new audio file selector: playlist
-       - para_server: the dopey selector is now called "random",
-         and is the default selector. Use the --selector option to
-         choose another selector at startup, or the chs command to
-         change the selector at runtime.
-       - X86_64 fixes (thanks to Steffen Klassert)
-       - para_play fixes
-
---------------------------------------
-0.2.10 (2006-02-17) "cyclic attractor"
---------------------------------------
-
-Huge documentation update, a scrollable window for para_gui, ortp
-improvements, and of course many small fixes not mentioned here.
-The diffstat below is rather misleading as most insertions are due
-to the new source documentation.
-
-       - autoconf cleanup
-       - para_server also uses the new error subsystem
-       - lots of new documentation (UTSL)
-       - gui improvements:
-               - keysyms for cursor keys and for next/previous page keys
-               - scrollable output window
-               - new internal commands: scroll up/down, page up/down
-               - fix color of command output.
-       - ortp: the --chunk_time and --header flags are no longer needed
-       for para_recv/para_audiod as this information is now encoded in
-       each rtp packet sent by para_server.
-
--------------------------------------------
-0.2.9 (2006-01-24) "progressive turbulence"
--------------------------------------------
-
-Internal audiod receivers/filters, the new error subsystem and
-a lot of small improvements.
-
-       - para_recv and para_filter are integrated into the para_audiod
-         binary, i.e. audiod no longer spawns a new process for
-         each receiver/filter. As para_recv and para_filter might be
-         useful as standalone programs, they still get built (linked
-         against the same object files that are also used for audiod).
-       - further ortp timing improvements should reduce the CPU usage
-         of the ortp receiver.
-       - improved audio grabbing. The 'grab' command of para_audiod
-         has its own set of command line options. Read the output of
-         "para_audioc help grab" for more info.
-       - oggdec: configurable input prebuffer size.
-       - the new error subsystem gives better error diagnostics
-         and reduces code size.
-
------------------------------------------
-0.2.8 (2006-01-02) "dynamic accumulation"
------------------------------------------
-
-The new modular filter design and the para_play-hangs bugfix.
-
-       - new executable: para_filter. It combines para_mp3dec,
-         para_oggdec and para_compress. It also adds a further filter
-         type, wav, that just inserts a wave header at the desired point
-         of the filter chain. All 'piping' is done in-memory (i.e. no
-         read/write operations are used).
-       - para_play: fix a stupid bug that caused it to hang under
-         certain circumstances.
-
--------------------------------------------
-0.2.7 (2006-12-27) "transparent invariance"
--------------------------------------------
-
-Not many user-visible changes but a fair amount of internal improvements.
-
-
-       - The http sender buffers data if it can not be sent
-         out immediately (because the socket is not writable). This
-         should prevent para_server from shutting down the connection
-         too early on a loaded network.
-       - para_play also prebuffers data if it is told to start at a
-         future time by the --start_time option.
-       - The return of para_recv: It combines para_ortp_recv and
-         para_http_recv. Use the --receiver option to switch between
-         the two. para_recv builds without libortp, but contains
-         only the http receiver in this case.
-       - update to ortp 0.8.1. As this ortp release contains incompatible
-         changes, para_recv-0.2.7 won't link against older ortp libs.
-       - improved ortp timings.
-       - use of gcc-extensions that #define away for non-gcc and
-         gcc < 3.0.
-
--------------------------------------------
-0.2.6 (2005-10-29) "recursive compensation"
--------------------------------------------
-
-Transparent session encryption (uses openssl's Alleged RC4 cipher),
-the internal find command and several other improvements and cleanups.
-
-       - Encrypt the session if encryption is requested by the client
-         (default for para_client 0.2.6). This is backwards
-         compatible, so older clients can still connect to para_server
-         0.2.6. Use the new client option --plain to request an
-         uncrypted session (off by default, must be set to on in
-         order to connect to para_server 0.2.x with 0 <= x <= 5).
-       - para_server uses an internal function to locate audio files
-         rather than calling find(1). The server option
-         --mysql_audio_file_dir replaces --mysql_find_cmd.
-       - documentation update
-       - man pages
-       - header file cleanup
-       - para_client code cleanup
-       - para_gui: faster display of output of display commands
-
-------------------------------------------
-0.2.5 (2005-10-13) "aggressive resolution"
-------------------------------------------
-
-This release adds internal senders, i.e. no more external programs are
-spawned for sending out the audio data. There are two different senders
-available: The http sender and the ortp sender (former para_send which
-is no longer needed).
-
-The new sender code has a plugin-like design so it can be easily
-extended should there be be any future need for supporting another
-network streaming protocol. All senders are completely independent of
-each other. In particular, the http and the ortp sender can operate
-in parallel.
-
-       - new server command: sender to control senders at runtime.
-         Read the output of "para_server -h" and "para_client help
-         sender" for more information.
-       - para_recv renamed to para_ortp_recv
-       - new executable: para_http_recv, a simple command line
-         http receiver.
-       - major afs/mp3/ogg code simplifications due to internal
-         senders.
-       - ogg timing improvements
-       - fix several minor memory leaks (found by valgrind)
-       - empty stream definitions work again
-       - com_ne(): ignore errors on remove
-       - audiod: fix segfault on server restart
-
----------------------------------------
-0.2.4 (2005-09-21) "toxic anticipation"
----------------------------------------
-
-Several small improvements, fixes and the new grab command.
-
-       - audiod:
-               - new command: "grab" to grab the output of the stream reader
-                 or any filters. Read the output of "para_audioc help grab"
-                 for more information.
-               - fix memory leak
-               - code cleanup
-       - audioc: new command line option: --bufsize to specify a
-         buffer size different from the default size 8192.
-       - improved error diagnostics for para_play.
-       - new configure option: --enable-ssldir so search for openssl in
-         non-standard places
-       - sdl_gui: Make it look nice again for 1024x768
-       - server: report total size of memory allocated with sbrk by malloc,
-         new command line option: --announce_time
-
------------------------------------------
-0.2.3 (2005-09-01) "hydrophilic movement"
------------------------------------------
-
-Two new executables and major feature enhancements.
-
-       - audiod filters: It is now possible to specify arbitrary many
-         (including none) filters for each supported audio
-         format. This can be used e.g. for normalizing volume,
-         transforming or grabbing the audio stream, or for using
-         visualizers.  Read the output of "para_audiod -h" for the
-         syntax of the new --filter_cmd option.
-       - new executable: para_play, a tiny alsa player. It
-         can play wave files or raw pcm (16 bit little endian)
-         from stdin.
-       - new executable: para_compress, a dynamic range compressor
-         intended to keep audio output at a consistent volume. Derived
-         from AudioCompress, http://trikuare.cx/code/AudioCompress.html.
-       - audiod: New option: --stream_delay. This can be used in
-         a local network to syncronize the audio output of all
-         clients that play the same stream.
-
-------------------------------------------
-0.2.2 (2005-08-19) "tangential excitation"
-------------------------------------------
-
-Mostly internal changes in this release, but also some new commands
-for the mysql database tool.
-
-       - cleanup exec.c, fix para_exec bug
-       - compile time loglevel (log messages below the given level
-         won't be compiled in, which reduces the size of the
-         resulting binaries)
-       - new log macros that shorten the size of the source code.
-       - workaround a gcc-4.1 bug (?) that caused send_cred_buffer()
-         to send only zeros. With this workaround, para_audioc works
-         again.
-       - avoid gcc-4 warning: conflicting types for built-in function 'clog'
-       - new mysql commands: "rm" (remove entry), "mv" (rename entry) "ne"
-         (new entry), "snp" (set numplayed). Read the manual for more
-         information.
-
----------------------------------------
-0.2.1 (2005-08-15) "surreal experience"
----------------------------------------
-
-Here comes paraslash-0.2.1. It contains a couple of new features and,
-surprise, only minor bug fixes.
-
-       - kill noisy mp3 debug message
-       - cleanup of the build system
-       - para_server and para_client directly use the crypto routines
-         of the openssl library rather than invoking the openssl command
-         line utitlity
-       - server/audiod: new option --user to switch to the given user
-         when invoked as root. Read the output of "para_server -h" for
-         more information.
-       - gui/sdl_gui: new option --stat_cmd to be used to retrieve the
-         status. Default: "para_audioc stat"
-       - sdl_gui: new option --pic_cmd to be used to download the picture.
-         Default: "para_client pic"
-       - audiod: 5 slots ought to be enough for everybody
-       - audiod: new status item: Uptime, kill hup command
-
-------------------------------------------
-0.2.0 (2005-08-06) "distributed diffusion"
-------------------------------------------
-
-After several month of increased development activity, paraslash-0.2.0
-has arrived. It contains many new features and is much more
-self-contained than the old 0.1.x series. Enjoy!
-
-
-       - para_server: fix hang on song change and crash on sighup.
-         Speed up mysql queries. The DIR_LIKE macro is gone.
-       - new executables: para_audiod, the local audio daemon that
-         starts playback (uses SCM_CREDENTIALS socket magic) and
-         para_audioc, the corresponding client.
-       - new executables: para_mp3dec/para_oggdec, two really teensy
-         decoders. para_mp3dec is based on libmad, para_oggdec requires
-         libvorbisfile.
-       - ovsend/ovrecv are capable of streaming ogg as well as mp3, so
-          they are now called para_send and para_recv respectively.
-       - documentation updates
-       - para_gui is themable. For now there is the default theme that
-         looks as before and the simple theme: blue and easy.
-       - gui: audio streaming is now handled by audiod. Time display shows
-         playback time rather than streaming time
-       - slider: update to libzmw-0.2.0
-       - para_krell: fix crash on server shutdown
-       - switch from gzip to bzip2
-
-----------------------------------------
-0.1.7 (2005-04-18) "melting penetration"
-----------------------------------------
-
-The main change in this release is clearly the oggvorbis rewrite,
-but there are also lots of smaller changes. If you intend to use both
-the mp3 and the ogg plugin, it is recommended to use software mixing,
-e.g. the dmix plugin which is provided by ALSA.
-
-       - new executables: para_ovsend and para_ovrecv for sending/receiving
-         oggvorbis files via rtp. Requires the open rtp library. Get it at
-         http://www.linphone.org/ortp/
-       - rewrite of the ogg_vorbis core code
-       - configure detects libzmw and, if detected, includes
-          para_slider to the list of binaries to be built by make
-       - server stream writers read from their associated fifo rather
-         than from stdin
-       - slider: two new sliders, lastplayed and numplayed
-       - fix nasty double free bug which caused random segfaults in case of
-         mp3 files with invalid header information
-       - gui: new command line option: --stream_timeout=seconds  to
-         deactivate a slot if it is idle for that many seconds (default=`5')
-       - diffstats
-
----------------------------------------
-0.1.6 (2005-03-05) "asymptotic balance"
----------------------------------------
-
-Only little user-visible changes in this release. Mainly bugfixes and
-core code cleanup. This is probably the most stable version ever if you
-stick to mp3...
-
-       - fix several memory leaks
-       - rename default name of mysql database from "music" to "paraslash".
-         Use para_server's  --mysql_database option if you do not want to
-         rename your old database.
-       - rework ogg vorbis code
-       - make update command work on mysql servers with LOCAL_INFILE
-         disabled
-       - gui: improved stream I/O (slots)
-       - simplified audio format API
-       - para_pob_ogg is gone
-
-------------------------------------
-0.1.5 (2004-12-31) "opaque eternity"
-------------------------------------
-
-Let's slide gently into the new year.
-
-       - new: para_slider (not built automatically, type "make
-          para_slider" to build). A toy for those who always felt that
-          creating stream definitions is difficult. See screenshots,
-          README and FEATURES for more info.
-       - improved signal handling. Fixes server segfault on SIGHUP
-         for linux kernels newer than Aug 24 2004 and makes para_gui
-         race-free.
-       - reload database tool on SIGHUP
-       - improved help message for sl
-       - do not log "broken pipe" messages as errors. They are
-         perfectly ok.
-       - fix wrong error message on permission errors
-
------------------------------------------
-0.1.4 (2004-12-19) "tunneling transition"
------------------------------------------
-
-Bugfix release. As expected, 0.1.3 introduced a bunch of new bugs.
-Hopefully, most of them got wiped out with this release. Some
-enhancements went also in.
-
-       - improved error diagnostics for all commands
-       - stradd/picadd: overwrite previous contents if entry already
-          exists, rather than returning errors
-       - stradd: use current stream if invoked without args
-       - faster (and hopefully more stable) ogg-vorbis handling
-       - para_krell: reap children to avoid zombie-flooding in case
-         no server is running
-       - si: report also server pid
-       - server: don't busy-loop if dbtool reports only invalid files.
-       - gui: CTRL+C works again, fix stream_read command line option
-       - fix pic_add, hist
-       - fix mysql dbtool startup in case no database exists
-       - many small fixes and cleanups
-
----------------------------------------
-0.1.3: (2004-12-10) "vanishing inertia"
----------------------------------------
-
-Starting from this release, the database tools are integrated in the
-server binary. This decreases server startup time, reduces code size
-and speeds up database commands. However, the layout of the underlying
-mysql database changed only slightly and 0.1.3 should be backwards
-compatible in that respect.
-
-Visible changes:
-
-       - If mysql is not detected at compile time, or fails to init
-         at runtime, fall back to the dopey database tool which should
-         always work.
-       - para_dbtool and dbtool.conf are gone. All mysql specific
-         options are read from server.conf and are prefixed by 'mysql_'.
-       - new command: cdt (change database tool)
-       - new command line option: dbtool (choose startup database tool)
-       - The name of current stream is now stored in the database,
-         so paraslash remembers its current stream when restarted.
-       - new command: csp (change stream and play)
-       - para_gui also reports current database tool and server uptime
-
--------------------------------------------
-0.1.2: (2004-11-28) "spherical fluctuation"
--------------------------------------------
-
-Point release before the big dbtool changes go in.
-
-       - dbtool: rename ca to cam (copy all meta data). It now also
-         copies numplayed and lastplayed time as well as the picture
-         id.
-       - fix endless-loop-bug caused by mp3 files with invalid header
-
------------------------------------------
-0.1.1: (2004-11-05) "floating atmosphere"
------------------------------------------
-
-       - gkrellm plugin
-       - new dbtool command: mbox. Browse your sound-file collection
-         with your favorite mail reader.
-       - several small fixes
-
--------------------------------------
-0.1.0: (2004-10-22) "rotating cortex"
--------------------------------------
-
-       - fix logging bug for loglevel > VERBOSE
-       - fix skip command
-       - correct timings for vbr mp3s
-       - modular audio format support
-       - ogg-vorbis support (experimental)
-       - new server option: autoplay
-
------------------------------------------
-0.0.99: (2004-07-25) "harmonic deviation"
------------------------------------------
-
-       - rename projectname from icc to paraslash (play, archive, rate
-         and stream large audio sets happily)
-       - paraslash is no longer restricted to one particular audio
-         streaming software
-       - new dbtool commands (stradd, strq, strdel) for easy stream
-         managment w/o configuration file. That obsoletes stream_defs
-         file/config option for dbtool.
-       - picadd accepts jpeg data from stdin
-       - new server commands: ps (select previous stream), sc (song change)
-       - new default pictures for sdl_gui
-       - gui: new key_map option for binding commands and internal
-         functions to arbitrary keys, nice help screen, rip out
-         soundcard/linux specific stuff, avoid noise artefacts while jumping,
-         show silly logo on startup
-       - new executables: para_fade for fading volume, para_dbadm for
-         manipulating attributes
-       - cdb adds _all_ tables to mysql database
-       - revised and beautified documentation
-       - sample dbtool rewritten in C
-       - autoconf
-
----------------------------------------------
-0.0.98: (2003-12-26) "incremental smoothness"
----------------------------------------------
-
-       - kick icecast in favour of poc. That removes some races and reduces
-         core code considerably.
-       - cbr/vbr is displayed by stat and gui/sdl_gui. New status flags
-         give finer info on afs' status.
-       - gui can start decoder (see config options). Further new gui
-         commands: refresh (^L), jmp (F1-F10)
-        - gui rereads conf on SIGUSR1 instead of SIGHUP. SIGHUP
-          terminates gui. This fixes dead instances consuming memory
-          continuously.
-       - new dbtool command: verb for sending verbatim sql queries.
-       - fix pid_list races (by removing pid_list)
-       - codename funnies
-
---------------------
-0.0.97: (2003-10-26)
---------------------
-
-       - installation prefix now defaults to /usr/local
-       - new commands for gui: snozze, sleep and reread config
-       - config file for gui and sdl_gui
-       - fix problems with filenames containing funny characters
-         (reported by Thomas Forell)
-       - improved signal handling for gui, now it rereads conf on SIGHUP
-       - new dbtool command: cdb (create database)
-       - switch from argtable to gengetopt
-       - major code cleanup and speed improvements
-       - fix several potential buffer overflows
-       - many small fixes and cleanups
-
--------------------
-0.0.96 (2003-08-30)
--------------------
-
-       - easy stream_defs syntax
-       - sdl_gui can display images associated to the file being played
-       - Major feature enhancements for icc_gui including dynamic text
-         placement and the top/bottom window design
-       - vrfy/clean now also checks for NULL values in attributes as
-         well as for invalid picture pointers
-       - fix long outstanding case sensitivity bug
-       - many small fixes and cleanups
-
--------------------
-0.0.95 (2003-06-29)
--------------------
-
-       - sdl gui runs much faster
-       - new dbtool command: ca (copy attributes)
-       - count and display number of times the song has been played
-       - new feature: scoring
-       - command line options for sdl_gui
-       - simpler syntax of streams file
-       - decrease network traffic of stat
-       - fix zombie bug
-       - many small fixes and cleanups
-
--------------------
-0.0.94 (2003-05-04)
--------------------
-
-       - new server command: ns (next stream)
-       - new icc_gui command: c (change stream)
-       - internal mp3info
-       - stat shows also id3 tag info
-       - new sdl based gui
-       - log flodding bug fixed
-       - many small fixes and cleanups
-
--------------------
-0.0.93 (2003-03-28)
--------------------
-
-       - colors for icc_gui
-       - icc_gui sets volume directly (linux only)
-       - proper locking that fixes some races
-       - fix security bug that caused commands to be executed even
-         with unsufficient permissions
-       - new command: hup to make all servers reread their configuration file
-       - icecast meta data streaming
-       - many small fixes and cleanups
diff --git a/NEWS.md b/NEWS.md
new file mode 100644 (file)
index 0000000..b920156
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,1343 @@
+NEWS
+====
+
+------------------------------------------
+0.5.7 (to be announced) "semantic density"
+------------------------------------------
+- Speedup of the base64 decoder.
+- One of the two source browsers has been removed from the web pages.
+  The doxygen API reference still contains an HTML version of each
+  source file.
+- Two race conditions in para_server have been fixed.
+- ls -p is now deprecated in favor of -F or -b. See the help text of
+  the ls command for details.
+- The openssl code has been adjusted to work also with openssl-1.1.
+- The wma decoder and audio format handler now correctly decodes
+  files with unusual block sizes.
+- We now compile with -Wformat-signedness if possible.
+- The touch command now refuses to set an invalid image or lyrics ID.
+- New section on contributing for the user manual.
+- Major simplification of the error subsystem.
+
+Download: [tarball](./releases/paraslash-git.tar.bz2)
+
+-------------------------------------------
+0.4.14 (2016-12-31) "branching oscillation"
+-------------------------------------------
+
+Many fixes for nasty bugs have accumulated over the years since 0.4.13
+was released in 2013. This release contains all of them. It is the
+last v0.4.x release, so v0.4.x is now end of life. The maint branch
+of the git repository has been forwarded to v0.5.7 and will start to
+receive important fixes for 0.5.x, which is now in maintenance mode.
+
+- Fix for the "double encryption bug" with gcrypt, which caused
+  garbage to be sent to stat clients.
+- The "prev" command of para_play now works as expected.
+- Invalid parameters for the begin and end chunk no longer cause
+  the afh receiver of para_recv to segfault.
+- A fix for an assertion that triggered in the FEC slice setup code.
+- Closing the terminal with a running para_gui causes the process to
+  die, rather than using 100% of CPU time.
+- para_gui no longer shows stale information about the previous
+  audio file.
+- A fix for a bug in the fec decoder which caused aborts if the buffer
+  pool filled up.
+- The assertion in the buffer tree code no longer triggers.
+- The "afs_event: table moods, event 0: key already exists in rbtree"
+  error no longer occurs on attribute changes.
+- A NULL pointer dereference in com_sender() has been fixed.
+- The -i option of lsatt works as advertised.
+- Various fixes for the build system.
+
+Downloads:
+[tarball](./releases/paraslash-0.4.14.tar.bz2),
+[signature](./releases/paraslash-0.4.14.tar.bz2.asc)
+
+---------------------------------------
+0.5.6 (2016-07-10) "cascading gradient"
+---------------------------------------
+
+The highlight of this release is the new -m flag for para_afh which
+lets it modify the meta tags of the given audio file(s). This feature
+is supported for all audio formats. Many small cleanups and bug fixes
+not mentioned here have accumulated and are also part of the release.
+
+- 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.
+- New mood methods: image_id and lyrics_id.
+- The manual and this NEWS file have been converted to markdown.
+- Support for the compile-time loglevel feature has been removed.
+- Cleanup of the wma decoder and bitstream code.
+- Improved wide-character support and fixes related to signal handling.
+- para_gui no longer reports 100% playing time at the stream start.
+- Opus cleanups.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.6.tar.bz2),
+[signature](./releases/paraslash-0.5.6.tar.bz2.asc)
+
+--------------------------------------
+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.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.5.tar.bz2),
+[signature](./releases/paraslash-0.5.5.tar.bz2.asc)
+
+------------------------------------------
+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:
+[tarball](./releases/paraslash-0.5.4.tar.bz2),
+[signature](./releases/paraslash-0.5.4.tar.bz2.asc)
+
+---------------------------------------------
+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:
+[tarball](./releases/paraslash-0.5.3.tar.bz2),
+[signature](./releases/paraslash-0.5.3.tar.bz2.asc)
+
+----------------------------------------
+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:
+[tarball](./releases/paraslash-0.5.2.tar.bz2),
+[signature](./releases/paraslash-0.5.2.tar.bz2.asc)
+
+------------------------------------------
+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:
+[tarball](./releases/paraslash-0.5.1.tar.bz2),
+[signature](./releases/paraslash-0.5.1.tar.bz2.asc)
+
+----------------------------------------
+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:
+[tarball](./releases/paraslash-0.5.0.tar.bz2),
+[signature](./releases/paraslash-0.5.0.tar.bz2.asc)
+
+--------------------------------------
+0.4.13 (2013-07-29) "spectral gravity"
+--------------------------------------
+
+One more 0.4.x release before the API-breaking changes for 0.5.0 go
+in. The main features of this release are the ogg/opus audio format,
+and UTF-8 support, but it includes also tons of other improvements
+and fixes all over the place.
+
+- New audio format: ogg/opus.
+- UTF8 support for para_gui and the mp3 audio format handler.
+- Scheduler improvements and fixes.
+- The obsolete gettimeofday() function has been replaced
+  by clock_gettime() on systems which support it.
+- Speed and usability improvements for para_gui.
+- para_client now restores the fd flags of stdin and stdout
+  on shutdown.
+- Improved manual pages.
+- Consistent version strings for all executables.
+- Reduced dependencies on generated files result in fewer
+  recompilations on changes.
+- Performance improvements for the compress filter.
+- Improved downloads web page.
+
+-----------------------------------------
+0.4.12 (2012-12-20) "volatile relativity"
+-----------------------------------------
+The new command line player, the resample filter, ALSA support for
+para_fade, and the improved build system are the highlights of this
+release which probably marks the end of the 0.4.x series.
+
+- The afh receiver and the para_play executable.
+- The resample filter: A sample rate converter based on
+  libsamplerate.
+- The "versions" directory has been removed from the master
+  branch. The tarballs of the old releases are now available
+  in the new "releases" branch.
+- Overhaul of the build system: All generated files are now
+  written to the "build" directory.
+- The modular mixer API and the alsa mixer.
+- Minor fixes for the osx writer.
+
+--------------------------------------
+0.4.11 (2012-07-20) "mutual diversity"
+--------------------------------------
+
+The major feature in this release is the new sideband API for
+client-server communication. This API will be used exclusively starting
+with 0.5.0, which breaks backward compatibility but allows to get rid
+of quite some compatibility code. Other noteworthy changes include
+decoder latency improvements and a long-standing bug fix for the
+ALSA writer.
+
+- Sideband connections: If both para_server and para_client
+  support this feature, data is sent as a multiplexed stream.
+- The --no_default_filters option of para_filter has been
+  removed.
+- Several fixes and latency improvements to various decoders.
+- The ALSA writer now limits the prebuffer time to 500ms.
+- Documentation improvements.
+- Overhaul of the command_util.sh script.
+- Fixes for some minor problems found by the clang analyzer.
+- Compiles (almost) without warnings on gcc-3.
+- Robustness improvements of the buffer tree code.
+
+------------------------------------------
+0.4.10 (2012-03-30) "heterogeneous vacuum"
+------------------------------------------
+
+Nothing earth-shaking in this release, but quite a few usability
+improvements and the usual mix of cleanups and fixes.
+
+- The --no_default_filters option of para_filter has been
+  deprecated. It still works but has no effect and will be
+  removed in the next version.
+- para_gui now prints also the stderr output of the executing
+  command in the bottom window.
+- Cleanup and consolidation of the various wrappers for
+  write(), writev(), send() and friends.
+- The obscure error messages on mmap() failures have been
+  replaced by meaningful messages. This affects mainly
+  para_afh.
+- para_audioc: Cleanups and memory leak fixes.
+- Test 0004-server no longer fails if para_server is not
+  being built.
+- New configure options: --with-id3tag-{headers,libs}.
+
+-------------------------------------
+0.4.9 (2011-12-06) "hybrid causality"
+-------------------------------------
+
+Support for another audio format, interactive mode for para_client
+and para_audiod and many small improvements/fixes all over the place.
+
+- Support for flac, the free lossless audio codec.
+- Fix for an endless loop in the mp3 decoder for certain
+  (corrupt) mp3 files.
+- When executed without specifying a command, para_client
+  and para_audioc start an interactive shell (requires
+  libreadline being installed). The interactive mode offers
+  full tab completion and command line history.
+- autogen.sh now detects a distcc setup and adjusts the
+  parameter for the -j option of make accordingly.
+- Shared memory areas are no longer restricted to 64K. We now
+  detect the maximal size of a shared memory area at runtime.
+- cleanup of the internal uptime API.
+- para_server prefaults the mmapped audio file to avoid
+  delays on slow media.
+- A new test for the test-suite that exercises the
+  communication between para_server and para_audiod.
+- The alsa writer eats up less CPU cycles when configured to
+  use the DMIX plugin.
+- Simplified and unified receiver code.
+- Makefile cleanups.
+- Commands which print a list of matching audio files now
+  emit a meaningful error message if no audio file matched the
+  given pattern(s).
+
+--------------------------------------
+0.4.8 (2011-08-19) "nested assignment"
+--------------------------------------
+
+Gcrypt support, the overhauled osx writer and regex format specifiers
+are the highlights of this release.
+
+- support for libgcrypt as a drop-in replacement for openssl.
+  Run configure --enable-cryptolib=gcrypt to link against
+  libgcrypt. The two crypto implementations are compatible to
+  each other, i.e. a para_client executable linked against
+  gcrypt can connect to para_server linked against libssl
+  and vice versa.
+- Rewrite of the osx writer (output plugin for Mac OS).
+- audiod: The format specifier for receivers, filters and
+  writers is now treated as a regular expression. This allows
+  to replace 5 lines in the config file (one for each audio
+  format) by one single line. See the manual for details.
+- The \*.cmdline.[ch] files are no longer contained in the released
+  tarballs. This reduces the size of the tarballs but requires
+  gengetopt to build the tarball.
+- Compiles cleanly also with llvm/clang.
+- Corrupt mp3 files are handled more gracefully.
+- The alsa writer uses poll fds instead of computing timeouts.
+- Cleanup of the generic writer API.
+- sched: Optimized zero timeouts.
+- vss timeout cleanups.
+- oggdec fixes and improvements.
+
+--------------------------------------
+0.4.7 (2011-06-01) "infinite rollback"
+--------------------------------------
+
+The new ao writer, support for ssh RSA keys and a couple of other
+enhancements.
+
+- Support for ESD, Pulseaudio, AIX, Solaris, IRIX and other
+  platforms through the libao audio library.
+- Support for RSA keys generated with ssh-keygen.
+- configure: improved options for ogg/vorbis/speex.
+- The git version reported by --version always matches HEAD.
+- The autogen script detects the number of processors and
+  runs a parallel make if possible.
+- Major cleanup of the crypto API.
+- Documentation updates.
+
+------------------------------------------
+0.4.6 (2011-03-31) "deterministic entropy"
+------------------------------------------
+
+Lots of ogg/vorbis improvements, the new test suite, enhancements
+for para_gui and a fair amount of other bug fixes.
+
+- For DCCP/OGG streams the audio file header is only sent once
+  at the beginning of the stream rather than periodically
+  every five seconds. This reduces network traffic and the
+  FEC group size.
+- The vorbis comment header is replaced by an empty dummy header
+  before the header is sent over the network. This also results in
+  less network traffic and smaller FEC groups.
+- The new "test" make target allows to perform some sanity checks prior
+  to installing the package.
+- ogg timing fixes and performance improvements
+- Scheduler improvements
+- Proper exit codes for para_write
+- para_gui: New option --theme to select a startup theme. Several
+  other improvements and fixes.
+- aacdec error message cleanups
+- simplified color error handling
+
+--------------------------------------------
+0.4.5 (2010-12-17) "symmetric randomization"
+--------------------------------------------
+
+Bug fixes, internal cleanups and variable-sized FEC slices.
+
+- Contains a fix for an invalid-free-bug in the ogg audio format
+  handler code.
+- Switching off the DCCP sender works again.
+- para_audiod handles crashes of para_server more robustly.
+- Internal scheduler and writer cleanups.
+- Reduced latency due to variable-sized FEC slices.
+- Improved documentation and error diagnostics.
+- The build of para_server is now optional, allowing the build
+  to succeed in case libosl is not installed.
+
+------------------------------------------
+0.4.4 (2010-08-06) "persistent regularity"
+------------------------------------------
+
+Support for yet another audio format, para_write improvements and
+bug fixes.
+
+- Support for the speex codec.
+- Support for sample formats other than 16 bit little endian.
+- error2.h is now created by a perl script which speeds up configure
+  considerably.
+- Fix a bug in the aac decoder which could lead to segfaults in
+  para_filter/para_audiod.
+- Fixes for autoconf-2.66.
+
+----------------------------------------
+0.4.3 (2010-07-05) "imaginary radiation"
+----------------------------------------
+
+Many improvements for the DCCP and the UDP transport, the new user
+manual and the usual mix of bug fixes and internal improvements.
+
+- FEC support for the DCCP sender (Gerrit Renker). The new
+  --dccp_max_slice_size, --dccp_data_slices_per_group and
+  --dccp_slices_per_group options can be used to set the FEC
+  parameters for the DCCP transport.
+- DNS lookups for UDP targets (Gerrit Renker).
+- The new user manual replaces the README, README.afs, REQUIREMENTS
+  and INSTALL documents.
+- Fix an end-of-file detection bug in the oggdec filter.
+- The new nonblock API.
+- Both options of the oggdec filter have been removed.
+- New debug mode for the internal scheduler.
+
+------------------------------------------
+0.4.2 (2010-04-23) "associative expansion"
+------------------------------------------
+
+It's been some time since the last release, but finally here is
+paraslash-0.4.2. The bulk of the changes comes from the new buffer
+tree API, but there are changes all over the tree. Mainly performance
+and usability improvements, but also quite some bug fixes.
+
+- The new buffer tree API.
+- DCCP: Support for CCID negotiation (Gerrit Renker).
+- UDP robustness fixes.
+- The --bufsize option for mp3dec is gone as it no longer makes sense
+  for the new buffer tree API.
+- Fix audible buffer underruns for wma streams.
+- The alsa writer no longer prints meaningless underrun durations.
+- audiod: Defaults work also for udp streams. If no filter is
+  given for an audio format that is received via upd, fecdec is
+  automatically added as the first filter (along with the decoder).
+
+---------------------------------------
+0.4.1 (2009-12-22) "concurrent horizon"
+---------------------------------------
+
+Support for another audio format, minor feature enhancements and lots of bug
+fixes. All fixes that have been accumulated in the maint branch (in particular
+those mentionened in the 0.3.6 release notes) appear in this release as well.
+
+- wma support.
+- new afh option: --human to activate human-readable output.
+- new server/audiod option: --log-timing to print timing information.
+- build system improvements.
+- source code documentation updates.
+
+-------------------------------------
+0.3.6 (2009-12-07) "cubic continuity"
+-------------------------------------
+
+Quite a few bugs have been found and fixed since 0.3.5, so here's
+another 0.3.x release. No new features.
+
+- Always check return value of malloc().
+- ogg vorbis/FEC: Do not write garbage after the audio file header.
+- exit if root privileges could not be dropped.
+- FEC: Fix computation of extra slices.
+- oss: Fix check for empty input buffer.
+- Avoid buffer underruns due to filter chain output buffer constraints.
+- server: Fix assignment of afs_pid.
+- Don't panic if the afs database contains unknown audio formats.
+- http/dccp: Do not send the audio file header twice.
+- FEC: Timing improvements.
+
+----------------------------------------------
+0.4.0 (2009-11-10) "simultaneous independence"
+----------------------------------------------
+
+Two significant changes which require the new version number: The
+improved authentication dialog and the fact that the database code
+has been moved to a library, libosl. To use the new version, you have
+to generate new RSA keys, see INSTALL for details. A shell script is
+provided for conversion of the 0.3 database to the new 0.4 format.
+
+- stronger crypto for client authentication
+- the database code has been moved to a library
+- improved status item handling
+- cleanup of the build system
+- The "-V" option now also prints the git version
+- the new parser-friendly listing mode for the ls and stat commands
+- mandatory rc4 encryption
+- major audio format handler cleanups
+- (id3,...) tags are no longer stored as a combined string in the database
+- new mood methods: artist_matches, title_matches, comment_matches,
+  album_matches, year_maches, year.
+
+--------------------------------------------
+0.3.5 (2009-09-21) "symplectic separability"
+--------------------------------------------
+
+Full client support for \*BSD Unixes, complete re-write of the ogg
+vorbis audio format handler, various improvements all over the place
+and the usual mix of bugfixes. This release marks the end of the 0.3
+series if no serious problems show up.
+
+- the new oss writer (supported on \*BSD and Linux)
+- rewrite of the ogg vorbis audio format handler. It's
+  recommended to replace the chunk tables of existing ogg
+  vorbis files in the afs database by re-adding these files
+  with "add -f".
+- support for netmask subsets (Gerrit Renker)
+- the new prebuffer filter
+- improved signal handling
+- variable fec output buffer size
+- improved FEC timing fixes audible buffer underruns in UDP mode
+- --log_color actually works
+- new ls option: -d (print dates as seconds after the epoch)
+- update to gengetopt 2.22.2
+- support for RSA keys of size > 512 bits
+- new option "mixer_channel" for para_fade
+
+-----------------------------------------
+0.3.4 (2009-05-07) "elliptic inheritance"
+-----------------------------------------
+
+The new udp sender, forward error correction, colored logs and various
+other improvements. As the udp sender does not depend on any special
+libraries, it is built unconditionally.
+
+- The udp sender replaces the ortp sender. The new code uses forward
+  error correction to protect against packet losses. Many thanks to
+  Gerrit Renker for providing ipv6 support.
+- The default port for udp streaming now defaults to 8000, like
+  for the http and the dccp senders/receivers.
+- Loglevels are now specified as symbolic names, e.g.
+  "--loglevel info".
+- improved ipv4 and ipv6 URI parser (Gerrit Renker).
+- para_server/para_audiod: Color support for log messages.
+- new options for mp3dec: --ignore-crc, --bufsize
+- new audiod option: --config-file.
+- gengetopt cleanups.
+- Improved help/man pages: The documentation of para_audiod,
+  para_recv, para_filter and para_write now also contains
+  all options of the available receivers/filters/writers. The
+  man page of para_fade contains a description of the different
+  modes of operation.
+- More source code documentation.
+- vss timing fixes.
+
+--------------------------------------------
+0.3.3 (2008-12-01) "axiomatic perspectivity"
+--------------------------------------------
+
+Internal code cleanups, bug fixes, improved tag handling and the new
+amplification filter.
+
+- para_server uses the generic scheduling code.
+- overhaul of the virtual streaming system.
+- mp3: id3 version 2 support via libid3tag (optional)
+- ogg: vorbis comment support.
+- aac meta info support.
+- mp3 audio format handler cleanups.
+- new filter: "amp" to amplify the amplitude of the audio stream
+- new status item/database entry: amplification. It is
+  used by the amp filter to pre-amplify the audio stream.
+- fix a close-without-open bug in para_write.
+- fix a bug in com_init() which was introduced in 0.3.2.
+- better error diagnostics for para_client.
+
+-----------------------------------------
+0.3.2 (2008-04-11) "probabilistic parity"
+-----------------------------------------
+
+The new para_afh executable, scheduling and documentation improvements.
+
+- new ls option: -lc (list chunk table)
+- new executable: para_afh, the stand-alone audio file handler tool
+- afs commands can send output more than SHMMAX (32MB on Linux). This
+  also reduces the memory usage of commands that produce large amounts
+  of output.
+- major scheduler and audiod cleanups.
+- more detailed and much nicer man pages.
+
+---------------------------------------
+0.3.1 (2008-02-23) "liquid interaction"
+---------------------------------------
+
+A mix of cleanups, bug fixes, improvements, and some new features. No
+significant changes to the new database (osl) code, which is generally
+a good sign.
+
+- Share some similar/duplicate code between the http and the
+  dccp sender.
+- Generic access control lists for paraslash senders.
+- dccp sender: Access control lists, connection limiting and support
+  for the allow,deny,on,off,help sender commands.
+- The default dccp port changed from 5001 to 8000 (suggested by
+  Gerrit Renker).
+- para_server starts even if not all public keys could be loaded.
+- Audiod performance improvements.
+- fix a bug in the "off" command of the http sender.
+- fix some fd and memory leaks.
+- Update to gengetopt-2.22.
+
+-------------------------------------
+0.3.0 (2008-01-12) "solar saturation"
+-------------------------------------
+
+paraslash.0.3.0 -- 'WWDBND --what would databases never do?'.
+
+
+Usually one might expect lots of new features AND a big increase in size
+for a major release like this.
+
+However, paraslash-0.3.0.tar.bz2 is the smallest paraslash tarball
+ever. The decrease in size is mostly due to the removal of some
+graphical tools (which were only quick hacks anyway). But also the
+fact that the mysql code is gone cuts down the size a bit.
+
+Being independent of mysql comes at a cost: The fact that paraslash
+now contains its own database (the object storage layer, osl) increases
+the (stripped) binary size of para_server by ~50K on i386.
+
+- no more restrictions on unique basenames.
+- independent of mysql: The new self-contained object
+  storage layer (osl) replaces the mysql database.
+- New executable para_fsck: Check integrity of osl tables.
+- Lyrics support.
+- Reliable audio file move/rename detection.
+- More portable than ever: Tested on Linux (x86_32, x86_64, sparc64),
+  MacOS (ppc32, x86_32), FreeBSD (x86_32), NetBSD (x86_32) and
+  Solaris (sparc64).
+- the new osl-based audio file selector (afs) replaces the random,
+  the playlist and the mysql selector of paraslash-0.2.x.
+- IPv6 support (thanks to Gerrit Renker).
+- paraslash-0.2.x streams are now called "moods". Writing
+  0.3.x-mood definitions should be both easier and more
+  powerful than writing 0.2.x-stream definitions.
+- para_krell, para_slider, para_para_sdl_gui, para_dbadm have
+  been removed. The world is a better place without them. However,
+  para_gui is still there.
+- afs tracks audio file selection also in playlist mode.
+- few easy-to-use afs commands replace the many not-so-easy-to-use
+  mysql commands (and are available also in playlist mode).
+- Improved error subsystem.
+- The earth-shaking new logo.
+
+-----------------------------------------
+0.2.17 (2007-11-20) "isotropic threshold"
+-----------------------------------------
+
+Mainly bugfixes and cleanups in this version which marks the end of
+the 0.2.x series if no serious bugs show up after the release.
+
+- mysql_selector: fix a locking bug.
+- universal chunk queueing.
+- dccp sender uses chunk queueing if write() returns EAGAIN (thanks
+  to Gerrit Renker).
+- be more carful wrt. signed vs. unsigned argument passing.
+- cleanup error.h and fix some references to invalid error
+  codes.
+- update to gengetopt-2.21.
+- update to ortp-0.13.1.
+- autoconf: extend checks for headers, library functions and
+  compiler characteristics.
+- Fix streaming of large mp3 files.
+- Fix an off-by-one bug in playlist handling.
+
+--------------------------------------
+0.2.16 (2007-04-05) "neural discharge"
+--------------------------------------
+
+The main change in this release is the major audio format handler
+cleanup which removes some similar/duplicate code and makes it easier
+to implement plugins for other audio formats. Of course, the usual mix
+of other improvements/changes/bugfixes also made it into the release.
+
+- simplified audio format handlers (most of the handling functions
+  were moved one layer up to the virtual streaming system).
+- para_server uses mmap to read audio files
+- repositioning of mp3 streams is much faster, in particular for
+  jumping near the end of large mp3 files.
+- permission flags DB_READ,DB_WRITE have been renamed to AFS_READ
+  and AFS_WRITE.
+- fix a bug in para_filter that caused decoding of aac files
+  to start only after a few seconds.
+- fix osx_writer hangs
+- simplified dccp code (thanks to Gerrit Renker)
+- the compress filter works also on big endian systems (ppc)
+
+-----------------------------------------
+0.2.15 (2007-02-16) "inductive resonance"
+-----------------------------------------
+
+Minor improvements, more documentation and a bunch of bug fixes.
+
+- para_server: The server.users file is only read once on server
+  startup rather than for each connection
+- mp3dec: Fix decoding of corrupt mp3 files
+- afs (audio file sender) is now called vss (virtual streaming
+  system). Consequently, the permission flags specified in
+  ~/.paraslash/server.users have also changed: AFS_READ and AFS_WRITE
+  become VSS_READ and VSS_WRITE respectively.
+- para_audiod/para_filter: Fix a bug that caused the last chunk
+  of audio data not being written under certain circumstances
+- audiod: compute the difference of server time and local time
+  correctly
+- para_server/para_audiod: Fix some memory leaks
+- documentation improvements
+- configure.ac: fix checks for para_krell
+- new man pages
+
+-------------------------------------------
+0.2.14 (2006-10-15) "transient singularity"
+-------------------------------------------
+
+The only major enhancement of this version is the osx writer which completes
+the Mac OS Port and was originally planned already for 0.2.13 but had to wait
+until now for reasons beyond the scope of this changelog entry.
+
+- new output plugin for Mac Os: the osx writer
+- rename configure command line options from --enable-xxx-headers to
+  --with-xxx-headers and  --enable-xxx-libs to --with-xxx-libs
+- configure: new command line options: --with-mad-headers,
+  --with-mad-libs, --with-oggvorbis-headers, and --with-oggvorbis-libs
+- some robustness fixes
+- dymamic audio format recognition for audiod
+- para_server: new command line option: --autoplay_delay
+- para_audiod: new command line option: --clock_diff_count
+
+---------------------------------------
+0.2.13 (2006-07-14) "sonic convolution"
+---------------------------------------
+
+A bunch of new features and core changes.
+
+- the new paraslash scheduler, short and sweet.
+- Support for m4a/mp4 files via the new aac audio format
+  handler/filter (requires libfaad).
+- each writer has its own command line parser, just like
+  para_recv and para_filter.
+- para_client and para_audioc use the error subsystem
+- writers are integrated in para_audiod (currently linux-only)
+- para_client is integrated in para_audiod
+- random/playlist selector: improved info strings
+- new audiod commands: tasks, kill
+- update to libortp-0.10.1
+- para_fade: wake time defaults to 8 hours from now
+- update to autoconf-2.60
+
+------------------------------------------
+0.2.12 (2006-05-12) "oriented abstraction"
+------------------------------------------
+
+Many user-visible changes in this release and lots of new
+features:
+
+- the new optional dccp sender/receiver. It uses the datagram
+  congestion control protocol. You'll need a fairly new kernel
+  for this.
+- paraslash works on Mac OS X (thanks to Gerd Becker)
+- para_play renamed to para_write
+- modular output plugin design (writers) for para_write
+- new file_writer output plugin for para_write
+- compress filter speed improvements
+- update to libortp-0.9.1
+- update to gengetopt-2.17rc
+- para_client no longer depends on libreadline (as the
+  code for the interactive mode was removed).
+- gcc-2-95 is no longer a supported compiler. It may still
+  work, but it gets no more testing.
+- the tarball no longer contains the screenshot images which
+  reduces its size quite a bit.
+- configure: new command line options: --enable-mysql-headers
+  and --enable-mysql-libs
+
+------------------------------------
+0.2.11 (2006-03-11) "atomic duality"
+------------------------------------
+
+Here it is, the first paraslash release developed with git. There
+are fairly many user-visible changes in this release. As two out of
+the three "database tools" of paraslash don't use a database at all,
+they are now called "audio file selectors" instead.
+
+- the cdt command (change database tool) becomes chs (change
+  selector)
+- no more colon separators: The syntax of some options of
+  para_audiod and para_filter have changed. Use --help for
+  more info (and some examples).
+- update to gengetopt-2.16 (thanks to Lorenzo Bettini)
+- switch from cvs to git (should've done that earlier)
+- the new ipc subsystem
+- new audio file selector: playlist
+- para_server: the dopey selector is now called "random",
+  and is the default selector. Use the --selector option to
+  choose another selector at startup, or the chs command to
+  change the selector at runtime.
+- X86_64 fixes (thanks to Steffen Klassert)
+- para_play fixes
+
+--------------------------------------
+0.2.10 (2006-02-17) "cyclic attractor"
+--------------------------------------
+
+Huge documentation update, a scrollable window for para_gui, ortp
+improvements, and of course many small fixes not mentioned here.
+The diffstat below is rather misleading as most insertions are due
+to the new source documentation.
+
+- autoconf cleanup
+- para_server also uses the new error subsystem
+- lots of new documentation (UTSL)
+- gui improvements:
+       - keysyms for cursor keys and for next/previous page keys
+       - scrollable output window
+       - new internal commands: scroll up/down, page up/down
+       - fix color of command output.
+- ortp: the --chunk_time and --header flags are no longer needed
+for para_recv/para_audiod as this information is now encoded in
+each rtp packet sent by para_server.
+
+-------------------------------------------
+0.2.9 (2006-01-24) "progressive turbulence"
+-------------------------------------------
+
+Internal audiod receivers/filters, the new error subsystem and
+a lot of small improvements.
+
+- para_recv and para_filter are integrated into the para_audiod
+  binary, i.e. audiod no longer spawns a new process for
+  each receiver/filter. As para_recv and para_filter might be
+  useful as standalone programs, they still get built (linked
+  against the same object files that are also used for audiod).
+- further ortp timing improvements should reduce the CPU usage
+  of the ortp receiver.
+- improved audio grabbing. The 'grab' command of para_audiod
+  has its own set of command line options. Read the output of
+  "para_audioc help grab" for more info.
+- oggdec: configurable input prebuffer size.
+- the new error subsystem gives better error diagnostics
+  and reduces code size.
+
+-----------------------------------------
+0.2.8 (2006-01-02) "dynamic accumulation"
+-----------------------------------------
+
+The new modular filter design and the para_play-hangs bugfix.
+
+- new executable: para_filter. It combines para_mp3dec,
+  para_oggdec and para_compress. It also adds a further filter
+  type, wav, that just inserts a wave header at the desired point
+  of the filter chain. All 'piping' is done in-memory (i.e. no
+  read/write operations are used).
+- para_play: fix a stupid bug that caused it to hang under
+  certain circumstances.
+
+-------------------------------------------
+0.2.7 (2006-12-27) "transparent invariance"
+-------------------------------------------
+
+Not many user-visible changes but a fair amount of internal improvements.
+
+- The http sender buffers data if it can not be sent
+  out immediately (because the socket is not writable). This
+  should prevent para_server from shutting down the connection
+  too early on a loaded network.
+- para_play also prebuffers data if it is told to start at a
+  future time by the --start_time option.
+- The return of para_recv: It combines para_ortp_recv and
+  para_http_recv. Use the --receiver option to switch between
+  the two. para_recv builds without libortp, but contains
+  only the http receiver in this case.
+- update to ortp 0.8.1. As this ortp release contains incompatible
+  changes, para_recv-0.2.7 won't link against older ortp libs.
+- improved ortp timings.
+- use of gcc-extensions that #define away for non-gcc and
+  gcc < 3.0.
+
+-------------------------------------------
+0.2.6 (2005-10-29) "recursive compensation"
+-------------------------------------------
+
+Transparent session encryption (uses openssl's Alleged RC4 cipher),
+the internal find command and several other improvements and cleanups.
+
+- Encrypt the session if encryption is requested by the client
+  (default for para_client 0.2.6). This is backwards
+  compatible, so older clients can still connect to para_server
+  0.2.6. Use the new client option --plain to request an
+  uncrypted session (off by default, must be set to on in
+  order to connect to para_server 0.2.x with 0 <= x <= 5).
+- para_server uses an internal function to locate audio files
+  rather than calling find(1). The server option
+  --mysql_audio_file_dir replaces --mysql_find_cmd.
+- documentation update
+- man pages
+- header file cleanup
+- para_client code cleanup
+- para_gui: faster display of output of display commands
+
+------------------------------------------
+0.2.5 (2005-10-13) "aggressive resolution"
+------------------------------------------
+
+This release adds internal senders, i.e. no more external programs are
+spawned for sending out the audio data. There are two different senders
+available: The http sender and the ortp sender (former para_send which
+is no longer needed).
+
+The new sender code has a plugin-like design so it can be easily
+extended should there be be any future need for supporting another
+network streaming protocol. All senders are completely independent of
+each other. In particular, the http and the ortp sender can operate
+in parallel.
+
+- new server command: sender to control senders at runtime.
+  Read the output of "para_server -h" and "para_client help
+  sender" for more information.
+- para_recv renamed to para_ortp_recv
+- new executable: para_http_recv, a simple command line
+  http receiver.
+- major afs/mp3/ogg code simplifications due to internal
+  senders.
+- ogg timing improvements
+- fix several minor memory leaks (found by valgrind)
+- empty stream definitions work again
+- com_ne(): ignore errors on remove
+- audiod: fix segfault on server restart
+
+---------------------------------------
+0.2.4 (2005-09-21) "toxic anticipation"
+---------------------------------------
+
+Several small improvements, fixes and the new grab command.
+
+- audiod:
+       - new command: "grab" to grab the output of the stream reader
+         or any filters. Read the output of "para_audioc help grab"
+         for more information.
+       - fix memory leak
+       - code cleanup
+- audioc: new command line option: --bufsize to specify a
+  buffer size different from the default size 8192.
+- improved error diagnostics for para_play.
+- new configure option: --enable-ssldir so search for openssl in
+  non-standard places
+- sdl_gui: Make it look nice again for 1024x768
+- server: report total size of memory allocated with sbrk by malloc,
+  new command line option: --announce_time
+
+-----------------------------------------
+0.2.3 (2005-09-01) "hydrophilic movement"
+-----------------------------------------
+
+Two new executables and major feature enhancements.
+
+- audiod filters: It is now possible to specify arbitrary many
+  (including none) filters for each supported audio
+  format. This can be used e.g. for normalizing volume,
+  transforming or grabbing the audio stream, or for using
+  visualizers. Read the output of "para_audiod -h" for the
+  syntax of the new --filter_cmd option.
+- new executable: para_play, a tiny alsa player. It
+  can play wave files or raw pcm (16 bit little endian)
+  from stdin.
+- new executable: para_compress, a dynamic range compressor
+  intended to keep audio output at a consistent volume. Derived
+  from [AudioCompress](http://trikuare.cx/code/AudioCompress.html).
+- audiod: New option: --stream_delay. This can be used in
+  a local network to syncronize the audio output of all
+  clients that play the same stream.
+
+------------------------------------------
+0.2.2 (2005-08-19) "tangential excitation"
+------------------------------------------
+
+Mostly internal changes in this release, but also some new commands
+for the mysql database tool.
+
+- cleanup exec.c, fix para_exec bug
+- compile time loglevel (log messages below the given level
+  won't be compiled in, which reduces the size of the
+  resulting binaries)
+- new log macros that shorten the size of the source code.
+- workaround a gcc-4.1 bug (?) that caused send_cred_buffer()
+  to send only zeros. With this workaround, para_audioc works
+  again.
+- avoid gcc-4 warning: conflicting types for built-in function 'clog'
+- new mysql commands: "rm" (remove entry), "mv" (rename entry) "ne"
+  (new entry), "snp" (set numplayed). Read the manual for more
+  information.
+
+---------------------------------------
+0.2.1 (2005-08-15) "surreal experience"
+---------------------------------------
+
+Here comes paraslash-0.2.1. It contains a couple of new features and,
+surprise, only minor bug fixes.
+
+- kill noisy mp3 debug message
+- cleanup of the build system
+- para_server and para_client directly use the crypto routines
+  of the openssl library rather than invoking the openssl command
+  line utitlity
+- server/audiod: new option --user to switch to the given user
+  when invoked as root. Read the output of "para_server -h" for
+  more information.
+- gui/sdl_gui: new option --stat_cmd to be used to retrieve the
+  status. Default: "para_audioc stat"
+- sdl_gui: new option --pic_cmd to be used to download the picture.
+  Default: "para_client pic"
+- audiod: 5 slots ought to be enough for everybody
+- audiod: new status item: Uptime, kill hup command
+
+------------------------------------------
+0.2.0 (2005-08-06) "distributed diffusion"
+------------------------------------------
+
+After several month of increased development activity, paraslash-0.2.0
+has arrived. It contains many new features and is much more
+self-contained than the old 0.1.x series. Enjoy!
+
+- para_server: fix hang on song change and crash on sighup.
+  Speed up mysql queries. The DIR_LIKE macro is gone.
+- new executables: para_audiod, the local audio daemon that
+  starts playback (uses SCM_CREDENTIALS socket magic) and
+  para_audioc, the corresponding client.
+- new executables: para_mp3dec/para_oggdec, two really teensy
+  decoders. para_mp3dec is based on libmad, para_oggdec requires
+  libvorbisfile.
+- ovsend/ovrecv are capable of streaming ogg as well as mp3, so
+  they are now called para_send and para_recv respectively.
+- documentation updates
+- para_gui is themable. For now there is the default theme that
+  looks as before and the simple theme: blue and easy.
+- gui: audio streaming is now handled by audiod. Time display shows
+  playback time rather than streaming time
+- slider: update to libzmw-0.2.0
+- para_krell: fix crash on server shutdown
+- switch from gzip to bzip2
+
+----------------------------------------
+0.1.7 (2005-04-18) "melting penetration"
+----------------------------------------
+
+The main change in this release is clearly the oggvorbis rewrite,
+but there are also lots of smaller changes. If you intend to use both
+the mp3 and the ogg plugin, it is recommended to use software mixing,
+e.g. the dmix plugin which is provided by ALSA.
+
+- new executables: para_ovsend and para_ovrecv for sending/receiving
+  oggvorbis files via rtp. Requires the open rtp library. Get it at
+  http://www.linphone.org/ortp/
+- rewrite of the ogg_vorbis core code
+- configure detects libzmw and, if detected, includes
+  para_slider to the list of binaries to be built by make
+- server stream writers read from their associated fifo rather
+  than from stdin
+- slider: two new sliders, lastplayed and numplayed
+- fix nasty double free bug which caused random segfaults in case of
+  mp3 files with invalid header information
+- gui: new command line option: --stream_timeout=seconds  to
+  deactivate a slot if it is idle for that many seconds (default=`5')
+- diffstats
+
+---------------------------------------
+0.1.6 (2005-03-05) "asymptotic balance"
+---------------------------------------
+
+Only little user-visible changes in this release. Mainly bugfixes and
+core code cleanup. This is probably the most stable version ever if you
+stick to mp3...
+
+- fix several memory leaks
+- rename default name of mysql database from "music" to "paraslash".
+  Use para_server's  --mysql_database option if you do not want to
+  rename your old database.
+- rework ogg vorbis code
+- make update command work on mysql servers with LOCAL_INFILE
+  disabled
+- gui: improved stream I/O (slots)
+- simplified audio format API
+- para_pob_ogg is gone
+
+------------------------------------
+0.1.5 (2004-12-31) "opaque eternity"
+------------------------------------
+
+Let's slide gently into the new year.
+
+- new: para_slider (not built automatically, type "make
+  para_slider" to build). A toy for those who always felt that
+  creating stream definitions is difficult. See screenshots,
+  README and FEATURES for more info.
+- improved signal handling. Fixes server segfault on SIGHUP
+  for linux kernels newer than Aug 24 2004 and makes para_gui
+  race-free.
+- reload database tool on SIGHUP
+- improved help message for sl
+- do not log "broken pipe" messages as errors. They are
+  perfectly ok.
+- fix wrong error message on permission errors
+
+-----------------------------------------
+0.1.4 (2004-12-19) "tunneling transition"
+-----------------------------------------
+
+Bugfix release. As expected, 0.1.3 introduced a bunch of new bugs.
+Hopefully, most of them got wiped out with this release. Some
+enhancements went also in.
+
+- improved error diagnostics for all commands
+- stradd/picadd: overwrite previous contents if entry already
+  exists, rather than returning errors
+- stradd: use current stream if invoked without args
+- faster (and hopefully more stable) ogg-vorbis handling
+- para_krell: reap children to avoid zombie-flooding in case
+  no server is running
+- si: report also server pid
+- server: don't busy-loop if dbtool reports only invalid files.
+- gui: CTRL+C works again, fix stream_read command line option
+- fix pic_add, hist
+- fix mysql dbtool startup in case no database exists
+- many small fixes and cleanups
+
+---------------------------------------
+0.1.3: (2004-12-10) "vanishing inertia"
+---------------------------------------
+
+Starting from this release, the database tools are integrated in the
+server binary. This decreases server startup time, reduces code size
+and speeds up database commands. However, the layout of the underlying
+mysql database changed only slightly and 0.1.3 should be backwards
+compatible in that respect.
+
+Visible changes:
+
+- If mysql is not detected at compile time, or fails to init
+  at runtime, fall back to the dopey database tool which should
+  always work.
+- para_dbtool and dbtool.conf are gone. All mysql specific
+  options are read from server.conf and are prefixed by 'mysql_'.
+- new command: cdt (change database tool)
+- new command line option: dbtool (choose startup database tool)
+- The name of current stream is now stored in the database,
+  so paraslash remembers its current stream when restarted.
+- new command: csp (change stream and play)
+- para_gui also reports current database tool and server uptime
+
+-------------------------------------------
+0.1.2: (2004-11-28) "spherical fluctuation"
+-------------------------------------------
+
+Point release before the big dbtool changes go in.
+
+- dbtool: rename ca to cam (copy all meta data). It now also
+  copies numplayed and lastplayed time as well as the picture
+  id.
+- fix endless-loop-bug caused by mp3 files with invalid header
+
+-----------------------------------------
+0.1.1: (2004-11-05) "floating atmosphere"
+-----------------------------------------
+
+- gkrellm plugin
+- new dbtool command: mbox. Browse your sound-file collection
+  with your favorite mail reader.
+- several small fixes
+
+-------------------------------------
+0.1.0: (2004-10-22) "rotating cortex"
+-------------------------------------
+
+- fix logging bug for loglevel > VERBOSE
+- fix skip command
+- correct timings for vbr mp3s
+- modular audio format support
+- ogg-vorbis support (experimental)
+- new server option: autoplay
+
+-----------------------------------------
+0.0.99: (2004-07-25) "harmonic deviation"
+-----------------------------------------
+
+- rename projectname from icc to paraslash (play, archive, rate
+  and stream large audio sets happily)
+- paraslash is no longer restricted to one particular audio
+  streaming software
+- new dbtool commands (stradd, strq, strdel) for easy stream
+  managment w/o configuration file. That obsoletes stream_defs
+  file/config option for dbtool.
+- picadd accepts jpeg data from stdin
+- new server commands: ps (select previous stream), sc (song change)
+- new default pictures for sdl_gui
+- gui: new key_map option for binding commands and internal
+  functions to arbitrary keys, nice help screen, rip out
+  soundcard/linux specific stuff, avoid noise artefacts while jumping,
+  show silly logo on startup
+- new executables: para_fade for fading volume, para_dbadm for
+  manipulating attributes
+- cdb adds _all_ tables to mysql database
+- revised and beautified documentation
+- sample dbtool rewritten in C
+- autoconf
+
+---------------------------------------------
+0.0.98: (2003-12-26) "incremental smoothness"
+---------------------------------------------
+
+- kick icecast in favour of poc. That removes some races and reduces
+  core code considerably.
+- cbr/vbr is displayed by stat and gui/sdl_gui. New status flags
+  give finer info on afs' status.
+- gui can start decoder (see config options). Further new gui
+  commands: refresh (^L), jmp (F1-F10)
+- gui rereads conf on SIGUSR1 instead of SIGHUP. SIGHUP
+  terminates gui. This fixes dead instances consuming memory
+  continuously.
+- new dbtool command: verb for sending verbatim sql queries.
+- fix pid_list races (by removing pid_list)
+- codename funnies
+
+--------------------
+0.0.97: (2003-10-26)
+--------------------
+
+- installation prefix now defaults to /usr/local
+- new commands for gui: snozze, sleep and reread config
+- config file for gui and sdl_gui
+- fix problems with filenames containing funny characters
+  (reported by Thomas Forell)
+- improved signal handling for gui, now it rereads conf on SIGHUP
+- new dbtool command: cdb (create database)
+- switch from argtable to gengetopt
+- major code cleanup and speed improvements
+- fix several potential buffer overflows
+- many small fixes and cleanups
+
+-------------------
+0.0.96 (2003-08-30)
+-------------------
+
+- easy stream_defs syntax
+- sdl_gui can display images associated to the file being played
+- Major feature enhancements for icc_gui including dynamic text
+  placement and the top/bottom window design
+- vrfy/clean now also checks for NULL values in attributes as
+  well as for invalid picture pointers
+- fix long outstanding case sensitivity bug
+- many small fixes and cleanups
+
+-------------------
+0.0.95 (2003-06-29)
+-------------------
+
+- sdl gui runs much faster
+- new dbtool command: ca (copy attributes)
+- count and display number of times the song has been played
+- new feature: scoring
+- command line options for sdl_gui
+- simpler syntax of streams file
+- decrease network traffic of stat
+- fix zombie bug
+- many small fixes and cleanups
+
+-------------------
+0.0.94 (2003-05-04)
+-------------------
+
+- new server command: ns (next stream)
+- new icc_gui command: c (change stream)
+- internal mp3info
+- stat shows also id3 tag info
+- new sdl based gui
+- log flodding bug fixed
+- many small fixes and cleanups
+
+-------------------
+0.0.93 (2003-03-28)
+-------------------
+
+- colors for icc_gui
+- icc_gui sets volume directly (linux only)
+- proper locking that fixes some races
+- fix security bug that caused commands to be executed even
+  with unsufficient permissions
+- new command: hup to make all servers reread their configuration file
+- icecast meta data streaming
+- many small fixes and cleanups
diff --git a/README b/README
index e991b7340e61bb2bee929fe145e39b0dee32bf8d..d8a545fc7abd744e6df57c51638dee607574e69e 100644 (file)
--- a/README
+++ b/README
@@ -1,26 +1,13 @@
-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/
+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..eeed252837570b75f055743f87664a1d8b07125d 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.
  */
@@ -9,12 +9,6 @@
 #include <neaacdec.h>
 
 NeAACDecHandle aac_open(void);
-int aac_find_esds(unsigned char *buf, size_t buflen, size_t *skip,
+int aac_find_esds(char *buf, size_t buflen, size_t *skip,
                unsigned long *decoder_length);
-ssize_t aac_find_entry_point(unsigned char *buf, size_t buflen, size_t *skip);
-
-static inline unsigned aac_read_int32(unsigned char *buf)
-{
-       uint8_t *d = (uint8_t*)buf;
-       return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
-}
+ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip);
index 2d04695a84305972c6a4d87b1aef77457a8606ad..1c7fd706f0a7c9fe8c9d4d8db43846be18a9304d 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 "portable_io.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)
+static int aac_find_stsz(char *buf, size_t buflen, off_t *skip)
 {
        int i;
 
        for (i = 0; i + 16 < buflen; i++) {
-               unsigned char *p = buf + i;
+               char *p = buf + i;
                unsigned sample_count, sample_size;
 
                if (p[0] != 's' || p[1] != 't' || p[2] != 's' || p[3] != 'z')
                        continue;
                PARA_DEBUG_LOG("found stsz@%d\n", i);
                i += 8;
-               sample_size = aac_read_int32(buf + i);
-               PARA_DEBUG_LOG("sample size: %d\n", sample_size);
+               sample_size = read_u32_be(buf + i);
+               PARA_DEBUG_LOG("sample size: %u\n", sample_size);
                i += 4;
-               sample_count = aac_read_int32(buf + i);
+               sample_count = read_u32_be(buf + i);
                i += 4;
-               PARA_DEBUG_LOG("sample count: %d\n", sample_count);
+               PARA_DEBUG_LOG("sample count: %u\n", sample_count);
                *skip = i;
                return sample_count;
        }
        return -E_STSZ;
 }
 
-static int atom_cmp(const unsigned char *buf1, const char *buf2)
+static int atom_cmp(const 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])
+static int read_atom_header(char *buf, uint64_t *subsize, char type[5])
 {
-       int i;
-       uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+       uint64_t size = read_u32_be(buf);
 
        memcpy(type, buf + 4, 4);
        type[4] = '\0';
@@ -72,13 +64,12 @@ static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char
        }
        buf += 4;
        size = 0;
-       for (i = 0; i < 8; i++)
-               size |= ((uint64_t)buf[i]) << ((7 - i) * 8);
+       size = read_u64_be(buf);
        *subsize = size;
        return 16;
 }
 
-static char *get_tag(unsigned char *p, int size)
+static char *get_tag(char *p, int size)
 {
        char *buf;
 
@@ -91,12 +82,12 @@ static char *get_tag(unsigned char *p, int size)
        return buf;
 }
 
-static void read_tags(unsigned char *buf, size_t buflen, struct afh_info *afhi)
+static void read_tags(char *buf, size_t buflen, struct afh_info *afhi)
 {
-       unsigned char *p = buf;
+       char *p = buf;
 
        while (p + 32 < buf + buflen) {
-               unsigned char *q, type1[5], type2[5];
+               char *q, type1[5], type2[5];
                uint64_t size1, size2;
                int ret, ret2;
 
@@ -111,23 +102,23 @@ 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;
        }
 }
 
-static void read_meta(unsigned char *buf, size_t buflen, struct afh_info *afhi)
+static void read_meta(char *buf, size_t buflen, struct afh_info *afhi)
 {
-       unsigned char *p = buf;
+       char *p = buf;
 
        while (p + 4 < buf + buflen) {
 
@@ -140,15 +131,14 @@ static void read_meta(unsigned char *buf, size_t buflen, struct afh_info *afhi)
        }
 }
 
-static void aac_get_taginfo(unsigned char *buf, size_t buflen,
-               struct afh_info *afhi)
+static void aac_get_taginfo(char *buf, size_t buflen, struct afh_info *afhi)
 {
        int i;
        uint64_t subsize;
-       unsigned char type[5];
+       char type[5];
 
        for (i = 0; i + 24 < buflen; i++) {
-               unsigned char *p = buf + i;
+               char *p = buf + i;
                if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
                        continue;
                PARA_INFO_LOG("found metadata at offset %d\n", i);
@@ -162,7 +152,7 @@ static void aac_get_taginfo(unsigned char *buf, size_t buflen,
 }
 
 static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
-               unsigned char *map, size_t numbytes)
+               char *map, size_t numbytes)
 {
        int ret, i;
        size_t sum = 0;
@@ -172,12 +162,12 @@ 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)
                        break;
-               sum += aac_read_int32(map + skip);
+               sum += read_u32_be(map + skip);
                afhi->chunk_table[i] = sum;
                skip += 4;
 //             if (i < 10 || i + 10 > afhi->chunks_total)
@@ -187,7 +177,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 +188,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)
@@ -220,32 +210,33 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        unsigned char channels = 0;
        mp4AudioSpecificConfig mp4ASC;
        NeAACDecHandle handle = NULL;
-       unsigned char *umap = (unsigned char *) map;
 
-       ret = aac_find_esds(umap, numbytes, &skip, &decoder_len);
+       ret = aac_find_esds(map, numbytes, &skip, &decoder_len);
        if (ret < 0)
                goto out;
-       aac_get_taginfo(umap, numbytes, afhi);
+       aac_get_taginfo(map, numbytes, afhi);
        handle = aac_open();
        ret = -E_AAC_AFH_INIT;
-       if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels))
+       if (NeAACDecInit(handle, (unsigned char *)map + skip, decoder_len,
+                       &rate, &channels))
                goto out;
        if (!channels)
                goto out;
        PARA_DEBUG_LOG("rate: %lu, channels: %d\n", rate, channels);
        ret = -E_MP4ASC;
-       if (NeAACDecAudioSpecificConfig(umap + skip, numbytes - skip, &mp4ASC))
+       if (NeAACDecAudioSpecificConfig((unsigned char *)map + skip,
+                       numbytes - skip, &mp4ASC))
                goto out;
        if (!mp4ASC.samplingFrequency)
                goto out;
-       ret = aac_compute_chunk_table(afhi, umap, numbytes);
+       ret = aac_compute_chunk_table(afhi, map, numbytes);
        if (ret < 0)
                goto out;
        skip = ret;
        ret = aac_set_chunk_tv(afhi, &mp4ASC, &afhi->seconds_total);
        if (ret < 0)
                goto out;
-       ret = aac_find_entry_point(umap + skip, numbytes - skip, &skip);
+       ret = aac_find_entry_point(map + skip, numbytes - skip, &skip);
        if (ret < 0)
                goto out;
        afhi->chunk_table[0] = ret;
@@ -263,7 +254,69 @@ out:
        return ret;
 }
 
-static const char* aac_suffixes[] = {"m4a", "mp4", NULL};
+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 * const aac_suffixes[] = {"m4a", "mp4", NULL};
 /**
  * the init function of the aac audio format handler
  *
@@ -273,4 +326,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..812c742cf668e7ad67e6f5b502fbe6c93829f0a3 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.
  */
@@ -13,6 +13,7 @@
 #include "para.h"
 #include "aac.h"
 #include "error.h"
+#include "portable_io.h"
 
 /**
  * Get a new libfaad decoder handle.
@@ -31,7 +32,7 @@ NeAACDecHandle aac_open(void)
        return h;
 }
 
-static unsigned long aac_read_decoder_length(unsigned char *buf, int *description_len)
+static unsigned long aac_read_decoder_length(char *buf, int *description_len)
 {
        uint8_t b;
        uint8_t numBytes = 0;
@@ -59,31 +60,31 @@ static unsigned long aac_read_decoder_length(unsigned char *buf, int *descriptio
  *
  * \return positive on success, negative on errors
  */
-int aac_find_esds(unsigned char *buf, size_t buflen, size_t *skip,
+int aac_find_esds(char *buf, size_t buflen, size_t *skip,
                unsigned long *decoder_length)
 {
        size_t i;
 
        for (i = 0; i + 4 < buflen; i++) {
-               unsigned char *p = buf + i;
+               char *p = buf + i;
                int description_len;
 
                if (p[0] != 'e' || p[1] != 's' || p[2] != 'd' || p[3] != 's')
                        continue;
                i += 8;
                p = buf + i;
-               PARA_INFO_LOG("found esds@%zu, next: %x\n", i, *p);
+               PARA_INFO_LOG("found esds@%zu, next: %x\n", i, (unsigned)*p);
                if (*p == 3)
                        i += 8;
                else
                        i += 6;
                p = buf + i;
-               PARA_INFO_LOG("next: %x\n", *p);
+               PARA_INFO_LOG("next: %x\n", (unsigned)*p);
                if (*p != 4)
                        continue;
                i += 18;
                p = buf + i;
-               PARA_INFO_LOG("next: %x\n", *p);
+               PARA_INFO_LOG("next: %x\n", (unsigned)*p);
                if (*p != 5)
                        continue;
                i++;
@@ -108,19 +109,19 @@ int aac_find_esds(unsigned char *buf, size_t buflen, size_t *skip,
  * \return the position of the first entry in the table on success,
  * -E_STCO on errors.
  */
-ssize_t aac_find_entry_point(unsigned char *buf, size_t buflen, size_t *skip)
+ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip)
 {
        ssize_t ret;
        size_t i;
 
        for (i = 0; i + 20 < buflen; i++) {
-               unsigned char *p = buf + i;
+               char *p = buf + i;
 
                if (p[0] != 's' || p[1] != 't' || p[2] != 'c' || p[3] != 'o')
                        continue;
                PARA_INFO_LOG("found stco@%zu\n", i);
                i += 12;
-               ret = aac_read_int32(buf + i); /* first offset */
+               ret = read_u32_be(buf + i); /* first offset */
                i += 4;
                PARA_INFO_LOG("entry point: %zd\n", ret);
                *skip = i;
index 3ff90834c62bb75f179e5778ef02f6d23feb0d09..5725ce043089361092ddb68e05bb3c60fcf66801 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,13 +80,13 @@ 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;
-       unsigned char *p, *inbuf, *outbuffer;
+       char *p, *inbuf, *outbuffer;
        char *btr_buf;
        size_t len, skip, consumed, loaded;
 
@@ -97,7 +97,7 @@ next_buffer:
        if (ret == 0)
                return 0;
        btr_merge(btrn, fn->min_iqs);
-       len = btr_next_buffer(btrn, (char **)&inbuf);
+       len = btr_next_buffer(btrn, &inbuf);
        len = PARA_MIN(len, (size_t)8192);
        consumed = 0;
        if (!padd->initialized) {
@@ -106,7 +106,7 @@ next_buffer:
                ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
                if (ret < 0) {
                        PARA_INFO_LOG("%s\n", para_strerror(-ret));
-                       ret = NeAACDecInit(padd->handle, inbuf,
+                       ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
                                len, &rate, &channels);
                        PARA_INFO_LOG("decoder init: %d\n", ret);
                        if (ret < 0) {
@@ -120,14 +120,14 @@ next_buffer:
                        consumed += skip;
                        p = inbuf + consumed;
                        ret = -E_AACDEC_INIT;
-                       if (NeAACDecInit2(padd->handle, p,
+                       if (NeAACDecInit2(padd->handle, (unsigned char *)p,
                                        padd->decoder_length, &rate,
                                        &channels) != 0)
                                goto out;
                }
                padd->sample_rate = rate;
                padd->channels = channels;
-               PARA_INFO_LOG("rate: %u, channels: %d\n",
+               PARA_INFO_LOG("rate: %u, channels: %u\n",
                        padd->sample_rate, padd->channels);
                padd->initialized = 1;
        }
@@ -158,8 +158,8 @@ next_buffer:
        p = inbuf + consumed;
        //PARA_CRIT_LOG("consumed: %zu (%zu + %zu), have: %zu\n", padd->consumed_total + consumed,
        //      padd->consumed_total, consumed, len - consumed);
-       outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p,
-               len - consumed);
+       outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info,
+               (unsigned char *)p, len - consumed);
        if (padd->frame_info.error) {
                int err = padd->frame_info.error;
                ret = -E_AAC_DECODE;
@@ -171,7 +171,7 @@ next_buffer:
                        goto success;
                }
                PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(err));
-               PARA_ERROR_LOG("consumed: %zu + %zd + %lu\n",
+               PARA_ERROR_LOG("consumed: %zu + %zu + %lu\n",
                        padd->consumed_total, consumed,
                        padd->frame_info.bytesconsumed);
                if (consumed < len)
diff --git a/acl.c b/acl.c
index 60f835aefc0e2c777dae34e1a0b80fb371e0268e..560ff9999d191f418c35695f4a6629b45436f49f 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;
 
@@ -81,7 +87,7 @@ static void acl_add_entry(struct list_head *acl, char *addr, int netmask)
 
        inet_pton(AF_INET, addr, &ai->addr);
        ai->netmask = netmask;
-       PARA_INFO_LOG("adding %s/%i to access list\n", addr, ai->netmask);
+       PARA_INFO_LOG("adding %s/%u to access list\n", addr, ai->netmask);
        para_list_add(&ai->node, acl);
 }
 
@@ -103,7 +109,7 @@ static void acl_del_entry(struct list_head *acl, char *addr, unsigned netmask)
 
                if (v4_addr_match(to_delete.s_addr, ai->addr.s_addr,
                                        PARA_MIN(netmask, ai->netmask))) {
-                       PARA_NOTICE_LOG("removing %s/%i from access list\n",
+                       PARA_NOTICE_LOG("removing %s/%u from access list\n",
                                        addr, ai->netmask);
                        list_del(&ai->node);
                        free(ai);
@@ -125,7 +131,7 @@ char *acl_get_contents(struct list_head *acl)
        char *ret = NULL;
 
        list_for_each_entry_safe(ai, tmp_ai, acl, node) {
-               char *tmp = make_message("%s%s/%d ", ret? ret : "",
+               char *tmp = make_message("%s%s/%u ", ret? ret : "",
                        inet_ntoa(ai->addr), ai->netmask);
                free(ret);
                ret = tmp;
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..36c432e54546d7f6079f9eb0ea4b35b675ab806b 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.
  */
 #include "version.h"
 #include "ggo.h"
 
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 static struct afh_args_info conf;
-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;
@@ -78,7 +171,7 @@ __noreturn static void print_help_and_die(void)
  */
 int main(int argc, char **argv)
 {
-       int i, ret, audio_format_num, fd;
+       int i, ret = 0, audio_format_num, fd;
        void *audio_file_data;
        size_t audio_file_size;
        struct afh_info afhi;
@@ -86,12 +179,9 @@ int main(int argc, char **argv)
        afh_cmdline_parser(argc, argv, &conf);
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        version_handle_flag("afh", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
+       if (conf.help_given || conf.detailed_help_given || conf.inputs_num == 0)
                print_help_and_die();
        afh_init();
-       ret = -E_AFH_SYNTAX;
-       if (conf.inputs_num == 0)
-               goto out;
        for (i = 0; i < conf.inputs_num; i++) {
                int ret2;
                ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
@@ -102,16 +192,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..a6f9c50073bd39563d05a76f38c5377b10451b16 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,17 +28,17 @@ 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. */
        struct taginfo tags;
        /**
-        * The table that specifies the offset of the individual pieces in
-        * the current audio file.
-        */
+        * The table that specifies the offset of the individual pieces in
+        * the current audio file.
+        */
        uint32_t *chunk_table;
        /** Period of time between sending data chunks. */
        struct timeval chunk_tv;
@@ -48,7 +48,7 @@ struct afh_info {
         * which means that this audio format does not need any special header
         * treatment. The audio format handler does not need to set this to
         * zero in this case.
-        */
+        */
        uint32_t header_len;
        /** The number of channels. */
        uint8_t channels;
@@ -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. */
@@ -76,7 +88,7 @@ struct audio_format_handler {
         */
        void (*init)(struct audio_format_handler*);
        /** Typical file endings for files that can be handled by this afh. */
-       const char **suffixes;
+       const char * const *suffixes;
        /**
         * Check if this audio format handler can handle the file.
         *
@@ -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..dfbf75132f4e4372eb0b4c95bff8ef25793dc545 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]);
        }
@@ -156,6 +139,38 @@ int guess_audio_format(const char *name)
        return -E_AUDIO_FORMAT;
 }
 
+/**
+ * Get the name of the given audio format.
+ *
+ * \param i The audio format number.
+ *
+ * \return This returns a pointer to statically allocated memory so it
+ * must not be freed by the caller.
+ */
+const char *audio_format_name(int i)
+{
+       if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
+               return "???";
+       return afl[i].name;
+}
+
+static int get_file_info(int format, const char *path, char *data,
+               size_t size, int fd, struct afh_info *afhi)
+{
+       int ret;
+       const char *fmt = audio_format_name(format);
+
+       memset(afhi, 0, sizeof(*afhi));
+       ret = afl[format].get_file_info(data, size, fd, afhi);
+       if (ret < 0) {
+               PARA_WARNING_LOG("%s: %s format not detected: %s\n",
+                       path, fmt, para_strerror(-ret));
+               return ret;
+       }
+       PARA_NOTICE_LOG("%s: detected %s format\n", path, fmt);
+       return format;
+}
+
 /**
  * Call get_file_info() to obtain an afhi structure.
  *
@@ -180,31 +195,18 @@ int compute_afhi(const char *path, char *data, size_t size, int fd,
 {
        int ret, i, format;
 
-       afhi->header_len = 0;
-       afhi->techinfo = NULL;
-       afhi->tags.artist = NULL;
-       afhi->tags.title = NULL;
-       afhi->tags.year = NULL;
-       afhi->tags.album = NULL;
-       afhi->tags.comment = NULL;
        format = guess_audio_format(path);
-
        if (format >= 0) {
-               ret = afl[format].get_file_info(data, size, fd, afhi);
-               if (ret >= 0) {
-                       ret = format;
+               ret = get_file_info(format, path, data, size, fd, afhi);
+               if (ret >= 0)
                        goto success;
-               }
        }
        FOR_EACH_AUDIO_FORMAT(i) {
                if (i == format) /* we already tried this one to no avail */
                        continue;
-               ret = afl[i].get_file_info(data, size, fd, afhi);
-               if (ret >= 0) {
-                       ret = i;
+               ret = get_file_info(i, path, data, size, fd, afhi);
+               if (ret >= 0)
                        goto success;
-               }
-               PARA_WARNING_LOG("%s\n", para_strerror(-ret));
        }
        return -E_AUDIO_FORMAT;
 success:
@@ -230,11 +232,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)
 {
@@ -249,19 +252,10 @@ void clear_afhi(struct afh_info *afhi)
        free(afhi->tags.comment);
 }
 
-/**
- * Get the name of the given audio format.
- *
- * \param i The audio format number.
- *
- * \return This returns a pointer to statically allocated memory so it
- * must not be freed by the caller.
- */
-const char *audio_format_name(int i)
+static inline size_t get_chunk_len(long unsigned chunk_num,
+               const struct afh_info *afhi)
 {
-       if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
-               return "???";
-       return afl[i].name;
+       return afhi->chunk_table[chunk_num + 1] - afhi->chunk_table[chunk_num];
 }
 
 /**
@@ -281,7 +275,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)
+                       return k;
+       return 0;
 }
 
 /**
@@ -356,9 +371,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 +396,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..28d8f3980f814763cfe249e5762203ff5038a9a5 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;
@@ -203,7 +205,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, struct task *t)
                afh_get_chunk(pard->first_chunk, afhi, pard->map, &start, &size);
                afh_get_chunk(pard->last_chunk, afhi, pard->map, &end, &size);
                end += size;
-               PARA_INFO_LOG("adding %zu bytes\n", end - start);
+               PARA_INFO_LOG("adding %td bytes\n", end - start);
                btr_add_output_dont_free(start, end - start, btrn);
                ret = -E_RECV_EOF;
                goto out;
diff --git a/afs.c b/afs.c
index 1a5e602dfbe1db769fef82c8f202ed0c9f5eca74..0946b6df3b0766f82f95f0cd100f039753543350 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;
@@ -504,7 +497,7 @@ no_admissible_files:
 }
 
 /* Never fails if arg == NULL */
-static int activate_mood_or_playlist(char *arg, int *num_admissible)
+static int activate_mood_or_playlist(const char *arg, int *num_admissible)
 {
        enum play_mode mode;
        int ret;
@@ -563,54 +556,73 @@ 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)
+{
+       const 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\n", arg);
+       if (current_mop) {
+               int ret2;
+               para_printf(&aca->pbout, "switching back to %s\n", current_mop);
+               ret2 = activate_mood_or_playlist(current_mop, &num_admissible);
+               if (ret2 >= 0)
+                       goto out;
+               para_printf(&aca->pbout, "could not reactivate %s: %s\n",
+                       current_mop, para_strerror(-ret2));
+       }
+       para_printf(&aca->pbout, "activating dummy mood\n");
+       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 +647,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_IROTH | S_IROTH);
+               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 +696,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;
 }
@@ -700,7 +706,7 @@ static int open_afs_tables(void)
        int i, ret;
 
        get_database_dir();
-       PARA_NOTICE_LOG("opening %u osl tables in %s\n", NUM_AFS_TABLES,
+       PARA_NOTICE_LOG("opening %d osl tables in %s\n", NUM_AFS_TABLES,
                database_dir);
        for (i = 0; i < NUM_AFS_TABLES; i++) {
                ret = afs_tables[i].open(database_dir);
@@ -717,13 +723,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 +751,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 +778,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 +807,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 +825,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 +839,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 +933,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 +988,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);
 }
 
 /**
@@ -1004,7 +1024,15 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
        register_command_task(cookie, &s);
        s.default_timeout.tv_sec = 0;
        s.default_timeout.tv_usec = 999 * 1000;
+       ret = write(socket_fd, "\0", 1);
+       if (ret != 1) {
+               if (ret == 0)
+                       errno = EINVAL;
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+               goto out_close;
+       }
        ret = schedule(&s);
+       sched_shutdown(&s);
 out_close:
        close_afs_tables();
 out:
@@ -1013,11 +1041,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 +1055,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 +1095,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 +1110,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 +1132,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 +1156,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 +1184,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 +1201,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)
-                       PARA_CRIT_LOG("table %s, event %d: %s\n", t->name,
+               if (ret < 0) {
+                       PARA_CRIT_LOG("table %s, event %u: %s\n", t->name,
                                event, para_strerror(-ret));
+                       return ret;
+               }
        }
+       return 1;
 }
 
 /**
diff --git a/afs.cmd b/afs.cmd
index 15459194cb7a4f0289890f3b6919eca1f33136c0..76b5f4dc82970d4bd2110ed8d9782cc8288dfb86 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,27 +36,28 @@ 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: -F  List full paths. If this option is not specified, only the basename
 H:     of each file is printed.
+H: -p  Synonym for -F. Deprecated.
+H:
+H: -b  Print only the basename of each matching file. This is the default, so
+H:     the option is currently a no-op. It is recommended to specify this option,
+H:     though, as the default might change in a future release.
 H:
 H: -a  List only files that are admissible with respect to the current mood or
 H:     playlist.
@@ -66,37 +66,28 @@ 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
+H:     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 +105,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 +126,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 +137,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 +160,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 +178,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 +221,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 +254,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 +266,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 +274,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 +291,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..2f9d7e5fc40bdc9c1661336ffce20d7a7df76148 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);
+int playlist_open(const 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..bfcd1fb0162fe8a8a03c0f5ba165acc60be71cf0 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,33 @@ 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 the given time is more than six month away from the current time,
+        * we print only the year. The additional space character in the format
+        * string below makes the formated date align nicely with dates that
+        * contain the time (those written by the above strftime() statement).
+        */
+       if (!strftime(buf, size, "%b %e  %Y", tm))
                return -E_STRFTIME;
        return 1;
 }
@@ -746,78 +738,65 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts)
        if (!hours) { /* m:ss or mm:ss */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 4;
-               sprintf(buf, "%*u:%02u", max_width - 3, mins, seconds % 60);
+               sprintf(buf, "%*u:%02d", max_width - 3, mins, seconds % 60);
        } else { /* more than one hour => h:mm:ss, hh:mm:ss, hhh:mm:ss, ... */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 7;
-               sprintf(buf, "%*u:%02u:%02u", max_width - 6, hours, mins,
+               sprintf(buf, "%*u:%02u:%02d", max_width - 6, hours, mins,
                        seconds % 60);
        }
 }
 
-
 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 +813,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 +848,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,22 +870,20 @@ 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  */
-                       "%*d "  /* lyrics_id */
-                       "%*d "  /* bitrate */
+                       "%*u "  /* image_id  */
+                       "%*u "  /* lyrics_id */
+                       "%*u "  /* bitrate */
                        "%*s "  /* audio format */
-                       "%*d "  /* frequency */
-                       "%d "   /* channels */
+                       "%*u "  /* frequency */
+                       "%u "   /* channels */
                        "%s "   /* duration */
-                       "%*d "  /* num_played */
+                       "%*u "  /* num_played */
                        "%s "   /* last_played */
                        "%s\n", /* path */
                        att_buf,
@@ -927,98 +900,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, "%u\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 +954,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 +989,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 +1021,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 +1300,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 +1324,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 +1333,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 +1357,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 +1372,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':
@@ -1459,10 +1410,14 @@ int com_ls(struct command_context *cc)
                                return -E_AFT_SYNTAX;
                        }
                }
-               if (!strcmp(arg, "-p")) {
+               if (!strcmp(arg, "-p") || !strcmp(arg, "-F")) {
                        flags |= LS_FLAG_FULL_PATH;
                        continue;
                }
+               if (!strcmp(arg, "-b")) {
+                       flags &= ~LS_FLAG_FULL_PATH;
+                       continue;
+               }
                if (!strcmp(arg, "-a")) {
                        flags |= LS_FLAG_ADMISSIBLE_ONLY;
                        continue;
@@ -1475,10 +1430,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 +1480,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 +1498,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 +1548,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 +1632,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 +1644,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 +1656,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 +1687,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 +1710,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 +1718,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 +1729,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 +1740,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 +1772,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 +1799,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 +1816,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 +1829,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 +1850,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 +1868,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 +1884,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 +1896,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 +1928,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 +1978,94 @@ 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->image_id >= 0) {
+               ret = img_get_name_by_id(cto->image_id, NULL);
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "invalid image ID: %d\n",
+                               cto->image_id);
+                       return ret;
+               }
+       }
+       if (cto->lyrics_id >= 0) {
+               ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "invalid lyrics ID: %d\n",
+                               cto->lyrics_id);
+                       return ret;
+               }
+       }
+       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 +2135,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 +2149,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 +2200,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 +2226,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 +2254,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 +2285,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 +2315,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 +2371,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)
-               send_strerror(cc, -ret);
+               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)
+               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)
 {
-       int *parser_friendly = query->data;
+       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 = 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 +2494,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 +2505,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 +2515,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);
 }
 
 /**
@@ -2638,12 +2641,12 @@ static int aft_open(const char *dir)
        if (ret >= 0) {
                unsigned num;
                osl_get_num_rows(audio_file_table, &num);
-               PARA_INFO_LOG("audio file table contains %d files\n", num);
+               PARA_INFO_LOG("audio file table contains %u 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 +2680,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..3adee929a804434fcea1999a439819dc85c66936 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,29 +136,36 @@ 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);
        if (!h->elem) {
-               PARA_NOTICE_LOG("unable to find simple control '%s',%i\n",
+               PARA_NOTICE_LOG("unable to find simple control '%s',%u\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..fd3b404c0359c1a571255b1ac7265b0ee0faef41 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:
@@ -285,7 +303,7 @@ again:
                get_btr_sample_format(btrn, &val);
                pad->sample_format = get_alsa_pcm_format(val);
 
-               PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels,
+               PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
                ret = alsa_init(pad, wn->conf);
                if (ret < 0) {
@@ -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..4cb2982860de78dd26b9fc55ad552c73a7ca1b9f 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, one = 1;
+       struct osl_object bitnum_obj;
+       int ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &bitnum_obj);
+
+       if (ret < 0)
+               return ret;
+       *att_mask |= one << *(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..f8fd80faa77bdea4a4f1139bb8168268ce59ddc3 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"
@@ -20,7 +24,8 @@
 #include "ggo.h"
 #include "version.h"
 
-INIT_AUDIOC_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
 /** The gengetopt structure containing command line args. */
 static struct audioc_args_info conf;
@@ -67,14 +72,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 +97,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 +130,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 +140,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 +168,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 +196,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 +246,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..db69cf141a3833a5227c12ccba3f81a9e357e3b3 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"
 #include "signal.h"
 #include "version.h"
 
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
-/** define the array of error lists needed by para_audiod */
-INIT_AUDIOD_ERRLISTS;
 /** define the array containing all supported audio formats */
 const char *audio_formats[] = {AUDIOD_AUDIO_FORMAT_ARRAY NULL};
 
@@ -62,6 +70,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 +119,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. */
@@ -137,15 +165,17 @@ int audiod_status = AUDIOD_ON;
  * the gengetopt args_info struct that holds information on all command line
  * arguments
  */
-struct audiod_args_info conf;
+static 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 +183,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 +226,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,53 +271,69 @@ 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;
+       int ret, seconds = 0, length = stat_task->length_seconds;
        struct timeval *tmp, sum, sss, /* server stream start */
                rstime, /* receiver start time */
                wstime, /* writer start time */
                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;
 
        if (audiod_status == AUDIOD_OFF)
                goto empty;
-       if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) {
-               if (stat_task->length_seconds) /* paused */
+       if (stat_task->server_stream_start.tv_sec == 0) {
+               if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)
+                       goto out; /* server is about to change file */
+               if (length > 0) /* paused */
                        return NULL;
                goto empty; /* stopped */
        }
-       if (audiod_status == AUDIOD_ON && !s)
-               goto empty;
        /*
         * Valid status items and playing, set length and tmp to the stream
         * start. We use the slot info and fall back to the info from current
         * status items if no slot info is available.
         */
-       length = stat_task->length_seconds;
        tmp = &stat_task->server_stream_start;
        if (s && s->wns && s->wns[0].btrn) { /* writer active in this slot */
                btr_get_node_start(s->wns[0].btrn, &wstime);
@@ -269,7 +352,7 @@ char *get_time_string(int slot_num)
                tv_diff(tmp, &stat_task->sa_time_diff, &sss);
        else
                tv_add(tmp, &stat_task->sa_time_diff, &sss);
-       if (!s || !s->wns || !s->wns[0].btrn) {
+       if (!s || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) {
                struct timeval diff;
                tv_diff(now, &sss, &diff);
                seconds = diff.tv_sec + stat_task->offset_seconds;
@@ -281,7 +364,8 @@ char *get_time_string(int slot_num)
        if (s->receiver_node->btrn) {
                btr_get_node_start(s->receiver_node->btrn, &rstime);
                ret = tv_diff(&rstime, &sss, &rskip);
-               if (ret > 0) { /* audiod was started in the middle of the stream */
+               if (ret > 0 && rskip.tv_sec > 2) {
+                       /* audiod was started in the middle of the stream */
                        tv_add(&wtime, &rskip, &sum);
                        seconds += sum.tv_sec;
                } else
@@ -308,20 +392,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 +414,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 +422,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 +478,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 +496,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 +526,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 +550,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 +588,27 @@ 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);
+               if (f->open)
+                       f->open(fn);
+               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 +622,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 +662,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 +682,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 +709,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 +745,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 +753,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);
@@ -662,7 +765,7 @@ static unsigned compute_time_diff(const struct timeval *status_time)
        if (count > 5) {
                int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
                if (tv_diff(&max_deviation, &tmp, NULL) < 0)
-                       PARA_WARNING_LOG("time diff jump: %lims\n",
+                       PARA_WARNING_LOG("time diff jump: %lums\n",
                                s * tv2ms(&tmp));
        }
        count++;
@@ -679,7 +782,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 +860,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 +995,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 +1016,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 +1024,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 +1059,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 +1072,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,
-               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));
+       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_IROTH | S_IWOTH);
+       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);
+               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 +1184,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 +1202,62 @@ 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;
+       bool dump = false;
 
-       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);
+                       dump = true;
+               }
+       if (dump)
+               audiod_status_dump(true);
+}
+
+/*
+ * 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 +1268,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 +1281,16 @@ 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);
+       audiod_status_dump(true);
 }
 
-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 +1320,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 +1342,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) {
@@ -1248,7 +1379,7 @@ static int status_post_select(struct sched *s, struct task *t)
        if (st->clock_diff_count) { /* get status only one time */
                char *argv[] = {"audiod", "--", "stat", "-p", "-n=1", NULL};
                int argc = 5;
-               PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count);
+               PARA_INFO_LOG("clock diff count: %u\n", st->clock_diff_count);
                st->clock_diff_count--;
                client_open(argc, argv, &st->ct, NULL, NULL, st->btrn, s);
                set_stat_task_restart_barrier(2);
@@ -1272,14 +1403,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,16 +1450,27 @@ __noreturn static void print_help_and_die(void)
        exit(0);
 }
 
-static void init_colors_or_die(void)
+/**
+ * Lookup the given UID in the whitelist.
+ *
+ * The whitelist is the array of arguments to the --user-allow opion. If the
+ * option was not given, the array is empty, in which case the check succeeds.
+ *
+ * \param uid User ID to look up.
+ *
+ * \return True if --user-allow was not given, or if uid matches an element of
+ * the whitelist.
+ */
+bool uid_is_whitelisted(uid_t uid)
 {
        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]);
+       if (!conf.user_allow_given)
+               return true;
+       for (i = 0; i < conf.user_allow_given; i++)
+               if (uid == uid_whitelist[i])
+                       return true;
+       return false;
 }
 
 /**
@@ -1359,9 +1505,14 @@ 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();
+       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+               conf.logfile_given)) {
+                       for (i = 0; i < conf.log_color_given; i++)
+                               daemon_set_log_color_or_die(conf.log_color_arg[i]);
+       }
        init_random_seed_or_die();
        daemon_set_flag(DF_LOG_TIME);
        daemon_set_flag(DF_LOG_HOSTNAME);
@@ -1377,13 +1528,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 +1541,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..7073c6dd2108fb31b915724fdbed431d76d51e63 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,17 @@ 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);
+/* defined in audiod.c */
 struct btr_node *audiod_get_btr_root(void);
-
-void stat_client_write_item(int item_num);
+__malloc char *audiod_get_decoder_flags(void);
 void clear_and_dump_items(void);
+char *get_time_string(void);
+bool uid_is_whitelisted(uid_t uid);
 
-/** iterate over all slots */
-#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++)
+/* defined in audiod_command.c */
+void audiod_status_dump(bool force);
+void close_stat_clients(void);
+int handle_connect(int accept_fd, fd_set *rfds);
+void stat_client_write_item(int item_num);
index b49d659e0ac0bd784b2002b941b3471540ee5ac9..3a39027523a72c686d997d1b358912dc5dfd2363 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,20 +387,21 @@ 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 i;
-
-       if (!conf.user_allow_given)
-               return 1;
-       for (i = 0; i < conf.user_allow_given; i++)
-               if (uid == conf.user_allow_arg[i])
-                       return 1;
-       return -E_UCRED_PERM;
+       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;
 }
 
 /**
@@ -437,9 +435,9 @@ 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);
-       if (ret < 0)
+       PARA_INFO_LOG("connection from UID %d, buf: %s\n", ret, buf);
+       ret = -E_UCRED_PERM;
+       if (!uid_is_whitelisted(uid))
                goto out;
        ret = create_argv(buf, "\n", &argv);
        if (ret <= 0)
@@ -455,27 +453,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 +482,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 +493,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 +501,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
diff --git a/base64.c b/base64.c
new file mode 100644 (file)
index 0000000..7b8fe29
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,205 @@
+/*
+ * The code in this file was taken from openssh-5.2p1, Copyright (c) 1996 by
+ * Internet Software Consortium.  Portions Copyright (c) 1995 by International
+ * Business Machines, Inc.
+ */
+
+/** \file base64.c Uudecode and base64decode implementation. */
+
+#include <regex.h>
+
+#include "para.h"
+#include "error.h"
+#include "base64.h"
+#include "string.h"
+
+static const char Base64[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const unsigned char base64_tab[256] = {
+       255, 255, 255, 255, 255, 255, 255, 255, /* 00-07 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 08-0f */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 10-17 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 18-1f */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 20-2f */
+       255, 255, 255,  62, 255, 255, 255,  63, /* 28-2f */
+       52 ,  53,  54,  55,  56,  57,  58,  59, /* 30-37 */
+       60 ,  61, 255, 255, 255, 255, 255, 255, /* 38-3f */
+       255,   0,   1,   2,   3,   4,   5,   6, /* 40-47 */
+       7  ,   8,   9,  10,  11,  12,  13,  14, /* 48-4f */
+       15 ,  16,  17,  18,  19,  20,  21,  22, /* 50-57 */
+       23 ,  24,  25, 255, 255, 255, 255, 255, /* 58-5f */
+       255,  26,  27,  28,  29,  30,  31,  32, /* 60-6f */
+       33 ,  34,  35,  36,  37,  38,  39,  40, /* 68-6f */
+       41 ,  42,  43,  44,  45,  46,  47,  48, /* 70-77 */
+       49 ,  50,  51, 255, 255, 255, 255, 255, /* 78-7f */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 80-87 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 88-8f */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 90-97 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* 98-9f */
+       255, 255, 255, 255, 255, 255, 255, 255, /* a0-a7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* a8-af */
+       255, 255, 255, 255, 255, 255, 255, 255, /* b0-b7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* b8-bf */
+       255, 255, 255, 255, 255, 255, 255, 255, /* c0-c7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* c8-cf */
+       255, 255, 255, 255, 255, 255, 255, 255, /* d0-d7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* d8-df */
+       255, 255, 255, 255, 255, 255, 255, 255, /* e0-e7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* e8-ef */
+       255, 255, 255, 255, 255, 255, 255, 255, /* f0-f7 */
+       255, 255, 255, 255, 255, 255, 255, 255, /* f8-ff */
+};
+
+/** Maximal possible size of the decoded data. */
+#define BASE64_MAX_DECODED_SIZE(_encoded_size) ((_encoded_size) / 4 * 3)
+
+#define PAD64 '='
+/**
+ * base64-decode a buffer.
+ *
+ * \param src The buffer to decode.
+ * \param encoded_size The special value -1 means: look for terminating zero byte.
+ * \param result Points to dynamically allocated target buffer on success.
+ * \param decoded_size Number of bytes written to \a result.
+ *
+ * Skips all whitespace anywhere. Converts characters, four at a time, starting
+ * at (or after) src from base - 64 numbers into three 8 bit bytes in the
+ * target area.
+ *
+ * It is OK to pass a \p NULL pointer as \a decoded_size. The result is
+ * terminated with a zero byte.
+ *
+ * \return Standard. The contents of result \a and \a decoded_size are
+ * undefined on failure.
+ */
+int base64_decode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size)
+{
+       size_t i, j, state; /* source/target indices */
+       const char *end = src + encoded_size, *p;
+       unsigned char *target, uch;
+
+       if (encoded_size == (size_t)-1)
+               encoded_size = strlen(src);
+       target = para_malloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
+
+       for (
+               i = 0, j = 0, state = 0;
+               i < encoded_size && (uch = src[i]) != '\0';
+               i++
+       ) {
+               if (para_isspace(uch)) /* Skip whitespace anywhere. */
+                       continue;
+               if (uch == PAD64)
+                       break;
+               if (base64_tab[uch] == 255) /* A non-base64 character. */
+                       goto fail;
+               uch = base64_tab[uch];
+               switch (state) {
+               case 0:
+                       target[j] = uch << 2;
+                       break;
+               case 1:
+                       target[j] |= uch >> 4;
+                       j++;
+                       target[j] = (uch & 0x0f) << 4;
+                       break;
+               case 2:
+                       target[j] |= uch >> 2;
+                       j++;
+                       target[j] = (uch & 0x03) << 6;
+                       break;
+               case 3:
+                       target[j] |= uch;
+                       j++;
+                       break;
+               }
+               state = (state + 1) % 4;
+       }
+       p = (i < encoded_size)? src + i : NULL;
+       /*
+        * We are done decoding Base-64 chars.  Let's see if we ended
+        * on a byte boundary, and/or with erroneous trailing characters.
+        */
+       if (p && *p == PAD64) { /* We got a pad char. Skip it, get next. */
+               p++;
+               switch (state) {
+               case 0: /* Invalid = in first position */
+               case 1: /* Invalid = in second position */
+                       goto fail;
+
+               case 2: /* Valid, means one byte of info */
+                       /* Skip any number of spaces. */
+                       for (; p < end && *p != '\0'; p++)
+                               if (!para_isspace(*p))
+                                       break;
+                       /* Make sure there is another trailing = sign. */
+                       if (*p != PAD64)
+                               goto fail;
+                       /* Fall through to "single trailing =" case. */
+                       p++;
+
+               case 3: /* Valid, means two bytes of info */
+                       /*
+                        * We know this char is an =.  Is there anything but
+                        * whitespace after it?
+                        */
+                       for (; p < end && *p != '\0'; p++)
+                               if (!para_isspace(*p))
+                                       goto fail;
+                       /*
+                        * Now make sure for cases 2 and 3 that the "extra"
+                        * bits that slopped past the last full byte were
+                        * zeros.  If we don't check them, they become a
+                        * subliminal channel.
+                        */
+                       if (target[j] != 0)
+                               goto fail;
+               }
+       } else {
+               /*
+                * We ended by seeing the end of the string.  Make sure we
+                * have no partial bytes lying around.
+                */
+               if (state != 0)
+                       goto fail;
+       }
+       /* success */
+       target[j] = '\0'; /* just to be sure */
+       if (decoded_size)
+               *decoded_size = j;
+       *result = (char *)target;
+       return 1;
+fail:
+       free(target);
+       return -E_BASE64;
+}
+
+/**
+ * Decode a buffer using the uuencode Base64 algorithm.
+ *
+ * \param src The buffer to decode.
+ * \param encoded_size Number of input bytes in the source buffer.
+ * \param result Contains the decoded data on success.
+ * \param decoded_size Number of output bytes on success.
+ *
+ * This is just a simple wrapper for \ref base64_decode() which strips
+ * whitespace.
+ *
+ * \return The return value of the underlying call to \ref base64_decode().
+ *
+ * \sa uuencode(1), uudecode(1).
+ */
+int uudecode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size)
+{
+       const char *end = src + encoded_size, *p;
+
+       /* skip whitespace and data */
+       for (p = src; p < end && (*p == ' ' || *p == '\t'); p++)
+               ;
+       for (; p < end && *p != '\0' && *p != ' ' && *p != '\t'; p++)
+               ;
+       /* and remove trailing whitespace because base64_decode needs this */
+       return base64_decode(src, p - src, result, decoded_size);
+}
diff --git a/base64.h b/base64.h
new file mode 100644 (file)
index 0000000..4bfaa99
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,4 @@
+int uudecode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size);
+int base64_decode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size);
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..638d19a3579b02053bd281e29e7f39d2bf54495c 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,32 +164,28 @@ 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.
  *
- * \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
- * 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,
-               int max_depth)
+int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits)
 {
        int n, idx, nb_bits, code;
 
        idx = show_bits(gbc, bits);
        code = table[idx][0];
        n = table[idx][1];
-       if (max_depth > 1 && n < 0) {
+       if (n < 0) {
                skip_bits(gbc, bits);
                nb_bits = -n;
                idx = show_bits(gbc, nb_bits) + code;
                code = table[idx][0];
                n = table[idx][1];
-               if (max_depth > 2 && n < 0) {
+               if (n < 0) {
                        skip_bits(gbc, nb_bits);
                        nb_bits = -n;
                        idx = show_bits(gbc, nb_bits) + code;
index a3393380e5e02801a4312c6f904c1ea25527aad9..5875b0d090e6e8007d34766ee090950c819188dd 100644 (file)
@@ -37,7 +37,7 @@ static inline uint32_t show_bits(struct getbit_context *gbc, int num)
 {
        int idx = gbc->index;
        const uint8_t *p = gbc->buffer + (idx >> 3);
-       uint32_t x = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+       uint32_t x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
        return (x << (idx & 7)) >> (32 - num);
 }
 
@@ -62,18 +62,20 @@ static inline unsigned int get_bits(struct getbit_context *gbc, int n)
 static inline unsigned int get_bit(struct getbit_context *gbc)
 {
        int idx = gbc->index++;
-       uint8_t tmp = gbc->buffer[idx >> 3], mask = (1 << (7 - (idx & 7)));
+       uint8_t tmp = gbc->buffer[idx >> 3], mask = 1 << (7 - (idx & 7));
        return !!(tmp & mask);
 }
 
 /**
  * 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)
@@ -86,6 +88,4 @@ static inline void init_get_bits(struct getbit_context *gbc,
 void init_vlc(struct vlc *vlc, int nb_bits, int nb_codes, const void *bits,
                const void *codes, int codes_size);
 void free_vlc(struct vlc *vlc);
-int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits,
-               int max_depth);
-
+int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits);
diff --git a/blob.c b/blob.c
index 44e58a1062a3ce4bf27cb0f620cefb4fdca5b329..ed684428aba55ae848c58fe8c96af2c900f4d6b9 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 %u 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) \
        { \
@@ -519,7 +487,8 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
        struct osl_object obj = {.data = &id, .size = sizeof(id)};
        int ret;
 
-       *name = NULL;
+       if (name)
+               *name = NULL;
        if (!id)
                return 1;
        ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row));
@@ -528,7 +497,10 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
        ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0)
                return ret;
-       *name = (char *)obj.data;
+       if (*(char *)obj.data == '\0')
+               return -E_DUMMY_ROW;
+       if (name)
+               *name = (char *)obj.data;
        return 1;
 }
 
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..1a47f946f3fb01829ae0d84caacb7461d0d22580 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.
  */
@@ -177,7 +177,7 @@ int check_wav_post_select(struct check_wav_context *cwc)
        else
                cwc->sample_format = (a[3] == 'F')?
                        SF_S16_LE : SF_S16_BE;
-       PARA_NOTICE_LOG("%dHz, %s, %s\n", cwc->sample_rate,
+       PARA_NOTICE_LOG("%uHz, %s, %s\n", cwc->sample_rate,
                cwc->channels == 1? "mono" : "stereo",
                sample_formats[cwc->sample_format]);
        btr_consume(btrn, WAV_HEADER_LEN);
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..f1100df49e144b873779ca84d226c1829cb1c1a3 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.
  */
@@ -22,7 +22,8 @@
 #include "error.h"
 #include "version.h"
 
-INIT_CLIENT_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
 static struct sched sched;
 static struct client_task *ct;
@@ -35,28 +36,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 +94,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 +103,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 +205,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 +218,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 +277,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", "-F", "-b", NULL
        };
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -440,7 +448,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 +455,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 +500,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 +532,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 +603,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..eea7510fd0589e86fb4058dd7f4361f3c79901aa 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,38 +263,32 @@ 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);
@@ -350,11 +297,8 @@ static int client_post_select(struct sched *s, struct task *t)
        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 +313,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 +480,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..fe4b92329b417009613e0f1034f747783699c2e0 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. */
@@ -487,7 +473,7 @@ static unsigned empty_status_items(int parser_friendly, char **result)
                        #define ITEM(x) "0004 %02x:\n"
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
-                       #define ITEM(x) , SI_ ## x
+                       #define ITEM(x) , (unsigned) SI_ ## x
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
                );
@@ -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: %li\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,19 @@ static void reset_signals(void)
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
+struct connection_features {
+       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 +834,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;
+                               continue;
+                       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 +853,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 +880,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 +898,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 +922,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 +936,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 +945,9 @@ __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 (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 +964,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 %d 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 +994,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 +1019,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 c80fe96903318fa1891fccae26afa7023f32c696..0b00cc2af9f006d492975a54210ce26f4e5aa56e 100644 (file)
-#                                               -*- Autoconf -*-
-# Process this file with autoconf to produce a configure script.
-
 AC_PREREQ([2.61])
 
-
-AC_INIT([paraslash],[0.4.14],[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([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_USE_SYSTEM_EXTENSIONS
 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([M4], [m4])
+test -z "$M4" && AC_MSG_ERROR(
+       [The m4 macro processor is required to build this package])
+
+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 +83,36 @@ 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"
+########################################################################### 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)
+if test $HAVE_OPENSSL = yes; then
+       AC_CHECK_LIB([crypto], [RSA_set0_key],
+               AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl-1.1]))
 fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-########################################################################### crypto
+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,143 +120,59 @@ 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
-########################################################################### libsocket
-AC_CHECK_LIB([c], [socket],
-       [socket_lib=],
-       [socket_lib="-lsocket"]
+       ;;
+*)
+       AC_MSG_ERROR([invalid value "$enable_cryptolib" for --enable-cryptolib])
+       ;;
+esac
+AC_SUBST(crypto_ldflags)
+########################################################################## iconv
+STASH_FLAGS
+LIBS=
+AC_SEARCH_LIBS([libiconv_open], [iconv],
+       [iconv_ldflags="$LIBS"],
+       []
 )
-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().])
-],[])
-########################################################################### libnsl
-AC_CHECK_LIB([c], [gethostbyname],
-       [nsl_lib=],
-       [nsl_lib="-lnsl"]
+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]
 )
-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_DEFINE_UNQUOTED(ICONV_CAST, $cast, [cast for second arg to iconv()])
+AC_MSG_RESULT($msg)
+UNSTASH_FLAGS
 ########################################################################### ucred
 AC_MSG_CHECKING(for struct ucred)
 AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-       #define _GNU_SOURCE
        #include <sys/types.h>
        #include <sys/socket.h>
 ]], [[
@@ -530,7 +183,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 +192,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,485 +227,397 @@ 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
+               base64
+               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))
 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
+               base64
+               version
+               ggo
+       "
+       if test "$CRYPTOLIB" = openssl; then
+               client_errlist_objs="$client_errlist_objs crypt"
+       else
+               client_errlist_objs="$client_errlist_objs gcrypt"
        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"
-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"
+       if test $HAVE_READLINE = yes; then
+               client_errlist_objs="$client_errlist_objs interactive"
+       fi
+       client_objs="add_cmdline($client_cmdline_objs) $client_errlist_objs"
+       AC_SUBST(client_objs, add_dot_o($client_objs))
 else
-       AC_MSG_WARN([no ogg/vorbis $msg])
+       build_client="no"
 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
+               base64
+               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))
+
+       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])
-       ])
+       build_audiod="no"
 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"
-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)
        enum="$(
                for i in $mixers; do
                        printf "${i}_MIX, " | tr '[a-z]' '[A-Z]'
@@ -1104,200 +638,382 @@ 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"
 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"
+########################################################################### 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))
+else
+       build_gui="no"
+       AC_MSG_WARN([no curses lib, cannot build para_gui])
 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])
-       ])
+######################################################################## 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
-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_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_LIB([pthread], [pthread_create], [], [
-               have_ao="no"
-               AC_MSG_WARN([pthread lib not found or not working, $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
-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"
+filters="$(echo $filters)"
+AC_SUBST(filters)
+filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs"
 
-       play_errlist_objs="$play_errlist_objs ao_write"
-       play_cmdline_objs="$play_cmdline_objs add_cmdline(ao_write)"
-       play_ldflags="$play_ldflags -lao -lpthread"
+AC_SUBST(filter_objs, add_dot_o($filter_objs))
 
-       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"
-       writers="$writers ao"
-       AC_SUBST(ao_cppflags)
-fi
+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
+"
 
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-############################################################# readline
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LDFLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
+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"
 
-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_FAAD = yes -a $HAVE_MP4V2 = yes; then
+       recv_errlist_objs="$recv_errlist_objs aac_afh aac_common"
 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"
+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))
+########################################################################### 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
-msg="no interactive cli support"
-AC_CHECK_HEADERS([readline/readline.h], [
-       ], [
-       have_readline="no"
-       AC_MSG_WARN([readline/readline.h not found, $msg])
-])
 
-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])
-fi
+afh_objs="add_cmdline($afh_cmdline_objs) $afh_errlist_objs"
 
-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
+AC_SUBST(afh_objs, add_dot_o($afh_objs))
+########################################################################## 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
-
-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])
+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
-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 $HAVE_MP4V2 = yes; then
+       play_errlist_objs="$play_errlist_objs aac_afh"
 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 $HAVE_MP4V2 = yes || test $HAVE_FAAD = yes; then
+       play_errlist_objs="$play_errlist_objs aac_common"
 fi
-
-AC_CHECK_HEADER(samplerate.h, [], have_samplerate=no)
-AC_CHECK_LIB([samplerate], [src_process], [], have_samplerate=no, [])
-
-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)"
+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 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 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])
+       play_cmdline_objs="$play_cmdline_objs resample_filter"
 fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
-############################################################# error2.h
-AC_MSG_NOTICE(creating error2.h)
-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'),"
-done
-AC_DEFINE_UNQUOTED(DEFINE_ERRLIST_OBJECT_ENUM,
-       [enum {$SS NUM_SS}],
-       [list of all objects that use the paraslash error facility]
-)
 
+play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs"
+AC_SUBST(play_objs, add_dot_o($play_objs))
+######################################################################### 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 ao_write"
+       writers="$writers ao"
+       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
+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_SUBST(writers)
+write_objs="add_cmdline($write_cmdline_objs) $write_errlist_objs"
+AC_SUBST(write_objs, add_dot_o($write_objs))
+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
+audioc_objs="add_cmdline($audioc_cmdline_objs) $audioc_errlist_objs"
+AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
 ################################################################## status items
 
 status_items="basename status num_played mtime bitrate frequency file_size
@@ -1325,104 +1041,26 @@ 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_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..8116fb6eeacdbeaa0f1f22d14cfcc3726ee3bbbf 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"
@@ -22,6 +23,7 @@
 #include "crypt.h"
 #include "fd.h"
 #include "crypt_backend.h"
+#include "base64.h"
 
 struct asymmetric_key {
        RSA *rsa;
@@ -132,23 +134,29 @@ static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
 {
        int ret;
        RSA *rsa;
+       BIGNUM *n, *e;
        const unsigned char *p = blob, *end = blob + blen;
 
        rsa = RSA_new();
        if (!rsa)
                return -E_BIGNUM;
-       ret = read_bignum(p, end - p, &rsa->e);
+       ret = read_bignum(p, end - p, &e);
        if (ret < 0)
                goto fail;
        p += ret;
-       ret = read_bignum(p, end - p, &rsa->n);
+       ret = read_bignum(p, end - p, &n);
        if (ret < 0)
                goto fail;
+#ifdef HAVE_RSA_SET0_KEY
+       RSA_set0_key(rsa, n, e, NULL);
+#else
+       rsa->n = n;
+       rsa->e = e;
+#endif
        *result = rsa;
        return 1;
 fail:
-       if (rsa)
-               RSA_free(rsa);
+       RSA_free(rsa);
        return ret;
 }
 
@@ -158,7 +166,7 @@ int get_asymmetric_key(const char *key_file, int private,
        struct asymmetric_key *key = NULL;
        void *map = NULL;
        unsigned char *blob = NULL;
-       size_t map_size, blob_size, decoded_size;
+       size_t map_size, encoded_size, decoded_size;
        int ret, ret2;
        char *cp;
 
@@ -180,16 +188,11 @@ int get_asymmetric_key(const char *key_file, int private,
                goto out;
        }
        cp = map + ret;
+       encoded_size = map_size - ret;
        PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
-       ret = -ERRNO_TO_PARA_ERROR(EOVERFLOW);
-       if (map_size > INT_MAX / 4)
-               goto out_unmap;
-       blob_size = 2 * map_size;
-       blob = para_malloc(blob_size);
-       ret = uudecode(cp, blob, blob_size);
+       ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size);
        if (ret < 0)
                goto out_unmap;
-       decoded_size = ret;
        ret = check_ssh_key_header(blob, decoded_size);
        if (ret < 0)
                goto out_unmap;
@@ -261,18 +264,35 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
 }
 
 struct stream_cipher {
-       RC4_KEY key;
+       bool use_aes;
+       union {
+               RC4_KEY rc4_key;
+               EVP_CIPHER_CTX *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)
 {
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
-       RC4_set_key(&sc->key, len, data);
+
+       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);
+       sc->context.aes = EVP_CIPHER_CTX_new();
+       EVP_EncryptInit_ex(sc->context.aes, EVP_aes_128_ctr(), NULL, data,
+               data + AES_CRT128_BLOCK_SIZE);
        return sc;
 }
 
 void sc_free(struct stream_cipher *sc)
 {
+       if (!sc)
+               return;
+       EVP_CIPHER_CTX_free(sc->context.aes);
        free(sc);
 }
 
@@ -283,44 +303,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 +326,32 @@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
        ((char *)dst->iov_base)[len] = '\0';
 }
 
+static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
+               struct iovec *dst)
+{
+       int ret, inlen = src->iov_len, outlen, tmplen;
+
+       *dst = (typeof(*dst)) {
+               /* Add one for the terminating zero byte. */
+               .iov_base = para_malloc(inlen + 1),
+               .iov_len = inlen
+       };
+       ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
+       assert(ret != 0);
+       ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
+       assert(ret != 0);
+       outlen += tmplen;
+       ((char *)dst->iov_base)[outlen] = '\0';
+       dst->iov_len = outlen;
+}
+
+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..f9a69d9490057382126e4efe04a15bfda9025419 100644 (file)
@@ -1,16 +1,17 @@
 /*
- * 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);
-int uudecode(const char *src, unsigned char *target, size_t targsize);
 int check_ssh_key_header(const unsigned char *blob, int blen);
 int check_key_file(const char *file, bool private_key);
-int base64_decode(char const *src, unsigned char *target, size_t targsize);
index cd9500ab39f3a89cd4ee70da64d0d7e72ff7c84f..a05572df85b1de5fb5e1043abfc34c66e26c65e7 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.
  */
@@ -45,176 +45,6 @@ size_t is_ssh_rsa_key(char *data, size_t size)
        return cp - data;
 }
 
-/*
- * This base64/uudecode stuff below is taken from openssh-5.2p1, Copyright (c)
- * 1996 by Internet Software Consortium.  Portions Copyright (c) 1995 by
- * International Business Machines, Inc.
- */
-
-static const char Base64[] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char Pad64 = '=';
-
-/**
- * base64-decode a buffer.
- *
- * \param src The buffer to decode.
- * \param target Result is stored here.
- * \param targsize Number of bytes of \a target.
- *
- * Skips all whitespace anywhere. Converts characters, four at a time, starting
- * at (or after) src from base - 64 numbers into three 8 bit bytes in the
- * target area.
- *
- * \return The number of data bytes stored at the target, -E_BASE64 on errors.
- */
-int base64_decode(char const *src, unsigned char *target, size_t targsize)
-{
-       unsigned int tarindex, state;
-       int ch;
-       char *pos;
-
-       state = 0;
-       tarindex = 0;
-
-       while ((ch = *src++) != '\0') {
-               if (para_isspace(ch)) /* Skip whitespace anywhere. */
-                       continue;
-
-               if (ch == Pad64)
-                       break;
-
-               pos = strchr(Base64, ch);
-               if (pos == NULL) /* A non-base64 character. */
-                       return -E_BASE64;
-
-               switch (state) {
-               case 0:
-                       if (target) {
-                               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 ;
-                       }
-                       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;
-                       }
-                       tarindex++;
-                       state = 3;
-                       break;
-               case 3:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex] |= (pos - Base64);
-                       }
-                       tarindex++;
-                       state = 0;
-                       break;
-               }
-       }
-
-       /*
-        * We are done decoding Base-64 chars.  Let's see if we ended
-        * on a byte boundary, and/or with erroneous trailing characters.
-        */
-
-       if (ch == Pad64) {              /* We got a pad char. */
-               ch = *src++;            /* Skip it, get next. */
-               switch (state) {
-               case 0:         /* Invalid = in first position */
-               case 1:         /* Invalid = in second position */
-                       return -E_BASE64;
-
-               case 2:         /* Valid, means one byte of info */
-                       /* Skip any number of spaces. */
-                       for (; ch != '\0'; ch = *src++)
-                               if (!isspace(ch))
-                                       break;
-                       /* Make sure there is another trailing = sign. */
-                       if (ch != Pad64)
-                               return -E_BASE64;
-                       ch = *src++;            /* Skip the = */
-                       /* Fall through to "single trailing =" case. */
-                       /* FALLTHROUGH */
-
-               case 3:         /* Valid, means two bytes of info */
-                       /*
-                        * We know this char is an =.  Is there anything but
-                        * whitespace after it?
-                        */
-                       for (; ch != '\0'; ch = *src++)
-                               if (!isspace(ch))
-                                       return -E_BASE64;
-
-                       /*
-                        * Now make sure for cases 2 and 3 that the "extra"
-                        * bits that slopped past the last full byte were
-                        * zeros.  If we don't check them, they become a
-                        * subliminal channel.
-                        */
-                       if (target && target[tarindex] != 0)
-                               return -E_BASE64;
-               }
-       } else {
-               /*
-                * We ended by seeing the end of the string.  Make sure we
-                * have no partial bytes lying around.
-                */
-               if (state != 0)
-                       return -E_BASE64;
-       }
-
-       return tarindex;
-}
-
-/**
- * uudecode a buffer.
- *
- * \param src The buffer to decode.
- * \param target Result buffer.
- * \param targsize The length of \a target in bytes.
- *
- * This is just a simple wrapper for base64_decode() which strips whitespace.
- *
- * \return The return value of the underlying call to base64_decode().
- */
-int uudecode(const char *src, unsigned char *target, size_t targsize)
-{
-       int len;
-       char *encoded, *p;
-
-       /* copy the 'readonly' source */
-       encoded = para_strdup(src);
-       /* skip whitespace and data */
-       for (p = encoded; *p == ' ' || *p == '\t'; p++)
-               ;
-       for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
-               ;
-       /* and remove trailing whitespace because base64_decode needs this */
-       *p = '\0';
-       len = base64_decode(encoded, target, targsize);
-       free(encoded);
-       return len;
-}
-
 /**
  * Read a 4-byte number from a buffer in big-endian format.
  *
@@ -266,7 +96,7 @@ int check_ssh_key_header(const unsigned char *blob, int blen)
                return -E_SSH_KEY_HEADER;
        if (rlen < strlen(KEY_TYPE_TXT))
                return -E_SSH_KEY_HEADER;
-       PARA_DEBUG_LOG("type: %s, rlen: %d\n", p, rlen);
+       PARA_DEBUG_LOG("type: %s, rlen: %u\n", p, rlen);
        if (strncmp((char *)p, KEY_TYPE_TXT, strlen(KEY_TYPE_TXT)))
                return -E_SSH_KEY_HEADER;
        return 4 + rlen;
@@ -321,41 +151,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..20cdc6ae605a99f56f35bf41f0abeff7d508936d 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] = {
@@ -61,11 +57,9 @@ void daemon_set_default_log_colors(void)
 /**
  * Set the color for one loglevel.
  *
- * \param arg The loglevel/color specifier.
- *
- * \a arg must be of the form "ll:[fg [bg]] [attr]".
+ * \param arg Must be of the form "ll:[fg [bg]] [attr]".
  */
-void daemon_set_log_color_or_die(char const *arg)
+void daemon_set_log_color_or_die(const char *arg)
 {
        char *p = strchr(arg, ':');
        int ret, ll;
@@ -80,16 +74,50 @@ void daemon_set_log_color_or_die(char const *arg)
        color_parse_or_die(p, me->log_colors[ll]);
        return;
 err:
-       PARA_EMERG_LOG("%s: color syntax error\n", arg);
+       PARA_EMERG_LOG("%s: invalid color argument\n", arg);
        exit(EXIT_FAILURE);
 }
 
+/**
+ * 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.
+ *
+ * If color_arg equals color_arg_no, color mode is disabled. That is, calls to
+ * para_log() will not produce colored output. If color_arg equals
+ * color_arg_auto, the function detects automatically whether to activate
+ * colors. Otherwise color mode is enabled.
+ *
+ * If color mode is to be enabled, the default colors are set for each
+ * loglevel. They can be overwritten by calling daemon_set_log_color_or_die().
+ *
+ * \return Whether colors have been enabled by the function.
+ */
+bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+               int color_arg_no, bool logfile_given)
+{
+       if (color_arg == color_arg_no)
+               return false;
+       if (color_arg == color_arg_auto) {
+               if (logfile_given)
+                       return false;
+               if (!isatty(STDERR_FILENO))
+                       return false;
+       }
+       daemon_set_flag(DF_COLOR_LOG);
+       daemon_set_default_log_colors();
+       return true;
+}
+
 /**
  * Init or change the name of the log file.
  *
  * \param logfile_name The full path of the logfile.
  */
-void daemon_set_logfile(char *logfile_name)
+void daemon_set_logfile(const char *logfile_name)
 {
        free(me->logfile_name);
        me->logfile_name = NULL;
@@ -102,7 +130,7 @@ void daemon_set_logfile(char *logfile_name)
  *
  * \param loglevel The smallest level that should be logged.
  */
-void daemon_set_loglevel(char *loglevel)
+void daemon_set_loglevel(const char *loglevel)
 {
        int ret = get_loglevel_by_name(loglevel);
 
@@ -122,27 +150,11 @@ 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;
 }
 
-static void dummy_sighandler(__a_unused int s)
-{
-}
-
 /**
  * Do the usual stuff to become a daemon.
  *
@@ -150,36 +162,48 @@ static void dummy_sighandler(__a_unused int s)
  *
  * Fork, become session leader, cd to /, and dup fd 0, 1, 2 to /dev/null. If \a
  * parent_waits is false, the parent process terminates immediately.
- * Otherwise, it calls pause() to sleep until it receives \p SIGTERM or \p
- * SIGCHLD and exits successfully thereafter. This behaviour is useful if the
- * daemon process should not detach from the console until the child process
- * has completed its setup.
+ * Otherwise, a pipe is created prior to the fork() and the parent tries to
+ * read a single byte from the reading end of the pipe. The child is supposed
+ * to write to the writing end of the pipe after it completed its setup
+ * procedure successfully. This behaviour is useful to let the parent process
+ * die with an error if the child process aborts early, since in this case the
+ * read() will return non-positive.
+ *
+ * \return This function either succeeds or calls exit(3). If parent_waits is
+ * true, the return value is the file descriptor of the writing end of the
+ * pipe. Otherwise the function returns zero.
  *
  * \sa fork(2), setsid(2), dup(2), pause(2).
  */
-void daemonize(bool parent_waits)
+int daemonize(bool parent_waits)
 {
        pid_t pid;
-       int null;
+       int null, pipe_fd[2];
 
-       PARA_INFO_LOG("daemonizing\n");
+       if (parent_waits && pipe(pipe_fd) < 0)
+               goto err;
+       PARA_INFO_LOG("subsequent log messages go to %s\n", me->logfile_name?
+                me->logfile_name : "/dev/null");
        pid = fork();
        if (pid < 0)
                goto err;
-       if (pid) {
+       if (pid) { /* parent exits */
                if (parent_waits) {
-                       signal(SIGTERM, dummy_sighandler);
-                       signal(SIGCHLD, dummy_sighandler);
-                       pause();
+                       char c;
+                       close(pipe_fd[1]);
+                       exit(read(pipe_fd[0], &c, 1) <= 0?
+                               EXIT_FAILURE : EXIT_SUCCESS);
                }
-               exit(EXIT_SUCCESS); /* parent exits */
+               exit(EXIT_SUCCESS);
        }
+       if (parent_waits)
+               close(pipe_fd[0]);
        /* become session leader */
        if (setsid() < 0)
                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)
@@ -189,7 +213,7 @@ void daemonize(bool parent_waits)
        if (dup2(null, STDERR_FILENO) < 0)
                goto err;
        close(null);
-       return;
+       return parent_waits? pipe_fd[1] : 0;
 err:
        PARA_EMERG_LOG("fatal: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
@@ -223,16 +247,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 +293,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 +334,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 +356,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 +371,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..989678df40e6b29a44ae8fb5491db05841d25bca 100644 (file)
--- a/daemon.h
+++ b/daemon.h
@@ -1,21 +1,21 @@
 /** \file daemon.h exported symbols from daemon.c */
 
 
-void daemonize(bool parent_waits);
+int 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_set_logfile(char *logfile_name);
+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(const 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_set_loglevel(const char *loglevel);
+bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+               int color_arg_no, bool logfile_given);
+void daemon_set_log_color_or_die(const char *arg);
 __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..899c574b315cd9e8ee7356eb29c1828e1dd6ae04 100644 (file)
--- a/error.h
+++ b/error.h
 /*
- * 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 error.h List of error messages for all subsystems. */
+/** \file error.h List of error codes and messages. */
 
-/** \cond errors */
-
-/* List of all subsystems that use paraslash's error facility. */
-DEFINE_ERRLIST_OBJECT_ENUM;
-
-/* these do not need error handling (yet) */
-#define SERVER_ERRORS
-#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
-#define HTTP_SEND_ERRORS
-#define GGO_ERRORS
-#define COLOR_ERRORS
-#define SIGNAL_ERRORS
-#define FADE_ERRORS
-#define STDOUT_ERRORS
-#define FILE_WRITE_ERRORS
-#define STDIN_ERRORS
-#define WRITE_ERRORS
-#define CHECK_WAV_ERRORS
-#define VERSION_ERRORS
-#define SCHED_ERRORS
-
-extern const char **para_errlist[];
-
-#define OSS_MIX_ERRORS \
-       PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \
-
-
-#define ALSA_MIX_ERRORS \
-       PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
-       PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \
+/** Codes and messages. */
+#define PARA_ERRORS \
+       PARA_ERROR(AAC_AFH_INIT, "failed to init aac decoder"), \
+       PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
+       PARA_ERROR(AAC_DECODE, "aac decode error"), \
+       PARA_ERROR(ACL_PERM, "access denied by acl"), \
+       PARA_ERROR(ADD_CALLBACK, "can not add callback"), \
+       PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
+       PARA_ERROR(AFH_RECV_BAD_FILENAME, "no file name given"), \
+       PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
+       PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \
+       PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \
+       PARA_ERROR(AFS_SYNTAX, "afs syntax error"), \
+       PARA_ERROR(AFT_SYNTAX, "audio file table syntax error"), \
+       PARA_ERROR(ALSA, "alsa error"), \
        PARA_ERROR(ALSA_MIX_GET_VAL, "could not read control element state"), \
+       PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
+       PARA_ERROR(ALSA_MIX_RANGE, "value control element out of range"), \
        PARA_ERROR(ALSA_MIX_SET_VAL, "could not set control element state"), \
-
-
-#define RESAMPLE_FILTER_ERRORS \
-       PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \
-       PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
-
-
-#define OPUS_COMMON_ERRORS \
-       PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
-
-
-#define OPUS_AFH_ERRORS \
-       PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
-
-
-#define OPUSDEC_FILTER_ERRORS \
-       PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
-       PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
-       PARA_ERROR(OPUS_DECODE, "opus decode error"), \
-
-#define SIDEBAND_ERRORS \
-       PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
-       PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
-
-
-#define PLAY_ERRORS \
-       PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
-       PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
-       PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
-
-
-#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 FLAC_AFH_ERRORS \
-       PARA_ERROR(FLAC_CHAIN_ALLOC, "could not create metadata chain"), \
-       PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \
-       PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \
-       PARA_ERROR(FLAC_VARBLOCK, "variable blocksize not supported"), \
-       PARA_ERROR(FLAC_AFH_DECODER_ALLOC, "could not allocate stream decoder"), \
-       PARA_ERROR(FLAC_AFH_DECODER_INIT, "could not init stream decoder"), \
-       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"), \
-
-
-#define AFH_RECV_ERRORS \
-       PARA_ERROR(AFH_RECV_BAD_FILENAME, "invalid file name"), \
-
-
-#define OGG_AFH_COMMON_ERRORS \
-       PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error (first packet)"), \
-       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(OGG_SYNC, "internal ogg storage overflow"), \
-       PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
-
-
-#define SPX_AFH_ERRORS \
-       PARA_ERROR(SPX_COMMENT, "invalid speex comment"), \
-
-
-#define SPX_COMMON_ERRORS \
-       PARA_ERROR(SPX_HEADER, "can not read speex header"), \
-       PARA_ERROR(SPX_HEADER_MODE, "invalid speex mode in header"), \
-       PARA_ERROR(SPX_VERSION, "incompatible speex bit stream version"), \
-       PARA_ERROR(SPX_DECODER_INIT, "speex decoder initialization failed"), \
-       PARA_ERROR(SPX_CTL_BAD_RQ, "speex_decoder_ctl: invalid request"), \
-       PARA_ERROR(SPX_CTL_INVAL, "speex_decoder_ctl: invalid argument"), \
-
-#define SPXDEC_FILTER_ERRORS \
-       PARA_ERROR(SPX_DECODE, "speex decoding error"), \
-       PARA_ERROR(SPX_DECODE_OVERFLOW, "speex decoding overflow"), \
-       PARA_ERROR(SPX_EOS, "speex: end of stream"), \
-
-#define BUFFER_TREE_ERRORS \
-       PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \
-       PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
-       PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
-
-
-#define BITSTREAM_ERRORS \
-       PARA_ERROR(VLC, "invalid vlc code"), \
-
-
-#define WMA_AFH_ERRORS \
-       PARA_ERROR(NO_WMA, "asf/wma format not recognized"), \
-
-
-#define WMA_COMMON_ERRORS \
-       PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
-
-
-#define WMADEC_FILTER_ERRORS \
-       PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \
-       PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \
-       PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \
-       PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \
-       PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \
-       PARA_ERROR(WMADEC_EOF, "wmadec: end of file"), \
-
-
-#define IMDCT_ERRORS \
-       PARA_ERROR(FFT_BAD_PARAMS, "invalid params for fft"), \
-
-
-#define PREBUFFER_FILTER_ERRORS \
-       PARA_ERROR(PREBUFFER_SYNTAX, "syntax error in prebuffer filter config"), \
-       PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
-
-
-#define OSS_WRITE_ERRORS \
-       PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
-       PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \
-       PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
-
-
-#define AO_WRITE_ERRORS \
-       PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
+       PARA_ERROR(AMP_EOF, "amp: end of file"), \
+       PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \
+       PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
-       PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
-       PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
+       PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
+       PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
+       PARA_ERROR(AO_EOF, "ao: end of file"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
+       PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
        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"), \
-
-
-#define COMPRESS_FILTER_ERRORS \
-       PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
-
-
-#define WAV_FILTER_ERRORS \
-       PARA_ERROR(WAV_BAD_FC, "invalid filter configuration"), \
-       PARA_ERROR(WAV_EOF, "wav filter: end of file"), \
-       PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \
-
-
-#define FEC_ERRORS \
-       PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
-       PARA_ERROR(FEC_SINGULAR, "unexpected singular matrix"), \
-       PARA_ERROR(FEC_PIVOT, "pivot column not found"), \
-       PARA_ERROR(FEC_PARMS, "invalid fec parameters"), \
-
-
-#define FECDEC_FILTER_ERRORS \
-       PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
-       PARA_ERROR(BAD_SLICE_SIZE, "slice size zero or too large"), \
-       PARA_ERROR(BAD_SLICE_NUM, "invalid slice number"), \
-       PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
-       PARA_ERROR(FECDEC_EOF, "received eof packet"), \
-
-
-#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"), \
-
-
-#define SEND_COMMON_ERRORS \
-       PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
-
-
-#define CLIENT_ERRORS \
-       PARA_ERROR(TASK_STARTED, "task started"), \
-
-
-#define AFH_ERRORS \
-       PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \
-
-
-#define AFH_COMMON_ERRORS \
-       PARA_ERROR(AUDIO_FORMAT, "audio format not recognized"), \
-
-
-#define ACL_ERRORS \
-       PARA_ERROR(ACL_PERM, "access denied by acl"), \
-
-
-#define AFS_ERRORS \
-       PARA_ERROR(BAD_TABLE_NAME, "invalid table"), \
-       PARA_ERROR(AFS_SYNTAX, "afs syntax error"), \
-       PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \
-       PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \
-
-
-#define MOOD_ERRORS \
-       PARA_ERROR(NO_MOOD, "no mood available"), \
-       PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
-
-
-#define MM_ERRORS \
-       PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
-
-
-#define ATTRIBUTE_ERRORS \
+       PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
+       PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
+       PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
+       PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
+       PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
        PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \
-       PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \
        PARA_ERROR(ATT_TABLE_FULL, "no more space left in attribute table"), \
-
-
-#define BLOB_ERRORS \
-       PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
-       PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \
-
-
-#define PLAYLIST_ERRORS \
-       PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
-       PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
-       PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
-
-
-#define AFT_ERRORS \
-       PARA_ERROR(BAD_AFSI, "invaiid afs info"), \
-       PARA_ERROR(LOCALTIME, "localtime() failed"), \
-       PARA_ERROR(STRFTIME, "strftime() failed"), \
-       PARA_ERROR(BAD_PATH, "invalid path"), \
-       PARA_ERROR(BAD_SORT, "invalid sorting method"), \
-       PARA_ERROR(FNMATCH, "fnmatch error"), \
-       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"), \
-
-
-#define USER_LIST_ERRORS \
-       PARA_ERROR(USERLIST, "failed to open user list file"), \
-
-
-#define OSX_WRITE_ERRORS \
-       PARA_ERROR(STREAM_FORMAT, "could not set stream format"), \
-       PARA_ERROR(ADD_CALLBACK, "can not add callback"), \
-       PARA_ERROR(OPEN_COMP, "OpenAComponent() error"), \
-       PARA_ERROR(UNIT_INIT, "AudioUnitInitialize() error"), \
-       PARA_ERROR(UNIT_START, "AudioUnitStart() error"), \
-       PARA_ERROR(DEFAULT_COMP, "can not find default audio output component"), \
-
-
-#define AUDIOC_ERRORS \
        PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
-
-
-#define CLIENT_COMMON_ERRORS \
-       PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
-       PARA_ERROR(NO_CONFIG, "config file not found"), \
+       PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
+       PARA_ERROR(AUDIOD_SIGNAL, "caught deadly signal"), \
+       PARA_ERROR(AUDIOD_TERM, "terminating on user request"), \
+       PARA_ERROR(AUDIO_FORMAT, "audio format not recognized"), \
+       PARA_ERROR(AUTH_REQUEST, "did not receive auth request"), \
+       PARA_ERROR(BAD_AFSI, "invalid afs info"), \
+       PARA_ERROR(BAD_ASF_FILE_PROPS, "invalid ASF file properties"), \
+       PARA_ERROR(BAD_AUTH, "authentication failure"), \
+       PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
+       PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \
+       PARA_ERROR(BAD_CHANNEL, "invalid channel"), \
+       PARA_ERROR(BAD_CMD, "invalid command"), \
        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"), \
-
-
-#define NET_ERRORS \
-       PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
-       PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
+       PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
+       PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
+       PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
+       PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
+       PARA_ERROR(BAD_LL, "invalid loglevel"), \
+       PARA_ERROR(BAD_PATH, "invalid path"), \
+       PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
+       PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
+       PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
+       PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
+       PARA_ERROR(BAD_SLICE_NUM, "invalid slice number"), \
+       PARA_ERROR(BAD_SLICE_SIZE, "slice size zero or too large"), \
+       PARA_ERROR(BAD_SORT, "invalid sorting method"), \
+       PARA_ERROR(BAD_TABLE_NAME, "invalid table"), \
+       PARA_ERROR(BAD_USER, "auth request for invalid user"), \
+       PARA_ERROR(BASE64, "failed to base64-decode ssh public key"), \
+       PARA_ERROR(BIGNUM, "bignum error"), \
+       PARA_ERROR(BLINDING, "failed to activate key blinding"), \
+       PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
+       PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \
+       PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
+       PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
        PARA_ERROR(CHMOD, "failed to set socket mode"), \
-       PARA_ERROR(SENDMSG, "sendmsg() failed"), \
-       PARA_ERROR(RECVMSG, "recvmsg() failed"), \
-       PARA_ERROR(SCM_CREDENTIALS, "did not receive SCM credentials"), \
-       PARA_ERROR(MAKESOCK, "makesock error"), \
-
-
-#define UDP_RECV_ERRORS \
-       PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
-
-
-#define UDP_SEND_ERRORS \
-       PARA_ERROR(TARGET_EXISTS, "requested target is already present"),\
-       PARA_ERROR(TARGET_NOT_FOUND, "requested target not found")
-
-
-#define HTTP_RECV_ERRORS \
-       PARA_ERROR(HTTP_RECV_OVERRUN, "http_recv: output buffer overrun"), \
-
-
-#define RECV_COMMON_ERRORS \
-       PARA_ERROR(RECV_SYNTAX, "recv syntax error"), \
-       PARA_ERROR(RECV_EOF, "end of file"), \
-
-
-#define AUDIOD_ERRORS \
-       PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
-       PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
-       PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
-       PARA_ERROR(NOT_PLAYING, "not playing"), \
-       PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
-
-
-#define AUDIOD_COMMAND_ERRORS \
+       PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(CLIENT_WRITE, "client write error"), \
-       PARA_ERROR(UCRED_PERM, "permission denied"), \
-       PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
-       PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \
-       PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
-
-
-#define FILTER_COMMON_ERRORS \
-       PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
-       PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
-
-
-#define STAT_ERRORS \
-       PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \
-
-
-#define OGGDEC_FILTER_ERRORS \
-       PARA_ERROR(OGGDEC_READ, "read from media returned an error"), \
-       PARA_ERROR(OGGDEC_NOTVORBIS, "bitstream is not vorbis data"), \
-       PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
-       PARA_ERROR(OGGDEC_BADHEADER, "invalid vorbis bitstream header"), \
-       PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \
-       PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \
-
-
-#define GRAB_CLIENT_ERRORS \
-       PARA_ERROR(GC_WRITE, "grab client write error"), \
+       PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
+       PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
+       PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
+       PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
+       PARA_ERROR(DECRYPT, "decrypt error"), \
+       PARA_ERROR(DEFAULT_COMP, "can not find default audio output component"), \
+       PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
+       PARA_ERROR(DUP_PIPE, "exec error: can not create pipe"), \
+       PARA_ERROR(EMPTY, "file is empty"), \
+       PARA_ERROR(ENCRYPT, "encrypt error"), \
+       PARA_ERROR(EOF, "end of file"), \
+       PARA_ERROR(ESDS, "did not find esds atom"), \
+       PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
+       PARA_ERROR(FECDEC_EOF, "received eof packet"), \
+       PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
+       PARA_ERROR(FEC_PARMS, "invalid fec parameters"), \
+       PARA_ERROR(FEC_PIVOT, "pivot column not found"), \
+       PARA_ERROR(FEC_SINGULAR, "unexpected singular matrix"), \
+       PARA_ERROR(FFT_BAD_PARAMS, "invalid params for fft"), \
+       PARA_ERROR(FGETS, "fgets error"), \
+       PARA_ERROR(FLAC_AFH_DECODER_ALLOC, "could not allocate stream decoder"), \
+       PARA_ERROR(FLAC_AFH_DECODER_INIT, "could not init stream decoder"), \
+       PARA_ERROR(FLAC_CHAIN_ALLOC, "could not create metadata chain"), \
+       PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \
+       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"), \
+       PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \
+       PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \
+       PARA_ERROR(FLAC_REPLACE_COMMENT, "could not replace vorbis comment"), \
+       PARA_ERROR(FLAC_SKIP_META, "could not skip metadata"), \
+       PARA_ERROR(FLAC_STREAMINFO, "could not read stream info meta block"), \
+       PARA_ERROR(FLAC_VARBLOCK, "variable blocksize not supported"), \
+       PARA_ERROR(FLAC_WRITE_CHAIN, "failed to write metadata chain"), \
+       PARA_ERROR(FNMATCH, "fnmatch error"), \
+       PARA_ERROR(FRAME, "invalid mp3 frame"), \
+       PARA_ERROR(FRAME_LENGTH, "invalid frame length"), \
        PARA_ERROR(GC_SYNTAX, "grab client syntax error"), \
-
-
-#define MP3DEC_FILTER_ERRORS \
+       PARA_ERROR(GC_WRITE, "grab client write error"), \
+       PARA_ERROR(GUI_SIGCHLD, "received SIGCHLD"), \
+       PARA_ERROR(HASH_MISMATCH, "hash mismatch, consider re-add"), \
+       PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
+       PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
+       PARA_ERROR(HTTP_RECV_OVERRUN, "http_recv: output buffer overrun"), \
+       PARA_ERROR(I9E_EOF, "end of input"), \
+       PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \
+       PARA_ERROR(I9E_TERM_RQ, "received termination request"), \
+       PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \
+       PARA_ERROR(ID3_DETACH, "could not detach id3 frame"), \
+       PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
+       PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
+       PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \
+       PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
+       PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
+       PARA_ERROR(KEY_PERM, "unprotected private key"), \
+       PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
+       PARA_ERROR(LOCALTIME, "localtime() failed"), \
+       PARA_ERROR(LOCK, "lock error"), \
        PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
-       PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
+       PARA_ERROR(MAKESOCK, "makesock error"), \
+       PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
+       PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
+       PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
        PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
-
-
-#define FILTER_ERRORS \
-       PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
-
-
-#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"), \
-
-
-#define EXEC_ERRORS \
-       PARA_ERROR(DUP_PIPE, "exec error: can not create pipe"), \
-
-
-#define MP3_AFH_ERRORS \
-       PARA_ERROR(FRAME, "invalid mp3 frame"), \
-       PARA_ERROR(FRAME_LENGTH, "invalid frame length"), \
+       PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
-       PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
-       PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
-
-
-#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"), \
-
-
-#define AAC_COMMON_ERRORS \
-       PARA_ERROR(ESDS, "did not find esds atom"), \
-       PARA_ERROR(STCO, "did not find stco atom"), \
-
-
-#define OGG_AFH_ERRORS \
-       PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
+       PARA_ERROR(MP4V2, "mp4v2 library error"), \
+       PARA_ERROR(MPI_PRINT, "could not convert multi-precision integer"), \
+       PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
+       PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
+       PARA_ERROR(NO_AFHI, "audio format handler info required"), \
+       PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \
+       PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
+       PARA_ERROR(NO_CONFIG, "config file not found"), \
+       PARA_ERROR(NOFD, "did not receive open fd from afs"), \
+       PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
+       PARA_ERROR(NO_MATCH, "no matches"), \
+       PARA_ERROR(NO_MOOD, "no mood available"), \
+       PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
+       PARA_ERROR(NOT_PLAYING, "not playing"), \
+       PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
+       PARA_ERROR(NO_WMA, "asf/wma format not recognized"), \
+       PARA_ERROR(OEAP, "error during oeap (un)padding"), \
+       PARA_ERROR(OGGDEC_BADHEADER, "invalid vorbis bitstream header"), \
+       PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \
+       PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \
+       PARA_ERROR(OGGDEC_NOTVORBIS, "bitstream is not vorbis data"), \
+       PARA_ERROR(OGGDEC_READ, "read from media returned an error"), \
+       PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
+       PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
        PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
-
-
-#define VSS_ERRORS \
-       PARA_ERROR(NOFD, "did not receive open fd from afs"), \
-       PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
-       PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
-
-
-#define CRYPT_COMMON_ERRORS \
-       PARA_ERROR(SSH_KEY_HEADER, "ssh key header not found"), \
-       PARA_ERROR(BASE64, "failed to base64-decode ssh public key"), \
-       PARA_ERROR(KEY_PERM, "unprotected private key"), \
-
-
-#define CRYPT_ERRORS \
+       PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
+       PARA_ERROR(OPEN_COMP, "OpenAComponent() error"), \
+       PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
+       PARA_ERROR(OPUS_DECODE, "opus decode error"), \
+       PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
+       PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
+       PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
+       PARA_ERROR(PERM, "permission denied"), \
+       PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
+       PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
+       PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
+       PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(PUBLIC_KEY, "can not read public key"), \
+       PARA_ERROR(QUEUE, "packet queue overrun"), \
+       PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
+       PARA_ERROR(RECV_EOF, "end of file"), \
+       PARA_ERROR(RECVMSG, "recvmsg() failed"), \
+       PARA_ERROR(RECV_SYNTAX, "recv syntax error"), \
+       PARA_ERROR(REGEX, "regular expression error"), \
+       PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \
        PARA_ERROR(RSA, "RSA error"), \
-       PARA_ERROR(ENCRYPT, "encrypt error"), \
-       PARA_ERROR(DECRYPT, "decrypt error"), \
-       PARA_ERROR(BLINDING, "failed to activate key blinding"), \
-       PARA_ERROR(BIGNUM, "bignum error"), \
-
-#define GCRYPT_ERRORS \
-       PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
-       PARA_ERROR(MPI_PRINT, "could not convert multi-precision integer"), \
-       PARA_ERROR(SEXP_FIND, "could not find sublist in S-expression"), \
-       PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
-       PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
-       PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
-       PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
-       PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
-       PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
-       PARA_ERROR(SSH_PARSE, "could not parse ssh public key"), \
-       PARA_ERROR(OEAP, "error during oeap (un)padding"), \
-
-
-#define COMMAND_ERRORS \
-       PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
-       PARA_ERROR(AUTH_REQUEST, "did not receive auth request"), \
-       PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
-       PARA_ERROR(BAD_CMD, "invalid command"), \
-       PARA_ERROR(PERM, "permission denied"), \
-       PARA_ERROR(LOCK, "lock error"), \
+       PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
+       PARA_ERROR(SCM_CREDENTIALS, "did not receive SCM credentials"), \
        PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
+       PARA_ERROR(SENDMSG, "sendmsg() failed"), \
+       PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
+       PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        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_AUTH, "authentication failure"), \
-
-
-#define DCCP_RECV_ERRORS \
-       PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
-
-
-#define FD_ERRORS \
-       PARA_ERROR(FGETS, "fgets error"), \
-       PARA_ERROR(EOF, "end of file"), \
-       PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
+       PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
+       PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
+       PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
+       PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
+       PARA_ERROR(SEXP_FIND, "could not find sublist in S-expression"), \
        PARA_ERROR(SHORT_WRITE, "unexpected short write"), \
-       PARA_ERROR(EMPTY, "file is empty"), \
-
-
-#define ALSA_WRITE_ERRORS \
-       PARA_ERROR(ALSA, "alsa error"), \
-
-
-#define WRITE_COMMON_ERRORS \
+       PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \
+       PARA_ERROR(SPX_COMMENT, "invalid speex comment"), \
+       PARA_ERROR(SPX_CTL_BAD_RQ, "speex_decoder_ctl: invalid request"), \
+       PARA_ERROR(SPX_CTL_INVAL, "speex_decoder_ctl: invalid argument"), \
+       PARA_ERROR(SPX_DECODE_OVERFLOW, "speex decoding overflow"), \
+       PARA_ERROR(SPX_DECODER_INIT, "speex decoder initialization failed"), \
+       PARA_ERROR(SPX_DECODE, "speex decoding error"), \
+       PARA_ERROR(SPX_EOS, "speex: end of stream"), \
+       PARA_ERROR(SPX_HEADER, "can not read speex header"), \
+       PARA_ERROR(SPX_HEADER_MODE, "invalid speex mode in header"), \
+       PARA_ERROR(SPX_VERSION, "incompatible speex bit stream version"), \
+       PARA_ERROR(SSH_KEY_HEADER, "ssh key header not found"), \
+       PARA_ERROR(SSH_PARSE, "could not parse ssh public key"), \
+       PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \
+       PARA_ERROR(STATUS_TIMEOUT, "status item timeout"), \
+       PARA_ERROR(STCO, "did not find stco atom"), \
+       PARA_ERROR(STREAM_FORMAT, "could not set stream format"), \
+       PARA_ERROR(STREAM_PACKETIN, "ogg stream packet-in error"), \
+       PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error"), \
+       PARA_ERROR(STREAM_PAGEIN, "ogg stream page-in error"), \
+       PARA_ERROR(STREAM_PAGEOUT, "ogg stream page-out error"), \
+       PARA_ERROR(STRFTIME, "strftime() failed"), \
+       PARA_ERROR(STSZ, "did not find stcz atom"), \
+       PARA_ERROR(SYNC_COMPLETE, "all buddies in sync"), \
+       PARA_ERROR(SYNC_LISTEN_FD, "no fd to listen on"), \
+       PARA_ERROR(SYNC_PAGEOUT, "ogg sync page-out error (no ogg file?)"), \
+       PARA_ERROR(TARGET_EXISTS, "requested target is already present"),\
+       PARA_ERROR(TARGET_NOT_FOUND, "requested target not found"), \
+       PARA_ERROR(TASK_STARTED, "task started"), \
+       PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \
+       PARA_ERROR(UCRED_PERM, "permission denied"), \
+       PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
+       PARA_ERROR(UNIT_INIT, "AudioUnitInitialize() error"), \
+       PARA_ERROR(UNIT_START, "AudioUnitStart() error"), \
+       PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
+       PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
+       PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
+       PARA_ERROR(USERLIST, "failed to open user list file"), \
+       PARA_ERROR(VLC, "invalid vlc code"), \
+       PARA_ERROR(VORBIS_COMMENTHEADER, "could not create vorbis comment header"), \
+       PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
+       PARA_ERROR(WAV_BAD_FC, "invalid filter configuration"), \
+       PARA_ERROR(WAV_EOF, "wav filter: end of file"), \
+       PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \
+       PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \
+       PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \
+       PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \
+       PARA_ERROR(WMADEC_EOF, "wmadec: end of file"), \
+       PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
+       PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \
        PARA_ERROR(WRITE_COMMON_EOF, "end of file"), \
 
-
-#define AACDEC_FILTER_ERRORS \
-       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"), \
-
-
-#define INTERACTIVE_ERRORS \
-       PARA_ERROR(I9E_EOF, "end of input"), \
-       PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \
-       PARA_ERROR(I9E_TERM_RQ, "received termination request"), \
-
-/** \endcond errors */
-
-/**
- * The subsystem shift.
- *
- * 255 error codes ought to be enough for every subsystem. Use the higher bits
- * of the return value to encode the subsystem number.
- */
-#define SS_SHIFT 8
-
-/**
- * Compute the subsystem offset.
- *
- * It is given by x * 2**8 where \a x is the subsystem number.
- */
-#define SS_OFFSET(ss) (SS_ ## ss << SS_SHIFT)
-
-/**
- * Make the enum of all errors of one subsystem.
- *
- * As zero should not be an error, we define a dummy enum entry with value
- * 2**ss. That lets the real errors start at 2**ss + 1.
- */
-#define SS_ENUM(ss) enum {\
-       E_ ## ss ## _DUMMY = SS_OFFSET(ss), \
-       ss ## _ERRORS}
-
-/**
- * Determine the subsystem number from the error number.
- *
- * Easy, it's just \a num / 2**8.
- */
-#define ERRNUM_TO_SS(num) ((num) >> SS_SHIFT)
-
 /**
- * Determine the index of an error number.
- *
- * Also easy: It's the lower 8 bits of num.
+ * This is temporarily defined to expand to its first argument (prefixed by
+ * 'E_') and gets later redefined to expand to the error text only
  */
-#define ERRNUM_TO_INDEX(num) (((1 << SS_SHIFT) - 1) & (num))
+#define PARA_ERROR(err, msg) E_ ## err
+enum para_error_codes {PARA_ERRORS};
+#undef PARA_ERROR
+#define PARA_ERROR(err, msg) msg
+/** Array of error strings. */
+extern const char * const para_errlist[];
+#define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
 
 /**
  * This bit indicates whether a number is considered a system error number
@@ -567,20 +299,7 @@ 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,13 +310,13 @@ _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 para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)];
+               return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
+       return para_errlist[num];
 }
 
 /**
@@ -607,7 +326,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.
  */
@@ -617,31 +336,3 @@ _static_inline_ int osl(int ret)
                return ret;
        return -OSL_ERRNO_TO_PARA_ERROR(-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).
- */
-#define DEFINE_ERRLIST(ss) const char * ss ## _ERRLIST[] = {#ss, ss ## _ERRORS}
-
-/**
- * Activate errors for one subsystem.
- *
- * Each executable needs only the error lists of those subsystems it is
- * actually linked with. We always reserve space for NUM_SS char pointers,
- * but only init those of the needed subsystems. This macro is used by macros
- * in config.h (generated by configure).
- */
-#define PARA_ERRLIST(ss) [SS_ ## ss] = ss ## _ERRLIST
-
-/**
- * This is temporarily defined to expand to its first argument (prefixed by
- * 'E_') and gets later redefined to expand to the error text only
- */
-#define PARA_ERROR(err, msg) E_ ## err
-#include "error2.h"
-#undef PARA_ERROR
-/* Rest of the world only sees the error text. */
-#define PARA_ERROR(err, msg) msg
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..67dc4d5494c13ea8d6d167d56502b5818146bcff 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.
  */
@@ -17,7 +17,9 @@
 #include "ggo.h"
 #include "version.h"
 
-INIT_FADE_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 static struct fade_args_info conf;
 
 enum mixer_id {MIXER_ENUM};
@@ -27,7 +29,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 +39,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 +65,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 %u seconds\n",
+               conf.mixer_channel_arg, vol, new_vol, secs);
        diff = new_vol - vol;
        if (!diff) {
                sleep(secs);
@@ -112,7 +123,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 +133,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 +186,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 +205,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: %d:%02d\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 +225,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 +243,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 +298,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)
@@ -296,6 +335,17 @@ __noreturn static void print_help_and_die(void)
        exit(0);
 }
 
+/**
+ * The main function of para_fade.
+ *
+ * The executable is linked with the alsa or the oss mixer API, or both. It has
+ * a custom log function which prefixes log messages with the current date.
+ *
+ * \param argc Argument counter.
+ * \param argv Argument vector.
+ *
+ * \return EXIT_SUCCESS or EXIT_FAILURE.
+ */
 int main(int argc, char *argv[])
 {
        int ret;
@@ -330,7 +380,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 +395,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..1c3a37849d1309fbf3b676bf04c513628dc1c657 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;
 };
 
@@ -171,7 +171,7 @@ static struct fecdec_group *free_oldest_group(struct private_fecdec_data *pfd)
                        oldest = fg;
        }
        if (!group_complete(oldest) && !group_empty(oldest))
-               PARA_WARNING_LOG("Clearing incomplete group %d "
+               PARA_WARNING_LOG("Clearing incomplete group %u "
                        "(contains %d slices)\n", oldest->h.group_num,
                        oldest->num_received_slices);
        if (oldest == pfd->first_complete_group)
@@ -224,7 +224,7 @@ static int add_slice(char *buf, struct fecdec_group *fg)
        uint8_t slice_num = fg->h.slice_num;
 
        if (group_complete(fg)) {
-               PARA_DEBUG_LOG("group %d complete, ignoring slice %d\n",
+               PARA_DEBUG_LOG("group %u complete, ignoring slice %d\n",
                        fg->h.group_num, slice_num);
                return 0;
        }
@@ -236,7 +236,7 @@ static int add_slice(char *buf, struct fecdec_group *fg)
        r = fg->num_received_slices;
        /* Check if we already have this slice. */
        if (test_and_set_slice_bit(fg, slice_num)) {
-               PARA_INFO_LOG("ignoring duplicate slice %d:%d\n", fg->h.group_num,
+               PARA_INFO_LOG("ignoring duplicate slice %u:%d\n", fg->h.group_num,
                        slice_num);
                return 0;
        }
@@ -293,10 +293,10 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn)
        char *buf = NULL;
 
        if (u == FEC_GROUP_UNUSABLE) {
-               PARA_INFO_LOG("dropping unusable group %d\n", fg->h.group_num);
+               PARA_INFO_LOG("dropping unusable group %u\n", fg->h.group_num);
                return 0;
        }
-       PARA_DEBUG_LOG("decoding group %d (%d slices)\n", fg->h.group_num,
+       PARA_DEBUG_LOG("decoding group %u (%d slices)\n", fg->h.group_num,
                fg->h.data_slices_per_group);
        ret = fec_decode(pfd->fec, fg->data, fg->idx, sb);
        if (ret < 0)
@@ -307,7 +307,7 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn)
                i = DIV_ROUND_UP(fg->h.audio_header_size, fg->h.slice_bytes);
                PARA_DEBUG_LOG("skipping %d header slices\n", i);
        }
-       PARA_DEBUG_LOG("writing group %d (%d/%d decoded data bytes)\n",
+       PARA_DEBUG_LOG("writing group %u (%u/%d decoded data bytes)\n",
                fg->h.group_num, fg->h.group_bytes,
                fg->h.data_slices_per_group * sb);
        need = (fg->h.data_slices_per_group - i) * sb;
@@ -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..5e66607e4dba4a0595da7f8fcfeae0dba0bf3bad 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.
  */
@@ -39,7 +39,7 @@ __must_check __malloc static char *random_filename(void)
        char *result, *home = para_homedir();
 
        srandom(clock_get_realtime(NULL)->tv_usec);
-       result = make_message("%s/.paraslash/%08lu", home,
+       result = make_message("%s/.paraslash/%08ld", home,
                para_random(99999999));
        free(home);
        return result;
@@ -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..81901896aef25294a64599c9d77f63bf541f91e4 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.
  */
 #include "error.h"
 #include "version.h"
 
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 /** 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. */
-INIT_FILTER_ERRLISTS;
-
 /** The task that reads from stdin. */
 static struct stdin_task stdin_task_struct;
 /** pointer to the stdin task. */
@@ -52,7 +58,7 @@ __noreturn static void print_help_and_die(void)
 
        ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
        print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
-       exit(0);
+       exit(EXIT_SUCCESS);
 }
 
 static int parse_config(void)
@@ -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,34 @@ 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;
-               f->open(fn);
-               register_task(&s, &fn->task);
+               ti.name = f->name;
+               ti.pre_select = f->pre_select;
+               ti.post_select = f->post_select;
+               ti.context = fn;
+               if (f->open)
+                       f->open(fn);
+               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..0bd546903927d412fccef946cf8f43783ed8bc70 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;
 };
@@ -58,17 +58,18 @@ struct filter {
        /**
         * Open one instance of this filter.
         *
-        * This should allocate the output buffer of the given filter node and do any
-        * other filter-specific preparations like initializing the private_data member
-        * of \a fn suitably. The open function is assumed to succeed.
+        * This should allocate the output buffer of the given filter node and
+        * do any other filter-specific preparations like initializing the
+        * private_data member of \a fn suitably. The open function is
+        * optional, If it is provided, it is assumed to succeed.
         */
        void (*open)(struct filter_node *fn);
        /**
         * Close one instance of this filter.
         *
-        * Free all resources of associated with \a fn that were previously allocated
-        * by the open() function. It's OK to leave this alone if the filter does not
-        * need any cleanups.
+        * Free all resources associated with \a fn that were previously
+        * allocated by the open() function. It's OK to set this to NULL if the
+        * filter does not need to perform any cleanup operation.
         */
        void (*close)(struct filter_node *fn);
        /**
@@ -96,19 +97,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.
         *
@@ -119,9 +122,9 @@ struct filter {
 };
 
 void filter_init(void);
-int check_filter_arg(char *filter_arg, void **conf);
+int check_filter_arg(const char *fa, 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 +141,4 @@ 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..e9b97e54633a8770009c0f746b7daa712d4135fb 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};
+
+/**
+ * Obtain a reference to a filter structure.
+ *
+ * \param filter_num Between zero and NUM_SUPPORTED_FILTERS, inclusively.
+ *
+ * \return Pointer to the filter identified by the given filter number.
+ *
+ * It is a fatal error if the given number is out of range. In this case
+ * the function aborts.
+ */
+const struct filter *filter_get(int filter_num)
+{
+       assert(filter_num >= 0);
+       assert(filter_num < NUM_SUPPORTED_FILTERS);
+       return filters + filter_num;
+}
 
 /**
  * Call the init function of each supported filter.
@@ -31,16 +51,16 @@ 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));
 }
 
 /*
  * If the filter has a command line parser and options is not NULL, run it.
  * Returns filter_num on success, negative on errors
  */
-static int parse_filter_args(int filter_num, char *options, void **conf)
+static int parse_filter_args(int filter_num, const char *options, void **conf)
 {
-       struct filter *f = &filters[filter_num];
+       const struct filter *f = filter_get(filter_num);
        int ret, argc;
        char **argv;
 
@@ -58,11 +78,12 @@ static int parse_filter_args(int filter_num, char *options, void **conf)
 /**
  * Check the filter command line options.
  *
- * \param fa The command line options.
+ * \param fa The filter argument.
  * \param conf Points to the filter configuration upon successful return.
  *
- * Check if \a fa starts with a the name of a supported filter, followed by
- * a colon. If yes, call the command line parser of that filter.
+ * Check if the given filter argument starts with the name of a supported
+ * filter, optionally followed by options for this filter. If yes, call the
+ * command line parser of that filter.
  *
  * \return On success, the number of the filter is returned and \a conf
  * is initialized to point to the filter configuration determined by \a fa.
@@ -73,14 +94,14 @@ static int parse_filter_args(int filter_num, char *options, void **conf)
  *
  * \sa filter::parse_config
  */
-int check_filter_arg(char *fa, void **conf)
+int check_filter_arg(const char *fa, void **conf)
 {
        int j;
 
        *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 +111,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 +134,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 +153,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);
 }
@@ -184,7 +204,7 @@ int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                return 1;
        }
        if (!strcmp(cmd, "sample_format")) {
-               *result = make_message("%u", DECODER_SAMPLE_FORMAT);
+               *result = make_message("%d", DECODER_SAMPLE_FORMAT);
                return 1;
        }
        return -ERRNO_TO_PARA_ERROR(ENOTSUP);
index d2f256dc152a8e9547968b5f30ece5cff381754c..385d4f0c3a0235b9be24cdc83666308c30b959b8 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,7 +433,89 @@ static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
        return 1;
 }
 
-static const char* flac_suffixes[] = {"flac", NULL};
+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: %u\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 * const flac_suffixes[] = {"flac", NULL};
 
 /**
  * The init function of the flac audio format handler.
@@ -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 dd6afe1d4fb25c73190626ee9206da43a67ffd6f..729e3ae1656deec60e6cafa37877db823e3c4852 100644 (file)
 #define __printf_2_3 __printf(2,3)
 #define __printf_3_4 __printf(3,4)
 
-# if __GNUC__ > 3  || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
-# define __must_check  __attribute__ ((warn_unused_result))
-# else
-# define __must_check  /* no warn_unused_result */
-# endif
-
+#define __must_check __attribute__ ((warn_unused_result))
 #define _static_inline_ static inline
-
index 6bb7452c254f550499945226ab96e4bc44c81c66..7c19aeb0c3edc022c5b0f0425a202e37fd38d906 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.
  */
@@ -15,6 +15,7 @@
 #include "crypt.h"
 #include "crypt_backend.h"
 #include "fd.h"
+#include "base64.h"
 
 //#define GCRYPT_DEBUG 1
 
@@ -26,7 +27,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 +88,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 +98,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;
 
@@ -212,7 +213,7 @@ static int decode_key(const char *key_file, const char *header_str,
 
        ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
        if (ret < 0)
-               return ret;
+               goto out;
        ret = -E_KEY_MARKER;
        if (strncmp(map, header_str, strlen(header_str)))
                goto unmap;
@@ -239,13 +240,11 @@ 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);
+       ret = base64_decode(key, j, (char **)&blob, &blob_size);
        free(key);
        if (ret < 0)
                goto free_unmap;
+       ret = blob_size;
        goto unmap;
 free_unmap:
        free(blob);
@@ -258,6 +257,7 @@ unmap:
                free(blob);
                blob = NULL;
        }
+out:
        *result = blob;
        return ret;
 }
@@ -275,14 +275,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 +305,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++;
@@ -388,7 +388,7 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
                for (i = 0; i < num_bytes; i++, cp++)
                        bn_size = (bn_size << 8) + *cp;
        }
-       PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, bn_size);
+       PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, (unsigned)bn_size);
        gret = gcry_mpi_scan(bn, GCRYMPI_FMT_STD, cp, bn_size, NULL);
        if (gret) {
                PARA_ERROR_LOG("%s while scanning n\n",
@@ -429,7 +429,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++;
@@ -586,7 +586,7 @@ static int get_asn_public_key(const char *key_file, struct asymmetric_key **resu
        key->num_bytes = n_size;
        *result = key;
        ret = n_size;
-       PARA_INFO_LOG("successfully read %u bit asn public key\n", n_size * 8);
+       PARA_INFO_LOG("successfully read %d bit asn public key\n", n_size * 8);
 
 release_e:
        gcry_mpi_release(e);
@@ -605,14 +605,10 @@ 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);
-       if (size > INT_MAX / 4)
-               return -ERRNO_TO_PARA_ERROR(EOVERFLOW);
-       blob = para_malloc(2 * size);
-       ret = uudecode((char *)data, blob, 2 * size);
+       PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
+       ret = uudecode((char *)data, size, (char **)&blob, &decoded_size);
        if (ret < 0)
                goto free_blob;
-       decoded_size = ret;
        end = blob + decoded_size;
        dump_buffer("decoded key", blob, decoded_size);
        ret = check_ssh_key_header(blob, decoded_size);
@@ -652,7 +648,7 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
                goto release_n;
        }
        ret = nr_scanned / 32 * 32;
-       PARA_INFO_LOG("successfully read %u bit ssh public key\n", ret * 8);
+       PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
 release_n:
        gcry_mpi_release(n);
 release_e:
@@ -782,6 +778,9 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
        gcry_sexp_t in, out, priv_key;
        size_t nbytes;
 
+       ret = check_key_file(key_file, true);
+       if (ret < 0)
+               return ret;
        PARA_INFO_LOG("decrypting %d byte input\n", inlen);
        /* key_file -> asymmetric key priv */
        ret = get_private_key(key_file, &priv);
@@ -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..926f47927c7617f0aa53cae4eeec7f42026ce9f6 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.
  */
@@ -14,7 +14,6 @@
 #include "sched.h"
 #include "ggo.h"
 #include "buffer_tree.h"
-#include "filter.h"
 #include "grab_client.h"
 #include "audiod.h"
 #include "error.h"
@@ -54,7 +53,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 +91,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 +107,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 +128,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 +184,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 +283,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..8e83cc53c6d55f53ccdb0a9dfbb4c89f0bb7525a 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.
  */
 #include "ggo.h"
 #include "version.h"
 
-/** define the array of error lists needed by para_gui */
-INIT_GUI_ERRLISTS;
-static char *stat_content[NUM_STAT_ITEMS];
-
-#define STANDARD_STATUS_BAR "para_gui " PACKAGE_VERSION " (hit ? for help)"
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
-static int signal_pipe;
+static char *stat_content[NUM_STAT_ITEMS];
 
-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,133 +85,69 @@ 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)
+static int find_cmd_byname(const char *name)
 {
        int i;
 
@@ -216,6 +157,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,29 +244,11 @@ 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)
 {
-       char space[] = "                                ";
-       unsigned sz = sizeof(space) - 1; /* number of spaces */
+       const char space[] = "                                ";
+       const unsigned sz = sizeof(space) - 1; /* number of spaces */
 
        while (num >= sz)  {
                waddstr(win, space);
@@ -302,8 +256,7 @@ static void add_spaces(WINDOW* win, unsigned int num)
        }
        if (num > 0) {
                assert(num < sz);
-               space[num] = '\0';
-               waddstr(win, space);
+               waddstr(win, space + sz - num);
        }
 }
 
@@ -311,97 +264,96 @@ static void add_spaces(WINDOW* win, unsigned int num)
  * print aligned string to curses window. This function always prints
  * exactly len chars.
  */
-static int align_str(WINDOW* win, char *str, unsigned int len,
+static int align_str(WINDOW* win, const char *str, unsigned int len,
                unsigned int align)
 {
-       int ret, i, num; /* of spaces */
+       int ret, num; /* of spaces */
        size_t width;
+       char *sstr; /* sanitized string */
 
        if (!win || !str)
                return 0;
-       ret = strwidth(str, &width);
+       ret = sanitize_str(str, len, &sstr, &width);
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                width = 0;
-               str[0] = '\0';
+               sstr = para_strdup(NULL);
        }
+       assert(width <= len);
        num = len - width;
-       if (num < 0) {
-               str[len] = '\0';
-               num = 0;
-       }
-       /* replace control characters by spaces */
-       for (i = 0; i < len && str[i]; i++) {
-               if (str[i] == '\n' || str[i] == '\r' || str[i] == '\f')
-                       str[i] = ' ';
-       }
        if (align == LEFT) {
-               waddstr(win, str);
+               waddstr(win, sstr);
                add_spaces(win, num);
        } else if (align == RIGHT) {
                add_spaces(win, num);
-               waddstr(win, str);
+               waddstr(win, sstr);
        } else {
                add_spaces(win, num / 2);
-               waddstr(win, str[0]? str: "");
+               waddstr(win, sstr);
                add_spaces(win, num - num / 2);
        }
+       free(sstr);
        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 +364,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 +374,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 +397,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 +417,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 +433,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 +458,84 @@ __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);
-}
-
+/* Call endwin() to reset the terminal into non-visual mode. */
 static void shutdown_curses(void)
 {
-       if (!curses_active)
-               return;
-       def_prog_mode();
-       curses_active = 0;
+       /*
+        * If para_gui received a terminating signal in external mode, the
+        * terminal can be in an unusable state at this point because the child
+        * process might not have caught the signal. In this case endwin() has
+        * already been called and must not be called again. So we first return
+        * to program mode, then call endwin().
+        */
+       if (!curses_active())
+               reset_prog_mode();
        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;
 
+       /* Kill every process in our process group. */
+       para_sigaction(SIGTERM, SIG_IGN);
+       kill(0, SIGTERM);
+       /* Wait up to two seconds for child processes to die. */
+       alarm(2);
+       while (waitpid(0, NULL, 0) >= 0)
+               ; /* nothing */
+       alarm(0);
+       /* mousemask() exists only in ncurses */
+#ifdef NCURSES_MOUSE_VERSION
+       mousemask(~(mmask_t)0, NULL); /* Avoid bad terminal state with xterm. */
+#endif
        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();
+       exit(exit_code);
 }
 
 /*
@@ -706,16 +546,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 +589,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 +609,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 +766,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 +827,353 @@ 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");
        case SIGINT:
-               PARA_WARNING_LOG("caught SIGINT, reset");
-               /* 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);
+}
 
-       ret = mark_fd_nonblocking(command_fds[0]);
+/* 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};
+
+       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,
+
+       print_in_bar(COLOR_MSG, "scrolled view: %u-%u/%u\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 +1189,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 +1203,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 +1223,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 +1232,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 +1242,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 +1252,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 +1285,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 +1292,7 @@ static void com_scroll_up(void)
                        break;
                i--;
        }
-       wrefresh(bot.win);
+       refresh_window(&bot);
        print_scroll_msg();
        return;
 err_out:
@@ -1342,30 +1321,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 +1365,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 +1394,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 +1403,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 +1424,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 various actions according to signals received. For
+ * example, it reloads the configuration file on SIGUSR1, and it shuts down the
+ * curses system on SIGTERM to restore the terminal settings before exit.
+ *
+ * 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..b72148cc41df54e79f0da53bd4853ba979674a0c 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,13 @@ 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;
+       char key_sequence[32];
+       unsigned key_sequence_length;
+       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 +52,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 +227,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 +238,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 +314,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;
@@ -328,8 +330,26 @@ static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t
        ret = 0;
        if (i9ep->caught_sigint)
                goto rm_btrn;
-       while (input_available())
+       while (input_available()) {
+               if (i9ep->stdout_btrn) {
+                       unsigned len = i9ep->key_sequence_length;
+                       assert(len < sizeof(i9ep->key_sequence) - 1);
+                       buf = i9ep->key_sequence + len;
+                       ret = read(i9ep->ici->fds[0], buf, 1);
+                       if (ret < 0) {
+                               ret = -ERRNO_TO_PARA_ERROR(errno);
+                               goto rm_btrn;
+                       }
+                       ret = -E_I9E_EOF;
+                       if (ret == 0)
+                               goto rm_btrn;
+                       buf[1] = '\0';
+                       i9ep->key_sequence_length++;
+                       rl_stuff_char((int)(unsigned char)*buf);
+               }
                rl_callback_read_char();
+               ret = 0;
+       }
        if (!i9ep->stdout_btrn)
                goto out;
        ret = btr_node_status(i9ep->stdout_btrn, 0, BTR_NT_LEAF);
@@ -369,7 +389,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 +433,30 @@ 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;
+again:
+       if (i9ep->key_sequence_length == 0)
+               return 0;
+       for (i = i9ep->num_key_bindings - 1; i >= 0; i--) {
+               if (strcmp(i9ep->key_sequence, i9ep->ici->bound_keyseqs[i]))
+                       continue;
+               i9ep->key_sequence[0] = '\0';
+               i9ep->key_sequence_length = 0;
+               ret = i9ep->ici->key_handler(i);
+               return ret < 0? ret : 0;
+       }
+       PARA_WARNING_LOG("ignoring key %d\n", i9ep->key_sequence[0]);
+       /*
+        * We received an undefined key sequence. Throw away the first byte,
+        * and try to parse the remainder.
+        */
+       memmove(i9ep->key_sequence, i9ep->key_sequence + 1,
+               i9ep->key_sequence_length); /* move also terminating zero byte */
+       i9ep->key_sequence_length--;
+       goto again;
 }
 
 /**
@@ -437,12 +468,12 @@ 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)
 {
        int ret;
 
+       memset(i9ep, 0, sizeof(struct i9e_private));
        if (!isatty(ici->fds[0]))
                return -E_I9E_SETUPTERM;
        ret = mark_fd_nonblocking(ici->fds[0]);
@@ -451,10 +482,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 +501,18 @@ 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 our dispatcher */
+               for (i = 0; (seq = ici->bound_keyseqs[i]); i++) {
+                       if (strlen(seq) >= sizeof(i9ep->key_sequence) - 1) {
+                               PARA_WARNING_LOG("ignoring overlong key %s\n",
+                                       seq);
+                               continue;
+                       }
+                       if (rl_bind_keyseq_in_map(seq,
+                                       dispatch_key, i9ep->bare_km) != 0)
+                               PARA_WARNING_LOG("could not bind #%d: %s\n", i, seq);
                }
+               i9ep->num_key_bindings = i;
        }
        if (ici->history_file)
                read_history(ici->history_file);
@@ -481,7 +520,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 +596,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..a6b718ee958815fdadff3ff4cb7206626f46540b 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,29 +34,27 @@ 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"
 default = "para_audioc -- stat -p"
 optional
 details = "
-       In order to run para_gui on a host on which no para_audiod
-       is running (hence the default command does not work), the
-       command
+       On a host on which the para_audiod service is not is running, the
+       default command will fail. An alternative is
 
                para_client -- stat -p
 
-       may be used. This command prints less information though.
-       In particular, no timing information about the current audio
-       file is printed.
+       This command connects para_server instead of para_audiod. However,
+       no timing information about the current audio file is printed.
 "
 
 #---------------------------------
 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..0db9d10e8a33d7fef84af8533fb7a6dbf545a421 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,41 +13,15 @@ $(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 $<'
-       $(Q) m4 -I $(m4_ggo_dir) $< > $@
+       $(Q) $(M4) -I $(m4_ggo_dir) $< > $@
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..48e7a1f49c299c3e8c52779c363df6b28b17e461 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,33 +271,32 @@ 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"
 optional
 default = "4"
 details = "
-       This value must be larger than the value given for above
-       dccp_data_slices_per_group above. The difference being the
-       number of redundant slices per group, i.e.  the number of
-       data packets that may be lost without causing interruptions
-       of the resulting audio stream.
+       This value must be larger than the value of the argument to
+       --dccp-data-slices-per-group. The difference of the two values is
+       the number of redundant slices, that is, the number of slices which
+       may be lost without causing interruptions in the audio stream.
 
-       Increase this value if for lossy networks.
+       Increase this value if you are on a lossy network.
 "
 
 ####################
 section "udp sender"
 ####################
 
-option "udp_target" -
+option "udp-target" -
 #~~~~~~~~~~~~~~~~~~~~
 "add udp target with optional port"
 string typestr="host[:port]"
@@ -306,7 +306,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 +317,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 +326,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 +357,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..6667cb8c8d76dc618d010696a77293c9d0f48158 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)
@@ -9,12 +9,12 @@ option "writer" w
 #~~~~~~~~~~~~~~~~
 "select stream writer"
 string typestr="name"
-default="alsa (file if alsa is unsupported)"
 optional
 multiple
 details="
-       May be give multiple times. The same writer may be specified
-       more than once.
+       May be given multiple times, and the same writer may be specified more
+       than once. If this option is not given, the first supported writer
+       is started. The list of supported writers is shown in the help output.
 "
 
 include(channels.m4)
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..92856ec3ef903eb905b0379c6eaff605bf6f8d06 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
 
@@ -187,6 +191,22 @@ static int mm_channels_score_function(__a_unused const char *path,
        return mm_compare_num_score_function(afhi->channels, private);
 }
 
+static int mm_image_id_score_function(__a_unused const char *path,
+               const struct afs_info *afsi,
+               __a_unused const struct afh_info *afhi,
+               const void *private)
+{
+       return mm_compare_num_score_function(afsi->image_id, private);
+}
+
+static int mm_lyrics_id_score_function(__a_unused const char *path,
+               const struct afs_info *afsi,
+               __a_unused const struct afh_info *afhi,
+               const void *private)
+{
+       return mm_compare_num_score_function(afsi->lyrics_id, private);
+}
+
 static int mm_num_played_score_function(__a_unused const char *path,
                const struct afs_info *afsi,
                __a_unused const struct afh_info *afhi,
@@ -204,7 +224,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;
@@ -352,5 +372,7 @@ const struct mood_method mood_methods[] = {
        {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
        {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
        {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
+       {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
+       {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},
        {.parser = NULL}
 };
diff --git a/mm.h b/mm.h
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..79f47e5a3d20801832d5ad396bbd5b3b98aeed08 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,8 @@
 #include "ipc.h"
 #include "mm.h"
 #include "sideband.h"
+#include "mood.h"
+#include "sched.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -29,7 +31,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 +411,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 +431,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 +450,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 +526,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 +653,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,32 +740,41 @@ 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)
                percent = 100;
        else if (percent < 0)
                percent = 0;
-       PARA_DEBUG_LOG("moving from rank %u to %lu%%\n", rank, percent);
+       PARA_DEBUG_LOG("moving from rank %u to %li%%\n", rank, percent);
        return score_update(aft_row, percent);
 }
 
 static void log_statistics(void)
 {
        unsigned n = statistics.num;
+       int mean_days, sigma_days;
+       /*
+        * We can not use the "now" pointer from sched.c here because we are
+        * called before schedule(), which initializes "now".
+        */
+       struct timeval rnow;
 
+       assert(current_mood);
+       PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
+               current_mood->name : "(dummy)");
        if (!n) {
-               PARA_NOTICE_LOG("no admissible files\n");
+               PARA_WARNING_LOG("no admissible files\n");
                return;
        }
-       PARA_INFO_LOG("last_played mean: %lli, last_played sigma: %llu\n",
-               (long long int)(statistics.last_played_sum / n),
-               (long long unsigned)int_sqrt(statistics.last_played_qd / n));
-       PARA_INFO_LOG("num_played mean: %lli, num_played sigma: %llu\n",
-               (long long int)statistics.num_played_sum / n,
+       PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
+       clock_get_realtime(&rnow);
+       mean_days = (rnow.tv_sec - statistics.last_played_sum / n) / 3600 / 24;
+       sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
+       PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
+       PARA_NOTICE_LOG("num_played mean/sigma: %llu/%llu\n",
+               (long long unsigned)statistics.num_played_sum / n,
                (long long unsigned)int_sqrt(statistics.num_played_qd / n));
 }
 
@@ -835,7 +807,7 @@ void close_current_mood(void)
  * \sa struct admissible_file_info, struct admissible_array, struct
  * afs_info::last_played, mood_close().
  */
-int change_current_mood(char *mood_name)
+int change_current_mood(const char *mood_name)
 {
        int i, ret;
        struct admissible_array aa = {
@@ -847,7 +819,7 @@ int change_current_mood(char *mood_name)
                struct mood *m;
                struct osl_row *row;
                struct osl_object obj = {
-                       .data = mood_name,
+                       .data = (char *)mood_name,
                        .size = strlen(mood_name) + 1
                };
                ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
@@ -869,15 +841,13 @@ int change_current_mood(char *mood_name)
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0)
                return ret;
-       log_statistics();
-       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);
                if (ret < 0)
                        goto out;
        }
-       PARA_NOTICE_LOG("loaded mood %s\n", mood_name? mood_name : "(dummy)");
+       log_statistics();
        ret = statistics.num;
 out:
        free(aa.array);
diff --git a/mood.h b/mood.h
index f97081a96c7d16d06e2f8d8ea865547c34035134..f7055753cf82fee8533f4f5fb0519ae4c86790d9 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);
+int change_current_mood(const 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..2115f71c77ce745b356b917058792d0c64db42ef 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,228 @@ 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, %u 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;
+
+       if (!id3_t)
+               return;
+       for (i = 0; i < id3_t->nframes; i++) {
+               struct id3_frame *fr = id3_t->frames[i];
+               for (j = 0; j < fr->nfields; j++) {
+                       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 {
+               PARA_NOTICE_LOG("adding id3v2 tag\n");
+               v2_tag = id3_tag_new();
+               assert(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:
+       free_tag(v1_tag);
+       free_tag(v2_tag);
+       return ret;
+}
+
+#else /* HAVE_ID3TAG */
 
 /*
  * Remove trailing whitespace from the end of a string
@@ -181,7 +391,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 +399,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 +426,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 +598,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,14 +655,13 @@ 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));
        free(afhi->chunk_table);
        return ret;
 }
@@ -471,7 +682,7 @@ static int mp3_get_file_info(char *map, size_t numbytes, int fd,
        return 1;
 }
 
-static const char* mp3_suffixes[] = {"mp3", NULL};
+static const char * const mp3_suffixes[] = {"mp3", NULL};
 
 /**
  * the init function of the mp3 audio format handler
@@ -482,4 +693,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..fa7cd4b80a7d96b26e35add01ad97d9a123f138d 100644 (file)
--- a/net.c
+++ b/net.c
@@ -1,17 +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 net.c Networking-related helper functions. */
 
-/*
- * Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined in order
- * to obtain the definition of the ucred structure.
- */
-#define _GNU_SOURCE
+#include "para.h"
 
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <netdb.h>
 
 /* At least NetBSD needs these. */
@@ -27,7 +28,6 @@
 
 #include <regex.h>
 
-#include "para.h"
 #include "error.h"
 #include "net.h"
 #include "string.h"
@@ -43,7 +43,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 +142,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 +168,15 @@ 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)
+               if (para_atoi32(++o, port) < 0 || *port < 0 || *port > 0xffff)
                        goto failed;
-
        if (host_string_ok(host))
                return host;
 failed:
@@ -187,6 +186,7 @@ failed:
 
 /**
  * Stringify port number, resolve into service name where defined.
+ *
  * \param port 2-byte port number, in host-byte-order.
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
@@ -203,7 +203,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 +328,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 +351,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, "%d", 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 +596,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 +617,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 +627,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 +636,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 +657,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 +829,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 +914,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 +945,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 +969,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 +987,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 +1003,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 +1042,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..2ddf0ee35818fc7fe7aa59ccaf04ad75e7a07502 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)
 {
@@ -81,7 +95,7 @@ static void add_ogg_page(ogg_page *og, struct vorbis_get_header_data *vghd)
        memcpy(p + og->header_len, og->body, og->body_len);
        vghd->buf = buf;
        vghd->len = new_len;
-       PARA_DEBUG_LOG("header/body/old/new: %lu/%lu/%zu/%zu\n",
+       PARA_DEBUG_LOG("header/body/old/new: %li/%li/%zu/%zu\n",
                og->header_len, og->body_len, old_len, new_len);
 }
 
@@ -157,7 +171,41 @@ fail:
        PARA_ERROR_LOG("%s\n", para_strerror(-ret));
 }
 
-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;
+}
+
+static const char * const ogg_suffixes[] = {"ogg", NULL};
 
 /**
  * The init function of the ogg vorbis audio format handler.
@@ -169,4 +217,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..adab7f481fbb0e7873c1d1e1ac4968834bd81fe3 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: %li/%li\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: %d\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 (%li 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..04be7020e57f29351b73501203db6b1bfa1dbb39 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;
@@ -163,7 +162,7 @@ open:
                goto out;
        pod->channels = ov_info(vf, 0)->channels;
        pod->sample_rate = ov_info(vf, 0)->rate;
-       PARA_NOTICE_LOG("%d channels, %d Hz\n", pod->channels,
+       PARA_NOTICE_LOG("%u channels, %u Hz\n", pod->channels,
                pod->sample_rate);
        ret = 1;
 out:
@@ -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..64eeb03c1a4bd89bb64488495ed5d76c3ca322e9 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.
  */
@@ -17,7 +17,9 @@
 #include "opus_common.h"
 #include "ogg_afh_common.h"
 
-static const char* opus_suffixes[] = {"opus", NULL};
+static const char * const 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);
@@ -53,7 +55,7 @@ static int opus_get_comments(char *comments, int length,
        p += 4;
        if (p + ntags * 4 > end)
                return -E_OPUS_COMMENT;
-       PARA_INFO_LOG("found %d tag(s)\n", ntags);
+       PARA_INFO_LOG("found %u tag(s)\n", ntags);
        for (i = 0; i < ntags; i++, p += val) {
                char *tag;
 
@@ -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..f4e18df915d657e43e9f9e583313ef962471a62a 100644 (file)
@@ -29,8 +29,6 @@
  * handler.
  */
 
-#include <ogg/ogg.h>
-
 #include "para.h"
 #include "error.h"
 #include "opus_common.h"
@@ -53,7 +51,7 @@ static int read_chars(struct packet *p, unsigned char *str, int nb_chars)
        return 1;
 }
 
-static int read_uint32(struct packet *p, ogg_uint32_t *val)
+static int read_uint32(struct packet *p, uint32_t *val)
 {
        if (p->pos > p->maxlen - 4)
                return 0;
@@ -62,7 +60,7 @@ static int read_uint32(struct packet *p, ogg_uint32_t *val)
        return 1;
 }
 
-static int read_uint16(struct packet *p, ogg_uint16_t *val)
+static int read_uint16(struct packet *p, uint16_t *val)
 {
        if (p->pos > p->maxlen - 2)
                return 0;
@@ -89,7 +87,6 @@ int opus_parse_header(const char *packet, int len, struct opus_header *h)
        char str[9];
        struct packet p;
        unsigned char ch, channel_mapping;
-       ogg_uint16_t shortval;
 
        p.data = packet;
        p.maxlen = len;
@@ -104,7 +101,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))
@@ -113,16 +110,14 @@ int opus_parse_header(const char *packet, int len, struct opus_header *h)
        if (h->channels == 0)
                return -E_OPUS_HEADER;
 
-       if (!read_uint16(&p, &shortval))
+       if (!read_uint16(&p, &h->preskip))
                return -E_OPUS_HEADER;
-       h->preskip = shortval;
 
        if (!read_uint32(&p, &h->input_sample_rate))
                return -E_OPUS_HEADER;
 
-       if (!read_uint16(&p, &shortval))
+       if (!read_uint16(&p, &h->gain))
                return -E_OPUS_HEADER;
-       h->gain = (short)shortval;
 
        if (!read_chars(&p, &ch, 1))
                return -E_OPUS_HEADER;
index cae9a6e9164de556ce5bde23de6c81aad7b476c3..2160f15192f30a8f1b5fde0be38fcd127a89681a 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.
  */
@@ -16,11 +16,11 @@ struct opus_header {
        /** 1..255 */
        int channels;
        /** Number of bytes to skip from the beginning. */
-       int preskip;
+       uint16_t preskip;
        /** Sample rate of the input stream, used by the audio format handler. */
-       ogg_uint32_t input_sample_rate;
+       uint32_t input_sample_rate;
        /** In dB, should be zero whenever possible. */
-       int gain;
+       uint16_t gain;
        /** Number of logical streams (usually 1). */
        int nb_streams;
        /** Number of streams to decode as 2 channel streams. */
index 90e65bc369571fbf9e8cde134efc07a17b76e124..282229855d0d97519f886483c249d8e3f4b00b4f 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.
  */
@@ -70,8 +70,9 @@ struct opusdec_context {
        ogg_page ogg_page;
        bool eos;
        int channels;
-       int preskip;
+       uint16_t preskip;
        bool have_opus_stream;
+       bool have_more;
        ogg_int32_t opus_serialno;
 };
 
@@ -141,9 +142,10 @@ static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx)
 static void opusdec_add_output(short *pcm, int frames_available,
                struct btr_node *btrn, struct opusdec_context *ctx)
 {
-       int tmp_skip, num_frames, bytes;
+       int num_frames, bytes;
+       uint16_t tmp_skip;
 
-       tmp_skip = PARA_MIN(ctx->preskip, frames_available);
+       tmp_skip = PARA_MIN((int)ctx->preskip, frames_available);
        ctx->preskip -= tmp_skip;
        num_frames = frames_available - tmp_skip;
        if (num_frames <= 0)
@@ -204,60 +206,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 +295,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..20186667e2f383880538275d61d1607203ed2b8a 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;
@@ -142,7 +165,7 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
                ret = -E_BAD_SAMPLERATE;
                if (100 * max > 110 * min) /* more than 10% deviation */
                        goto err;
-               PARA_NOTICE_LOG("using %dHz rather than %dHz\n", rate,
+               PARA_NOTICE_LOG("using %uHz rather than %uHz\n", rate,
                        sample_rate);
        }
        wn->min_iqs = powd->bytes_per_frame;
@@ -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..12d236391dcfb13e18d50450164517346e77398e 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"), \
@@ -251,49 +240,12 @@ enum sample_format {SAMPLE_FORMATS};
 /** Number of all loglevels. */
 #define NUM_LOGLEVELS 7
 
-/** Log messages with lower priority than that will not be compiled in. */
-#define COMPILE_TIME_LOGLEVEL 0
-
 /** \cond log */
-#if LL_DEBUG >= COMPILE_TIME_LOGLEVEL
 #define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_DEBUG_LOG(...) do {;} while (0)
-#endif
-
-#if LL_INFO >= COMPILE_TIME_LOGLEVEL
 #define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_INFO_LOG(...) do {;} while (0)
-#endif
-
-#if LL_NOTICE >= COMPILE_TIME_LOGLEVEL
 #define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_NOTICE_LOG(...) do {;} while (0)
-#endif
-
-#if LL_WARNING >= COMPILE_TIME_LOGLEVEL
 #define PARA_WARNING_LOG(f,...) para_log(LL_WARNING, "%s: " f, __FUNCTION__, ##  __VA_ARGS__)
-#else
-#define PARA_WARNING_LOG(...) do {;} while (0)
-#endif
-
-#if LL_ERROR >= COMPILE_TIME_LOGLEVEL
 #define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_ERROR_LOG(...) do {;} while (0)
-#endif
-
-#if LL_CRIT >= COMPILE_TIME_LOGLEVEL
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_CRIT_LOG(...) do {;} while (0)
-#endif
-
-#if LL_EMERG >= COMPILE_TIME_LOGLEVEL
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-#else
-#define PARA_EMERG_LOG(...)
-#endif
 /** \endcond log */
diff --git a/play.c b/play.c
index 18bfab98ca60a5e3982b6356c76fccc9650a20d8..fac551aa161d14f258e1495a4530462ca1146357 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.
  */
@@ -13,7 +13,6 @@
 #include "para.h"
 #include "list.h"
 #include "play.cmdline.h"
-#include "filter.cmdline.h"
 #include "error.h"
 #include "ggo.h"
 #include "buffer_tree.h"
@@ -37,6 +36,9 @@
  * Playlist handling is done exclusively in play context.
  */
 
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 /**
  * Describes a request to change the state of para_play.
  *
@@ -57,7 +59,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. */
@@ -96,9 +98,6 @@ struct play_task {
        char *afhi_txt;
 };
 
-/** Initialize the array of errors for para_play. */
-INIT_PLAY_ERRLISTS;
-
 /* Activate the afh receiver. */
 extern void afh_recv_init(struct receiver *r);
 #undef AFH_RECEIVER
@@ -184,10 +183,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 +242,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 +262,27 @@ 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));
 
-       decoder->close(&pt->fn);
+       task_reap(&pt->fn.task);
+       if (decoder->close)
+               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);
 }
 
@@ -322,8 +328,7 @@ static int open_new_file(struct play_task *pt)
        pt->rn.receiver = afh_recv;
        ret = afh_recv->open(&pt->rn);
        if (ret < 0) {
-               PARA_ERROR_LOG("could not open %s: %s\n", path,
-                       para_strerror(-ret));
+               PARA_ERROR_LOG("could not open %s\n", path);
                goto fail;
        }
        pt->audio_format_num = ret;
@@ -351,9 +356,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 +365,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 +375,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);
@@ -386,27 +387,40 @@ static int load_file(struct play_task *pt)
        /* set up decoding filter */
        af = audio_format_name(pt->audio_format_num);
        tmp = make_message("%sdec", af);
+       PARA_INFO_LOG("decoder: %s\n", tmp);
        ret = check_filter_arg(tmp, &pt->fn.conf);
        freep(&tmp);
        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));
-       decoder->open(&pt->fn);
+       if (decoder->open)
+               decoder->open(&pt->fn);
+       PARA_INFO_LOG("buffer tree:\n");
+       btr_log_tree(pt->rn.btrn, LL_INFO);
 
        /* 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:
@@ -441,6 +455,8 @@ again:
                pt->next_file = pt->current_file;
        ret = load_file(pt);
        if (ret < 0) {
+               PARA_ERROR_LOG("%s: marking file as invalid\n",
+                       para_strerror(-ret));
                pt->invalid[pt->next_file] = true;
                pt->rq = CRT_NONE;
                goto again;
@@ -452,7 +468,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
@@ -579,6 +596,28 @@ static char *get_key_map_seq(int key)
                get_internal_key_map_seq(key) : get_user_key_map_seq(key);
 }
 
+static char *get_key_map_seq_safe(int key)
+{
+       const char hex[] = "0123456789abcdef";
+       char *seq = get_key_map_seq(key), *sseq;
+       size_t n, len = strlen(seq);
+
+       if (len == 1 && isprint(*seq))
+               return seq;
+       sseq = para_malloc(2 + 2 * len + 1);
+       sseq[0] = '0';
+       sseq[1] = 'x';
+       for (n = 0; n < len; n++) {
+               uint8_t val = (seq[n] & 0xf0) >> 4;
+               sseq[2 + 2 * n] = hex[val];
+               val = seq[n] & 0xf;
+               sseq[2 + 2 * n + 1] = hex[val];
+       }
+       free(seq);
+       sseq[2 + 2 * n] = '\0';
+       return sseq;
+}
+
 static inline char *get_internal_key_map_cmd(int key)
 {
        return para_strdup(default_commands[get_internal_key_map_idx(key)]);
@@ -607,37 +646,31 @@ static char **get_mapped_keyseqs(void)
 
        result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
        FOR_EACH_MAPPED_KEY(i) {
-               int idx = get_key_map_idx(i);
                char *seq = get_key_map_seq(i);
-               char *cmd = get_key_map_cmd(i);
-               bool internal = is_internal_key(i);
-               PARA_DEBUG_LOG("%s key sequence #%d: %s -> %s\n",
-                       internal? "internal" : "user-defined",
-                       idx, seq, cmd);
                result[i] = seq;
-               free(cmd);
        }
        result[i] = NULL;
        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);
@@ -703,7 +736,7 @@ static int com_help(struct play_task *pt, int argc, char **argv)
                        FOR_EACH_MAPPED_KEY(i) {
                                bool internal = is_internal_key(i);
                                int idx = get_key_map_idx(i);
-                               char *seq = get_key_map_seq(i);
+                               char *seq = get_key_map_seq_safe(i);
                                char *cmd = get_key_map_cmd(i);
                                sz = xasprintf(&buf,
                                        "%s key #%d: %s -> %s\n",
@@ -755,7 +788,7 @@ static void list_file(struct play_task *pt, int num)
        char *buf;
        size_t sz;
 
-       sz = xasprintf(&buf, "%s %4u %s\n", num == pt->current_file?
+       sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
                "*" : " ", num, conf.inputs[num]);
        btr_add_output(buf, sz, pt->btrn);
 }
@@ -908,6 +941,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 +1051,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 +1120,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 +1143,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 +1167,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 +1206,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 +1216,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 +1265,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 +1279,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..8ea1854bddfc004db52d6d0afe577d6601960b07 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));
 }
 
 /**
@@ -173,13 +159,13 @@ void playlist_close(void)
  *
  * \return Standard.
  */
-int playlist_open(char *name)
+int playlist_open(const char *name)
 {
        struct osl_object obj;
        int ret;
        struct osl_row *row;
 
-       obj.data = name;
+       obj.data = (char *)name;
        obj.size = strlen(obj.data);
        ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
        if (ret < 0) {
index c72d6d4951bb784816502a982185647ef9bcfe63..4e10c2e3386d1511ac6f71bbfc47210d02852e3a 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * 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 portable_io.h Inline functions for endian-independent binary IO. */
+/** \file portable_io.h Inline functions for binary IO. */
 
 static inline uint64_t read_portable(unsigned bits, const char *buf)
 {
@@ -18,6 +18,18 @@ static inline uint64_t read_portable(unsigned bits, const char *buf)
        return ret;
 }
 
+static inline uint64_t read_portable_be(unsigned bits, const char *buf)
+{
+       uint64_t ret = 0;
+       int i, num_bytes = bits / 8;
+
+       for (i = 0; i < num_bytes; i++) {
+               unsigned char c = buf[i];
+               ret += ((uint64_t)c << (8 * (num_bytes - i - 1)));
+       }
+       return ret;
+}
+
 static inline uint64_t read_u64(const char *buf)
 {
        return read_portable(64, buf);
@@ -38,13 +50,35 @@ static inline uint8_t read_u8(const char *buf)
        return read_portable(8, buf);
 }
 
+static inline uint64_t read_u64_be(const char *buf)
+{
+       return read_portable_be(64, buf);
+}
+
+static inline uint32_t read_u32_be(const char *buf)
+{
+       return read_portable_be(32, buf);
+}
+
+static inline uint16_t read_u16_be(const char *buf)
+{
+       return read_portable_be(16, buf);
+}
+
 static inline void write_portable(unsigned bits, char *buf, uint64_t val)
 {
        int i, num_bytes = bits / 8;
-//     fprintf(stderr, "val: %lu\n", val);
        for (i = 0; i < num_bytes; i++) {
                buf[i] = val & 0xff;
-//             fprintf(stderr, "buf[%d]=%x\n", i, buf[i]);
+               val = val >> 8;
+       }
+}
+
+static inline void write_portable_be(unsigned bits, char *buf, uint64_t val)
+{
+       int i, num_bytes = bits / 8;
+       for (i = 0; i < num_bytes; i++) {
+               buf[num_bytes - i - 1] = val & 0xff;
                val = val >> 8;
        }
 }
@@ -68,3 +102,18 @@ static inline void write_u8(char *buf, uint8_t val)
 {
        write_portable(8, buf, (uint64_t) val);
 }
+
+static inline void write_u64_be(char *buf, uint64_t val)
+{
+       write_portable_be(64, buf, val);
+}
+
+static inline void write_u32_be(char *buf, uint32_t val)
+{
+       write_portable_be(32, buf, (uint64_t) val);
+}
+
+static inline void write_u16_be(char *buf, uint16_t val)
+{
+       write_portable_be(16, buf, (uint64_t) val);
+}
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..9de3033fee92925a4cd1446c8895f59055c82486 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.
  */
@@ -22,6 +22,9 @@
 #include "stdout.h"
 #include "version.h"
 
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
 extern void afh_recv_init(struct receiver *r);
 #undef AFH_RECEIVER
 #define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
@@ -34,9 +37,6 @@ static int loglevel;
 /** Always log to stderr. */
 INIT_STDERR_LOGGING(loglevel);
 
-/** init array of error codes used by para_recv */
-INIT_RECV_ERRLISTS;
-
 __noreturn static void print_help_and_die(void)
 {
        struct ggo_help h = DEFINE_GGO_HELP(recv);
@@ -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..1699ed2c03afcc84cc7b8b3e2e8a7f91aca13cc5 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);
 
@@ -128,13 +128,7 @@ static int resample_init(struct filter_node *fn)
        int ret, converter;
        struct resample_context *ctx = fn->private_data;
        struct resample_filter_args_info *conf = fn->conf;
-       struct btr_node *btrn = fn->btrn;
 
-       ret = -E_RESAMPLE_EOF;
-       if (btr_no_parent(btrn))
-               return ret;
-       if (btr_get_input_queue_size(btrn) == 0)
-               return 0;
        ret = resample_set_params(fn);
        if (ret < 0)
                return ret;
@@ -202,10 +196,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;
@@ -216,14 +210,14 @@ static int resample_post_select(__a_unused struct sched *s, struct task *t)
        ret = check_wav_post_select(ctx->cwc);
        if (ret < 0)
                goto out;
+       ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+       if (ret <= 0)
+               goto out;
        if (!ctx->src_state) {
                ret = resample_init(fn);
                if (ret <= 0)
                        goto out;
        }
-       ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret <= 0)
-               goto out;
        if (ctx->source_sample_rate == conf->dest_sample_rate_arg) {
                /*
                 * No resampling necessary. We do not splice ourselves out
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..bc301778f9a8c20d567db31b1b19f4c413fc0ed3 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 (%s)\n", t->name, t->status < 0?
+               para_strerror(-t->status) :
+               (t->status == TS_DEAD? "[dead]" : "[running]"));
+
+       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 +126,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 +143,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 +159,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 t The task to add.
- * \param s The scheduler instance to add the task to.
+ * \param tptr Identifies the task to reap.
  *
- * 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.
+ * 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.
  *
- * \sa task::pre_select, task::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 \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 +273,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 +309,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 +324,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 +361,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..a023b1526931c1eeeb3fcec7be3a6b6c72662c69 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.
- *
+ * \mainpage Main data structures and selected APIs:
  *
- * 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.
- *
- * 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,
+ *     - 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"
 #include "ggo.h"
 #include "version.h"
 
-__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
-/** Define the array of error lists needed by para_server. */
-INIT_SERVER_ERRLISTS;
+__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 
 /** Shut down non-authorized connections after that many seconds. */
 #define ALARM_TIMEOUT 10
 
 /**
  * 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,13 @@ void parse_config_or_die(int override)
                daemon_set_logfile(conf.logfile_arg);
                daemon_open_log_or_die();
        }
-       init_colors_or_die();
+
+       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+                       conf.logfile_given)) {
+               int i;
+               for (i = 0; i < conf.log_color_given; i++)
+                       daemon_set_log_color_or_die(conf.log_color_arg[i]);
+       }
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
        daemon_set_flag(DF_LOG_TIME);
@@ -261,12 +229,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 +241,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 +297,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 +351,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 +362,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 +385,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 +395,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));
@@ -446,8 +411,9 @@ static int init_afs(int argc, char **argv)
 {
        int ret, afs_server_socket[2];
        pid_t afs_pid;
+       char c;
 
-       ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
+       ret = socketpair(PF_UNIX, SOCK_STREAM, 0, afs_server_socket);
        if (ret < 0)
                exit(EXIT_FAILURE);
        get_random_bytes_or_die((unsigned char *)&afs_socket_cookie,
@@ -457,6 +423,7 @@ static int init_afs(int argc, char **argv)
                exit(EXIT_FAILURE);
        if (afs_pid == 0) { /* child (afs) */
                int i;
+
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
                sprintf(argv[0], "para_server (afs)");
@@ -465,6 +432,10 @@ static int init_afs(int argc, char **argv)
        }
        mmd->afs_pid = afs_pid;
        close(afs_server_socket[1]);
+       if (read(afs_server_socket[0], &c, 1) <= 0) {
+               PARA_EMERG_LOG("early afs exit\n");
+               exit(EXIT_FAILURE);
+       }
        ret = mark_fd_nonblocking(afs_server_socket[0]);
        if (ret < 0)
                exit(EXIT_FAILURE);
@@ -492,7 +463,7 @@ static void server_init(int argc, char **argv)
                .check_ambiguity = 0,
                .print_errors = 1
        };
-       int afs_socket;
+       int afs_socket, daemon_pipe = -1;
 
        valid_fd_012();
        init_random_seed_or_die();
@@ -502,18 +473,17 @@ 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)
-               daemonize(true /* parent waits for SIGTERM */);
+               daemon_pipe = daemonize(true /* parent waits for us */);
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
 
@@ -539,15 +509,20 @@ static void server_init(int argc, char **argv)
        PARA_NOTICE_LOG("initializing virtual streaming system\n");
        init_vss_task(afs_socket, &sched);
        init_server_command_task(argc, argv);
-       if (conf.daemon_given)
-               kill(getppid(), SIGTERM);
+       if (daemon_pipe >= 0) {
+               if (write(daemon_pipe, "\0", 1) < 0) {
+                       PARA_EMERG_LOG("daemon_pipe: %s", strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+               close(daemon_pipe);
+       }
        PARA_NOTICE_LOG("server init complete\n");
 }
 
 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;
@@ -562,7 +537,7 @@ out:
        prev_uptime = uptime;
        prev_events = mmd->events;
        mmd->vss_status_flags = mmd->new_vss_status_flags;
-       PARA_DEBUG_LOG("%d events, forcing status update\n", mmd->events);
+       PARA_DEBUG_LOG("%u events, forcing status update\n", mmd->events);
        killpg(0, SIGUSR1);
 }
 
@@ -596,6 +571,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..742f677a2568cad3001ae9cc2220344ebf7f8b1b 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,36 @@ struct signal_task {
        /** The signal pipe. */
        int fd;
        /** The associated task structure. */
-       struct task task;
+       struct task *task;
 };
 
-int para_signal_init(void);
+/**
+ * A generic pre-select method for signal tasks.
+ *
+ * \param s Passed to para_fd_set().
+ * \param context Signal task pointer.
+ *
+ * This convenience helper is called from several programs which need to handle
+ * signals, including para_server and para_audiod. These programs define a
+ * signal task structure and set its ->pre_select method to this function which
+ * adds the file descriptor of the signal task to the set of descriptors to be
+ * watched in the next select() call.
+ *
+ * Although the second parameter must be in fact a pointer to a signal_task
+ * structure, the parameter is specified as void * here to match the
+ * ->pre_select method of struct task.
+ */
+_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..4e318af19af8940570887ac3ebf8edb7a4c25b85 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.
  */
@@ -88,7 +88,7 @@ static int spx_get_comments(unsigned char *comments, int length,
        if (c + 4 > end)
                return -E_SPX_COMMENT;
        nb_fields = read_u32(c);
-       PARA_DEBUG_LOG("%d comment(s)\n", nb_fields);
+       PARA_DEBUG_LOG("%u comment(s)\n", nb_fields);
        c += 4;
        for (i = 0; i < nb_fields; i++, c += len) {
                char *tag;
@@ -118,8 +118,6 @@ static int spx_get_comments(unsigned char *comments, int length,
        return 1;
 }
 
-static const char* speex_suffixes[] = {"spx", "speex", NULL};
-
 static int spx_packet_callback(ogg_packet *packet, int packet_num,
                __a_unused int serial, struct afh_info *afhi,
                void *private_data)
@@ -163,6 +161,96 @@ 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;
+}
+
+static const char * const speex_suffixes[] = {"spx", "speex", NULL};
+
 /**
  * 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..6033a008dbf154953de673f85048eb78812d1722 100644 (file)
--- a/string.c
+++ b/string.c
@@ -1,24 +1,21 @@
 /*
- * 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.
  */
 
 /** \file string.c Memory allocation and string handling functions. */
 
-#define _GNU_SOURCE
+#include "para.h"
 
 #include <pwd.h>
 #include <sys/utsname.h> /* uname() */
-
 #include <string.h>
 #include <regex.h>
-
 #include <langinfo.h>
 #include <wchar.h>
 #include <wctype.h>
 
-#include "para.h"
 #include "string.h"
 #include "error.h"
 
  * 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 +295,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 +377,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 +559,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 +630,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)
@@ -817,15 +803,18 @@ err:
  * Split a buffer into words.
  *
  * This parser honors single and double quotes, backslash-escaped characters
- * and special characters like \p \\n. The result contains pointers to copies
- * of the words contained in \a buf and has to be freed by using \ref
- * free_argv().
+ * and special characters like \\n. The result contains pointers to copies of
+ * the words contained in buf and has to be freed by using \ref free_argv().
  *
  * \param buf The buffer to be split.
  * \param delim Each character in this string is treated as a separator.
  * \param result The array of words is returned here.
  *
- * \return Number of words in \a buf, negative on errors.
+ * It's OK to pass NULL as the buffer argument. This is equivalent to passing
+ * the empty string.
+ *
+ * \return Number of words in buf, negative on errors. The array returned
+ * through the result pointer is NULL terminated.
  */
 int create_argv(const char *buf, const char *delim, char ***result)
 {
@@ -966,36 +955,24 @@ static bool utf8_mode(void)
        return have_utf8;
 }
 
-/*
- * glibc's wcswidth returns -1 if the string contains a tab character, which
- * makes the function next to useless. The two functions below are taken from
- * mutt.
- */
-
-#define IsWPrint(wc) (iswprint(wc) || wc >= 0xa0)
-
-static int mutt_wcwidth(wchar_t wc, size_t pos)
+static int xwcwidth(wchar_t wc, size_t pos)
 {
        int n;
 
+       /* special-case for tab */
        if (wc == 0x09) /* tab */
                return (pos | 7) + 1 - pos;
        n = wcwidth(wc);
-       if (IsWPrint(wc) && n > 0)
-               return n;
-       if (!(wc & ~0x7f))
-               return 2;
-       if (!(wc & ~0xffff))
-               return 6;
-       return 10;
+       /* wcswidth() returns -1 for non-printable characters */
+       return n >= 0? n : 1;
 }
 
-static size_t mutt_wcswidth(const wchar_t *s, size_t n)
+static size_t xwcswidth(const wchar_t *s, size_t n)
 {
        size_t w = 0;
 
        while (n--)
-               w += mutt_wcwidth(*s++, w);
+               w += xwcwidth(*s++, w);
        return w;
 }
 
@@ -1040,7 +1017,7 @@ int skip_cells(const char *s, size_t cells_to_skip, size_t *bytes_to_skip)
                if (mbret == (size_t)-1 || mbret == (size_t)-2)
                        return -ERRNO_TO_PARA_ERROR(EILSEQ);
                bytes_parsed += mbret;
-               cells_skipped += mutt_wcwidth(wc, cells_skipped);
+               cells_skipped += xwcwidth(wc, cells_skipped);
        }
        *bytes_to_skip = bytes_parsed;
        return 1;
@@ -1084,12 +1061,75 @@ __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);
        assert(num_wchars > 0 && num_wchars != (size_t)-1);
-       *result = mutt_wcswidth(dest, num_wchars);
+       *result = xwcswidth(dest, num_wchars);
        free(dest);
        return 1;
 }
+
+/**
+ * Truncate and sanitize a (wide character) string.
+ *
+ * This replaces all non-printable characters by spaces and makes sure that the
+ * modified string does not exceed the given maximal width.
+ *
+ * \param src The source string in multi-byte form.
+ * \param max_width The maximal number of cells the result may occupy.
+ * \param result Sanitized multi-byte string, must be freed by caller.
+ * \param width The width of the sanitized string, always <= max_width.
+ *
+ * The function is wide-character aware but falls back to C strings for
+ * non-UTF-8 locales.
+ *
+ * \return Standard. On success, *result points to a sanitized copy of the
+ * given string. This copy was allocated with malloc() and should hence be
+ * freed when the caller is no longer interested in the result.
+ *
+ * The function fails if the given string contains an invalid multibyte
+ * sequence. In this case, *result is set to NULL, and *width to zero.
+ */
+__must_check int sanitize_str(const char *src, size_t max_width,
+               char **result, size_t *width)
+{
+       mbstate_t state;
+       static wchar_t *wcs;
+       size_t num_wchars, n;
+
+       if (!utf8_mode()) {
+               *result = para_strdup(src);
+               /* replace non-printable characters by spaces */
+               for (n = 0; n < max_width && src[n]; n++) {
+                       if (!isprint((unsigned char)src[n]))
+                               (*result)[n] = ' ';
+               }
+               (*result)[n] = '\0';
+               *width = n;
+               return 0;
+       }
+       *result = NULL;
+       *width = 0;
+       memset(&state, 0, sizeof(state));
+       num_wchars = mbsrtowcs(NULL, &src, 0, &state);
+       if (num_wchars == (size_t)-1)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       wcs = para_malloc((num_wchars + 1) * sizeof(*wcs));
+       memset(&state, 0, sizeof(state));
+       num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
+       assert(num_wchars != (size_t)-1);
+       for (n = 0; n < num_wchars && *width < max_width; n++) {
+               if (!iswprint(wcs[n]))
+                       wcs[n] = L' ';
+               *width += xwcwidth(wcs[n], *width);
+       }
+       wcs[n] = L'\0';
+       n = wcstombs(NULL, wcs, 0) + 1;
+       *result = para_malloc(n);
+       num_wchars = wcstombs(*result, wcs, n);
+       assert(num_wchars != (size_t)-1);
+       free(wcs);
+       return 1;
+}
index 935c7d456c2c5b6910b356f2ddd52fd62848cf67..52f989417d8071a1f2ed33d99ce7fd8ba18d66c1 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, (unsigned)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);
@@ -104,3 +101,5 @@ char *safe_strdup(const char *src, size_t len);
 char *key_value_copy(const char *src, size_t len, const char *key);
 int skip_cells(const char *s, size_t cells_to_skip, size_t *result);
 __must_check int strwidth(const char *s, size_t *result);
+__must_check int sanitize_str(const char *src, size_t max_width,
+               char **result, size_t *width);
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..1963748f81547ac5706b992597cc41955abffa6d 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,38 @@ 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 -b ${oggs_base[@]}"
+good[$i]='^basename:'
+
+let i++
+commands[$i]='addatt'
+required_objects[$i]=''
+cmdline[$i]="addatt $(seq 64 | tr '\n' ' ')"
+bad[$i]='.'
+
+let i++
+commands[$i]='lsatt'
+required_objects[$i]=''
+cmdline[$i]="lsatt"
+good[$i]='^1$'
+
+let i++
+commands[$i]='setatt'
+required_objects[$i]='ogg_afh'
+cmdline[$i]="setatt 33+ ${oggs[@]}"
+bad[$i]='.'
+
+let i++
+commands[$i]="ls"
+required_objects[$i]='ogg_afh'
+cmdline[$i]="ls -l=v -F ${oggs[@]}"
+good[$i]='^attributes_txt: 33'
 
 let i++
 commands[$i]="term"
@@ -74,15 +99,16 @@ 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"
+       (($? != 0)) && exit 1
 fi
 
 for ((i=0; i < ${#commands[@]}; i++)); do
@@ -114,9 +140,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..1f9913e3cf9ba49b4cb76cc5b985d914cb527b69 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
 }
@@ -156,7 +156,7 @@ test_require_executables()
 test_duration()
 {
        local t=$(exec 2>&1 1>/dev/null; time -p "$@")
-       result=$(awk '{print $2 * 1000}' <<< $t)
+       result=$(awk '{print $2 * 1000; exit 0}' <<< "$t")
 }
 
 test_expect_success()
@@ -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;;
@@ -259,17 +272,19 @@ fixup_dirs()
 {
        local wd=$(pwd)
 
-       test_dir="$wd/${0%/*}"
+       test_dir="$(realpath $wd/${0%/*})"
        test_audio_file_dir="$test_dir/audio_files"
 
        [[ -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..425118a135edfee7224f40256dc087a803115aaf 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"
@@ -45,6 +46,8 @@ struct udp_target {
        struct fec_client *fc;
        /** The FEC parameters for this target. */
        struct fec_client_parms fcp;
+       /** Whether we already sent the FEC eof packet to this target. */
+       bool sent_fec_eof;
 };
 
 static struct list_head targets;
@@ -53,15 +56,16 @@ static int sender_status;
 static void udp_close_target(struct sender_client *sc)
 {
        const char *buf;
-       size_t len = vss_get_fec_eof_packet(&buf);
-
-       /*
-        * Ignore the return value of wirte() since we are closing the target
-        * anyway. The sole purpose of the "do_nothing" statement is to silence
-        * gcc.
-        */
-       if (write(sc->fd, buf, len))
-               do_nothing;
+       size_t len;
+       struct udp_target *ut = sc->private_data;
+
+       if (ut->sent_fec_eof)
+               return;
+       PARA_NOTICE_LOG("sending FEC EOF\n");
+       len = vss_get_fec_eof_packet(&buf);
+       /* Ignore write() errors since we are closing the target anyway. */
+       if (write(sc->fd, buf, len) == len)
+               ut->sent_fec_eof = true;
 }
 
 static void udp_delete_target(struct sender_client *sc, const char *msg)
@@ -155,11 +159,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 +190,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;
 }
 
@@ -236,9 +235,11 @@ static int udp_com_delete(struct sender_command_data *scd)
 /** Initialize UDP session and set maximum payload size. */
 static int udp_init_fec(struct sender_client *sc)
 {
+       struct udp_target *ut = sc->private_data;
        int mps;
 
-       udp_init_session(sc);
+       PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
+       ut->sent_fec_eof = false;
        mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
        return mps;
@@ -281,7 +282,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 +352,7 @@ err:
        return ret;
 }
 
-static char *udp_info(void)
+static char *udp_status(void)
 {
        struct sender_client *sc;
        char *ret, *tgts = NULL;
@@ -367,11 +368,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 +423,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..3a77e98fa8c4636406035ce66f7842235832db4d 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.
  */
@@ -7,14 +7,19 @@
 /** \file user_list.h exported functions from user_list.c */
 
 /**
- * permission flags that can be set individually for any server command
+ * Flags for server commands and user permissions.
  *
- * - AFS_READ: read-only command of the audio file selector
- * - AFS_WRITE: command changes state of the audio file selector
- * - VSS_READ: command reads information about the current audio stream
- * - VSS_WRITE: command changes the current audio stream
+ * For each command, zero or more of these flags are ored to define the command
+ * permissions. A user is allowed to run the command if and only if all command
+ * permission flags are set for the user in the server.users config file which
+ * is read at server startup.
  */
-enum {AFS_READ = 1, AFS_WRITE = 2, VSS_READ = 4, VSS_WRITE = 8};
+enum server_command_permissions {
+       AFS_READ = 1, /** Read-only operation on the AFS database. */
+       AFS_WRITE = 2, /** Read-write operation on the AFS database. */
+       VSS_READ = 4, /** Read-only operation on the virtual streaming system. */
+       VSS_WRITE = 8 /** Read-write operation on the virtual streaming system. */
+};
 
 /**
  * data needed to authenticate the user
index f8f7e6a33f69a4624f347e8b2c95f88e61111bb7..bc61f54bba03aaa5a3255c6831b1bf8d6e9d2324 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) " COPYRIGHT_YEAR " 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..5484db9d9479401fe90e481f842c37a9afed8e4f 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. */
@@ -605,7 +611,7 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
                for (; i < k; i++)
                        fc->src_data[i] = (const unsigned char *)buf;
        }
-       PARA_DEBUG_LOG("FEC group %d: %d chunks (%d - %d), %d bytes\n",
+       PARA_DEBUG_LOG("FEC group %u: %u chunks (%u - %u), %u bytes\n",
                g->num, g->num_chunks, g->first_chunk,
                g->first_chunk + g->num_chunks - 1, g->bytes
        );
@@ -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)
@@ -1061,7 +1027,7 @@ static void vss_send(struct vss_task *vsst)
                }
                if (compute_next_fec_slice(fc, vsst) <= 0)
                        continue;
-               PARA_DEBUG_LOG("sending %d:%d (%u bytes)\n", fc->group.num,
+               PARA_DEBUG_LOG("sending %u:%u (%u bytes)\n", fc->group.num,
                        fc->current_slice_num, fc->group.slice_bytes);
                fc->fcp->send_fec(fc->sc, (char *)fc->enc_buf,
                        fc->group.slice_bytes + FEC_HEADER_SIZE);
@@ -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);
@@ -1196,10 +1187,14 @@ void init_vss_task(int afs_socket, struct sched *s)
                mmd->vss_status_flags |= VSS_PLAYING;
                mmd->new_vss_status_flags |= VSS_PLAYING;
                ms2tv(autoplay_delay, &tmp);
-               tv_add(now, &tmp, &vsst->autoplay_barrier);
+               tv_add(clock_get_realtime(NULL), &tmp, &vsst->autoplay_barrier);
                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..88047adbb71c2becef6afe13f45ab96ddd3b1774 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.
  */
@@ -31,7 +31,7 @@ static void make_wav_header(unsigned int channels, unsigned int sample_rate,
        int bytespersec = channels * sample_rate * BITS / 8;
        int align = channels * BITS / 8;
 
-       PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, sample_rate);
+       PARA_DEBUG_LOG("writing wave header: %u channels, %u KHz\n", channels, sample_rate);
        memset(headbuf, 0, WAV_HEADER_LEN);
        memcpy(headbuf, "RIFF", 4);
        write_u32(headbuf + 4, size - 8);
@@ -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..b226943b199a224e6b38e4685235707982e23f11 100644 (file)
        [<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>
 
 <ul>
-
-       <li> <a href="doxygen/html/index.html">API
-       Reference</a>. </li>
-
-       <li> <a href="HTML/index.html">Browsable source</a>. </li>
-
+       <li> <a href="doxygen/html/index.html">API Reference</a> </li>
 </ul>
index 393fce9352d65a48f751735b886f5f57473dbfe3..a09fd1c868f5c9aa1eabd9b004d00dc30754d256 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
@@ -17,7 +19,7 @@ provided at this point. There are several ways to download the source:
                check out any of the four integration branches maint,
                master, next, pu (see the
 
-               <a href="manual.html#git_branches">git_branches</a>
+               <a href="manual.html#Git.branches">Git branches</a>
 
                section of the manual). All previous releases
                correspond to tagged commits and may be checked out
@@ -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 0a22322..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-<h1>Events</h1>
-<hr>
-<ul>
-       <li>2016-12-31: <a href="releases/paraslash-0.4.14.tar.bz2">paraslash-0.4.14</a>
-               <a href="releases/paraslash-0.4.14.tar.bz2.asc">(sig)</a>
-               "branching oscillation"
-       </li>
-       <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>
diff --git a/web/manual.m4 b/web/manual.m4
deleted file mode 100644 (file)
index 3f5f148..0000000
+++ /dev/null
@@ -1,2208 +0,0 @@
-dnl To generate the html version, execute
-dnl m4 web/manual.m4 | grutatxt --toc
-
-define(`LOCAL_LINK_NAME', `translit(`$1', `A-Z 
-', `a-z__')')
-define(`REMOVE_NEWLINE', `translit(`$1',`
-', ` ')')
-
-define(`REFERENCE', ./``#''`LOCAL_LINK_NAME($1)' (`REMOVE_NEWLINE($2)'))
-define(`XREFERENCE', `$1' (`REMOVE_NEWLINE($2)'))
-define(`EMPH', ``_''`REMOVE_NEWLINE($1)'``_'')
-
-Paraslash user manual
-=====================
-
-This document describes how to install, configure and use the paraslash
-network audio streaming system.  Most chapters start with a chapter
-overview and conclude with an example section. We try to focus on
-general concepts and on the interaction of the various pieces of the
-paraslash package. Hence this user manual is not meant as a replacement
-for the manual pages that describe all command line options of each
-paraslash executable.
-
-------------
-Introduction
-------------
-
-In this chapter we give an REFERENCE(Overview, overview) of the
-interactions of the two main programs contained in the paraslash
-package, followed by REFERENCE(The paraslash executables, brief
-descriptions) of all executables.
-
-Overview
-~~~~~~~~
-
-The core functionality of the para suite is provided by two main
-executables, para_server and para_audiod. The former maintains a
-database of audio files and streams these files to para_audiod which
-receives and plays the stream.
-
-In a typical setting, both para_server and para_audiod act as
-background daemons whose functionality is controlled by client
-programs: the para_audioc client controls para_audiod over a local
-socket while the para_client program connects to para_server over a
-local or remote networking connection.
-
-Typically, these two daemons run on different hosts but a local setup
-is also possible.
-
-A simplified picture of a typical setup is as follows
-<<
-<pre>
- server_host                                  client_host
- ~~~~~~~~~~~                                  ~~~~~~~~~~~
- +-----------+         audio stream           +-----------+
- |para_server| -----------------------------> |para_audiod|
- +-----------+                                +-----------+
-      ^                                            ^
-      |                                            |
-      |                                            | connect
-      |                                            |
-      |                                            |
-      |                                       +-----------+
-      |                                       |para_audioc|
-      |                                       +-----------+
-      |
-      |
-      |                  connect              +-----------+
-      +-------------------------------------- |para_client|
-                                              +-----------+
-</pre>
->>
-
-The paraslash executables
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-*para_server*
-
-para_server streams binary audio data (MP3, ...) over local and/or
-remote networks. It listens on a TCP port and accepts commands such
-as play, stop, pause, next from authenticated clients. There are
-many more commands though, see the man page of para_server for a
-description of all commands.
-
-It supports three built-in network streaming protocols
-(senders/receivers): HTTP, DCCP, or UDP. This is explained in more
-detail in the section on REFERENCE(Networking, networking).
-
-The built-in audio file selector of paraslash is used to manage your
-audio files. It maintains statistics on the usage of all available
-audio files such as last-played time, and the number of times each
-file was selected.
-
-Additional information may be added to the database to allow
-fine-grained selection based on various properties of the audio file,
-including information found in (ID3) tags. However, old-fashioned
-playlists are also supported.
-
-It is also possible to store images (album covers) and lyrics in the
-database and associate these to the corresponding audio files.
-
-The section on the REFERENCE(The audio file selector, audio file
-selector) discusses this topic.
-
-
-*para_client*
-
-The client program to connect to para_server. paraslash commands
-are sent to para_server and the response is dumped to STDOUT. This
-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
-create a public/secret RSA key pair for authentication.
-
-If para_client is started without non-option arguments, an interactive
-session (shell) is started. Command history and command completion are
-supported through libreadline.
-
-*para_audiod*
-
-The local daemon that collects information from para_server.
-
-It runs on the client side and connects to para_server. As soon as
-para_server announces the availability of an audio stream, para_audiod
-starts an appropriate receiver, any number of filters and a paraslash
-writer to play the stream.
-
-Moreover, para_audiod listens on a local socket and sends status
-information about para_server and para_audiod to local clients on
-request. Access via this local socket may be restricted by using Unix
-socket credentials, if available.
-
-
-*para_audioc*
-
-The client program which talks to para_audiod. Used to control
-para_audiod, to receive status info, or to grab the stream at any
-point of the decoding process. Like para_client, para_audioc supports
-interactive sessions on systems with libreadline.
-
-*para_recv*
-
-A command line HTTP/DCCP/UDP stream grabber. The http mode is
-compatible with arbitrary HTTP streaming sources (e.g. icecast).
-In addition to the three network streaming modes, para_recv can also
-operate in local (afh) mode. In this mode it writes the content of
-an audio file on the local file system in complete chunks to stdout,
-optionally 'just in time'. This allows to cut an audio file without
-first decoding it, and it enables third-party software which is unaware
-of the particular audio format to send complete frames in real time.
-
-*para_filter*
-
-A filter program that reads from STDIN and writes to STDOUT.
-Like para_recv, this is an atomic building block which can be used to
-assemble higher-level audio receiving facilities. It combines several
-different functionalities in one tool: decoders for multiple audio
-formats and a number of processing filters, among these a normalizer
-for audio volume.
-
-*para_afh*
-
-A small stand-alone program that prints tech info about the given
-audio file to STDOUT. It can be instructed to print a "chunk table",
-an array of offsets within the audio file.
-
-*para_write*
-
-A modular audio stream writer. It supports a simple file writer
-output plug-in and optional WAV/raw players for ALSA (Linux) and for
-coreaudio (Mac OS). para_write can also be used as a stand-alone WAV
-or raw audio player.
-
-*para_play*
-
-A command line audio player.
-
-*para_gui*
-
-Curses-based gui that presents status information obtained in a curses
-window. Appearance can be customized via themes. para_gui provides
-key-bindings for the most common server commands and new key-bindings
-can be added easily.
-
-
-*para_fade*
-
-An alarm clock and volume-fader for OSS and ALSA.
-
------------
-Quick start
------------
-
-This chapter lists the REFERENCE(Requirements, necessary software)
-that must be installed to compile the paraslash package, describes
-how to REFERENCE(Installation, compile and install) the paraslash
-source code and the steps that have to be performed in order to
-REFERENCE(Quick start, set up) a typical server and client.
-
-Requirements
-~~~~~~~~~~~~
-For the impatient:
-
-       git clone git://git.tuebingen.mpg.de/osl
-       cd osl && make && sudo make install && sudo ldconfig
-       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
-
-Detailed description: In any case you'll need
-
-       - XREFERENCE(http://systemlinux.org/~maan/osl/, libosl).
-       The _object storage layer_ library is used by para_server. To
-       clone the source code repository, execute
-
-       git clone git://git.tuebingen.mpg.de/osl
-
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/gcc, gcc) or
-       XREFERENCE(http://clang.llvm.org, clang). All gcc versions
-       >= 3.3 are currently supported. Clang version 1.1 or newer
-       should work as well.
-
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/make, gnu make) is
-       also shipped with the disto. On BSD systems the gnu make
-       executable is often called gmake.
-
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/bash, bash). Some
-       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.
-
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/help2man, help2man)
-       is used to create the man pages.
-
-Optional:
-
-       - 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
-       debian-based systems. Note that libmad is not necessary on
-       the server side, i.e. for sending MP3 files.
-
-       - 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.
-
-       - XREFERENCE(http://www.xiph.org/downloads/, ogg vorbis).
-       For ogg vorbis streams you'll need libogg, libvorbis,
-       libvorbisfile. The corresponding Debian packages are called
-       libogg-dev and libvorbis-dev.
-
-       - XREFERENCE(http://www.audiocoding.com/, libfaad).  For aac
-       files (m4a) you'll need libfaad (libfaad-dev).
-
-       - XREFERENCE(http://www.speex.org/, speex). In order to stream
-       or decode speex files, libspeex (libspeex-dev) is required.
-
-       - XREFERENCE(http://flac.sourceforge.net/, flac). To stream
-       or decode files encoded with the _Free Lossless Audio Codec_,
-       libFLAC (libFLAC-dev) must be installed.
-
-       - XREFERENCE(http://www.mega-nerd.com/SRC/index.html,
-       libsamplerate). The resample filter will only be compiled if
-       this library is installed. Debian package: libsamplerate-dev.
-
-       - XREFERENCE(ftp://ftp.alsa-project.org/pub/lib/, alsa-lib). On
-       Linux, you'll need to have ALSA's development package
-       libasound2-dev installed.
-
-       - XREFERENCE(http://downloads.xiph.org/releases/ao/,
-       libao). Needed to build the ao writer (ESD, PulseAudio,...).
-       Debian package: libao-dev.
-
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/ncurses, curses). Needed
-       for para_gui. Debian package: libncurses-dev.
-
-       - XREFERENCE(http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html,
-       GNU Readline). If this library (libreadline-dev) is installed,
-       para_client, para_audioc and para_play support interactive
-       sessions.
-
-Installation
-~~~~~~~~~~~~
-
-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) > /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:
-
-       ./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
-might need to tell the configure script where to find them. Try
-
-       ./configure --help
-
-to see a list of options. If the paraslash package was compiled
-successfully, execute (optionally)
-
-       make test
-
-to run the paraslash test suite. If all tests pass, execute as root
-
-       make install
-
-to install executables under /usr/local/bin and the man pages under
-/usr/local/man.
-
-Configuration
-~~~~~~~~~~~~~
-
-*Step 1*: Create a paraslash user
-
-In order to control para_server at runtime you must create a paraslash
-user. As authentication is based on the RSA crypto system you'll have
-to create an RSA key pair. If you already have a user and an RSA key
-pair, you may skip this step.
-
-In this section we'll assume a typical setup: You would like to run
-para_server on some host called server_host as user foo, and you want
-to connect to para_server from another machine called client_host as
-user bar.
-
-As foo@server_host, create ~/.paraslash/server.users by typing the
-following commands:
-
-       user=bar
-       target=~/.paraslash/server.users
-       key=~/.paraslash/id_rsa.pub.$user
-       perms=AFS_READ,AFS_WRITE,VSS_READ,VSS_WRITE
-       mkdir -p ~/.paraslash
-       echo "user $user $key $perms" >> $target
-
-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
-
-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.
-
-para_server only needs to know the public key of the key pair just
-created. Copy this public key to server_host:
-
-       src=~/.ssh/id_rsa.pub
-       dest=.paraslash/id_rsa.pub.$LOGNAME
-       scp $src foo@server_host:$dest
-
-Finally, tell para_client to connect to server_host:
-
-       conf=~/.paraslash/client.conf
-       echo 'hostname server_host' > $conf
-
-
-*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.
-
-       para_server -l info
-
-Now you can use para_client to connect to the server and issue
-commands. Open a new shell as bar@client_host and try
-
-       para_client help
-       para_client si
-
-to retrieve the list of available commands and some server info.
-Don't proceed if this doesn't work.
-
-*Step 3*: Create and populate the database
-
-An empty database is created with
-
-       para_client init
-
-This initializes a couple of empty tables under
-~/.paraslash/afs_database-0.4. You normally don't need to look at these
-tables, but it's good to know that you can start from scratch with
-
-       rm -rf ~/.paraslash/afs_database-0.4
-
-in case something went wrong.
-
-Next, you need to add some audio files to that database so that
-para_server knows about them. Choose an absolute path to a directory
-containing some audio files and add them to the audio file table:
-
-       para_client add /my/mp3/dir
-
-This might take a while, so it is a good idea to start with a directory
-containing not too many files. Note that the table only contains data
-about the audio files found, not the files themselves.
-
-You may print the list of all known audio files with
-
-       para_client ls
-
-*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:
-
-       para_audiod -l info -r '.:http -i server_host'
-
-You should now be able to listen to the audio stream once para_server
-starts streaming. To activate streaming, execute
-
-       para_client play
-
-Since no playlist has been specified yet, the "dummy" mode which
-selects all known audio files is activated automatically. See the
-section on the REFERENCE(The audio file selector, audio file selector)
-for how to use playlists and moods to specify which files should be
-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.
-
-For simplicity we assume that you're running Linux/ALSA and that only
-MP3 files have been added to the database.
-
-       para_recv -r 'http -i server_host' > file.mp3
-       # (interrupt with CTRL+C after a few seconds)
-       ls -l file.mp3 # should not be empty
-       para_filter -f mp3dec -f wav < file.mp3 > file.wav
-       ls -l file.wav # should be much bigger than file.mp3
-       para_write -w alsa < file.wav
-
-Double check what is logged by para_server and use the --loglevel
-option of para_recv, para_filter and para_write to increase verbosity.
-
----------------
-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.
-
-In this chapter we briefly describe RSA and RC4 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).
-These sections are all about communication between the client and the
-server. Connecting para_audiod is a different matter and is described
-in a REFERENCE(Connecting para_audiod, separate section).
-
-
-
-RSA and RC4
-~~~~~~~~~~~
-
-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,
-called the public key and the private key. A message can be encrypted
-with either key and only the counterpart of that key can decrypt
-the message. While RSA can be used for both signing and encrypting
-a message, paraslash uses RSA only for the latter purpose. The
-RSA public key encryption and signatures algorithms are defined in
-detail in RFC 2437.
-
-RC4 is a stream cipher, i.e. the input is XORed with a pseudo-random
-key stream to produce the output. Decryption uses the same function
-calls as encryption. While RC4 supports variable key lengths,
-paraslash uses a fixed length of 256 bits, which is considered a
-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.
-
-Client-server authentication
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The authentication handshake between para_client and para_server goes
-as follows:
-
-       - para_client connects to para_server and sends an
-       authentication request for a user. It does so by connecting
-       to TCP port 2990 of the server host. This port is called the
-       para_server _control port_.
-
-       - para_server accepts the connection and forks a child process
-       which handles the incoming request. The parent process keeps
-       listening on the control port while the child process (also
-       called para_server below) continues as follows.
-
-       - para_server loads the RSA public key of that user, fills a
-       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
-       session key.
-
-       - para_client receives the encrypted buffer and decrypts it
-       with the user's private key, thereby obtaining the challenge
-       buffer and the session key. It sends the SHA1 hash value of
-       the challenge back to para_server and stores the session key
-       for further use.
-
-       - para_server also computes the SHA1 hash of the challenge
-       and compares it against what was sent back by the client.
-
-       - If the two hashes do not match, the authentication has
-       failed and para_server closes the connection.
-
-       - 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.
-
-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
-infeasibility to invert the SHA1 function.
-
-Neither para_server or para_client create RSA keys on their own. This
-has to be done once for each user as sketched in REFERENCE(Quick start,
-Quick start) and discussed in more detail REFERENCE(The user_list
-file, below).
-
-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.
-
-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
-while the private key is needed by para_client. Each line of the
-user list file must be of the form
-
-       user <username> <key> <perms>
-
-where _username_ is an arbitrary string (usually the user's login
-name), _key_ is the full path to that user's public RSA key, and
-_perms_ is a comma-separated list of zero or more of the following
-permission bits:
-
-       +---------------------------------------------------------+
-       | AFS_READ  | read the contents of the databases          |
-       +-----------+---------------------------------------------+
-       | AFS_WRITE | change database contents                    |
-       +-----------+---------------------------------------------+
-       | VSS_READ  | obtain information about the current stream |
-       +-----------+---------------------------------------------+
-       | VSS_WRITE | change the current stream                   |
-       +---------------------------------------------------------+
-
-The permission bits specify which commands the user is allowed to
-execute. The output of
-
-       para_client help
-
-contains in the third column the permissions needed to execute the
-command.
-
-It is possible to make para_server reread the user_list file by
-executing the paraslash "hup" command or by sending SIGHUP to the
-PID of para_server.
-
-
-Connecting para_audiod
-~~~~~~~~~~~~~~~~~~~~~~
-
-para_audiod listens on a Unix domain socket. Those sockets are
-for local communication only, so only local users can connect to
-para_audiod. The default is to let any user connect but this can be
-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
-a limited set of users.
-
------------------------
-The audio file selector
------------------------
-
-paraslash comes with a sophisticated audio file selector (AFS),
-whose main task is to determine which file to stream next, based on
-information on the audio files stored in a database. It communicates
-also with para_client whenever an AFS command is executed, for example
-to answer a database query.
-
-Besides the traditional playlists, AFS supports audio file selection
-based on _moods_ which act as a filter that limits the set of all
-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
-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
-lightweight than a full database backend.
-
-In this chapter we sketch the setup of the REFERENCE(The AFS process,
-AFS process) during server startup and proceed with the description
-of the REFERENCE(Database layout, layout) of the various database
-tables. The section on REFERENCE(Playlists and moods, playlists
-and moods) explains these two audio file selection mechanisms
-in detail and contains pratical examples. The way REFERENCE(File
-renames and content changes, file renames and content changes) are
-detected is discussed briefly before the REFERENCE(Troubleshooting,
-Troubleshooting) section concludes the chapter.
-
-The AFS process
-~~~~~~~~~~~~~~~
-
-On startup, para_server forks to create the AFS process which opens
-the OSL database tables. The server process communicates with the
-AFS process via pipes and shared memory. Usually, the AFS process
-awakes only briefly whenever the current audio file changes. The AFS
-process determines the next audio file, opens it, verifies it has
-not been changed since it was added to the database and passes the
-open file descriptor to the server process, along with audio file
-meta-data such as file name, duration, audio format and so on. The
-server process then starts to stream the audio file.
-
-The AFS process also accepts connections from local clients via
-a well-known socket. However, only child processes of para_server
-may connect through this socket. All server commands that have the
-AFS_READ or AFS_WRITE permission bits use this mechanism to query or
-change the database.
-
-Database layout
-~~~~~~~~~~~~~~~
-
-*The audio file table*
-
-This is the most important and usually also the largest table of the
-AFS database. It contains the information needed to stream each audio
-file. In particular the following data is stored for each audio file.
-
-       - SHA1 hash value of the audio file contents. This is computed
-       once when the file is added to the database. Whenever AFS
-       selects this audio file for streaming the hash value is
-       recomputed and checked against the value stored in the
-       database to detect content changes.
-
-       - The time when this audio file was last played.
-
-       - The number of times the file has been played so far.
-
-       - The attribute bitmask.
-
-       - The image id which describes the image associated with this
-       audio file.
-
-       - The lyrics id which describes the lyrics associated with
-       this audio file.
-
-       - The audio format id (MP3, OGG, ...).
-
-       - An amplification value that can be used by the amplification
-       filter to pre-amplify the decoded audio stream.
-
-       - The chunk table. It describes the location and the timing
-       of the building blocks of the audio file. This is used by
-       para_server to send chunks of the file at appropriate times.
-
-       - The duration of the audio file.
-
-       - Tag information contained in the audio file (ID3 tags,
-       Vorbis comments, ...).
-
-       - The number of channels
-
-       - The encoding bitrate.
-
-       - The sampling frequency.
-
-To add or refresh the data contained in the audio file table, the _add_
-command is used. It takes the full path of either an audio file or a
-directory. In the latter case, the directory is traversed recursively
-and all files which are recognized as valid audio files are added to
-the database.
-
-*The attribute table*
-
-The attribute table contains two columns, _name_ and _bitnum_. An
-attribute is simply a name for a certain bit number in the attribute
-bitmask of the audio file table.
-
-Each of the 64 bits of the attribute bitmask can be set for each
-audio file individually. Hence up to 64  different attributes may be
-defined. For example, "pop", "rock", "blues", "jazz", "instrumental",
-"german_lyrics", "speech", whatever. You are free to choose as
-many attributes as you like and there are no naming restrictions
-for attributes.
-
-A new attribute "test" is created by
-
-       para_client addatt test
-and
-       para_client lsatt
-
-lists all available attributes. You can set the "test" attribute for
-an audio file by executing
-
-       para_client setatt test+ /path/to/the/audio/file
-
-Similarly, the "test" bit can be removed from an audio file with
-
-       para_client setatt test- /path/to/the/audio/file
-
-Instead of a path you may use a shell wildcard pattern. The attribute
-is applied to all audio files matching this pattern:
-
-       para_client setatt test+ '/test/directory/*'
-
-The command
-
-       para_client -- ls -lv
-
-gives you a verbose listing of your audio files also showing which
-attributes are set.
-
-In case you wonder why the double-dash in the above command is needed:
-It tells para_client to not interpret the options after the dashes. If
-you find this annoying, just say
-
-       alias para='para_client --'
-
-and be happy. In what follows we shall use this alias.
-
-The "test" attribute can be dropped from the database with
-
-       para rmatt test
-
-Read the output of
-
-       para help ls
-       para help setatt
-
-for more information and a complete list of command line options to
-these commands.
-
-*Blob tables*
-
-The image, lyrics, moods and playlists tables are all blob tables.
-Blob tables consist of three columns each: The identifier which is
-a positive non-negative number that is auto-incremented, the name
-(an arbitrary string) and the content (the blob).
-
-All blob tables support the same set of actions: cat, ls, mv, rm
-and add. Of course, _add_ is used for adding new blobs to the table
-while the other actions have the same meaning as the corresponding
-Unix commands. The paraslash commands to perform these actions are
-constructed as the concatenation of the table name and the action. For
-example addimg, catimg, lsimg, mvimg, rmimg are the commands that
-manipulate or query the image table.
-
-The add variant of these commands is special as these commands read
-the blob contents from stdin. To add an image to the image table the
-command
-
-       para addimg image_name < file.jpg
-
-can be used.
-
-Note that the images and lyrics are not interpreted at all, and also
-the playlist and the mood blobs are only investigated when the mood
-or playlist is activated with the select command.
-
-*The score table*
-
-Unlike all other tables the contents of the score table remain in
-memory and are never stored on disk. The score table contains two
-columns: The SHA1 hash value (of an audio file) and its current
-score.
-
-However, only those files which are admissible for the current mood
-or playlist are contained in the score table. The audio file selector
-always chooses the row with the highest score as the file to stream
-next. While doing so, it computes the new score and updates the
-last_played and the num_played fields in the audio file table.
-
-The score table is recomputed by the select command which loads a
-mood or playlist. Audio files are chosen for streaming from the rows
-of the score table on a highest-score-first basis.
-
-
-Playlists and moods
-~~~~~~~~~~~~~~~~~~~
-
-Playlists and moods offer two different ways of specifying the set of
-admissible files. A playlist in itself describes a set of admissible
-files. A mood, in contrast, describes the set of admissible files in
-terms of attributes and other type of information available in the
-audio file table. As an example, a mood can define a filename pattern,
-which is then matched against the names of audio files in the table.
-
-*Playlists*
-
-Playlists are accommodated in the playlist table of the afs database,
-using the aforementioned blob format for tables. A new playlist is
-created with the addpl command by specifying the full (absolute)
-paths of all desired audio files, separated by newlines. Example:
-
-       find /my/mp3/dir -name "*.mp3" | para addpl my_playlist
-
-If _my_playlist_ already exists it is overwritten. To activate the
-new playlist, execute
-
-       para select p/my_playlist
-
-The audio file selector will assign scores to each entry of the list,
-in descending order so that files will be selected in order. If a
-file could not be opened for streaming, its entry is removed from
-the score table (but not from the playlist).
-
-*Moods*
-
-A mood consists of a unique name and its *mood definition*, which is
-a set of *mood lines* containing expressions in terms of attributes
-and other data contained in the database.
-
-At any time at most one mood can be *active* which means that
-para_server is going to select only files from that subset of
-admissible files.
-
-So in order to create a mood definition one has to write a set of
-mood lines. Mood lines come in three flavours: Accept lines, deny
-lines and score lines.
-
-The general syntax of the three types of mood lines is
-
-
-       accept [with score <score>] [if] [not] <mood_method> [options]
-       deny [with score <score>] [if] [not] <mood_method> [options]
-       score <score>  [if] [not] <mood_method> [options]
-
-
-Here <score> is either an integer or the string "random" which assigns
-a random score to all matching files. The score value changes the
-order in which admissible files are going to be selected, but is of
-minor importance for this introduction.
-
-So we concentrate on the first two forms, i.e. accept and deny
-lines. As usual, everything in square brackets is optional, i.e.
-accept/deny lines take the following form when ignoring scores:
-
-       accept [if] [not] <mood_method> [options]
-
-and analogously for the deny case. The "if" keyword is only syntactic
-sugar and has no function. The "not" keyword just inverts the result,
-so the essence of a mood line is the mood method part and the options
-following thereafter.
-
-A *mood method* is realized as a function which takes an audio file
-and computes a number from the data contained in the database.
-If this number is non-negative, we say the file *matches* the mood
-method. The file matches the full mood line if it either
-
-       - matches the mood method and the "not" keyword is not given,
-or
-       - does not match the mood method, but the "not" keyword is given.
-
-The set of admissible files for the whole mood is now defined as those
-files which match at least one accept mood line, but no deny mood line.
-More formally, an audio file F is admissible if and only if
-
-       (F ~ AL1 or F ~ AL2...) and not (F ~ DL1 or F ~ DN2 ...)
-
-where AL1, AL2... are the accept lines, DL1, DL2... are the deny
-lines and "~" means "matches".
-
-The cases where no mood lines of accept/deny type are defined need
-special treatment:
-
-       - Neither accept nor deny lines: This treats all files as
-       admissible (in fact, that is the definition of the dummy mood
-       which is activated automatically if no moods are available).
-
-       - Only accept lines: A file is admissible iff it matches at
-       least one accept line:
-
-               F ~ AL1 or F ~ AL2 or ...
-
-       - Only deny lines: A file is admissible iff it matches no
-       deny line:
-
-               not (F ~ DL1 or F ~ DN2 ...)
-
-
-
-*List of mood_methods*
-
-       no_attributes_set
-
-Takes no arguments and matches an audio file if and only if no
-attributes are set.
-
-       is_set <attribute_name>
-
-Takes the name of an attribute and matches iff that attribute is set.
-
-       path_matches <pattern>
-
-Takes a filename pattern and matches iff the path of the audio file
-matches the pattern.
-
-       artist_matches <pattern>
-       album_matches <pattern>
-       title_matches <pattern>
-       comment_matches <pattern>
-
-Takes an extended regular expression and matches iff the text of the
-corresponding tag of the audio file matches the pattern. If the tag
-is not set, the empty string is matched against the pattern.
-
-       year ~ <num>
-       bitrate ~ <num>
-       frequency ~ <num>
-       channels ~ <num>
-       num_played ~ <num>
-
-Takes a comparator ~ of the set {<, =, <=, >, >=, !=} and a number
-<num>. Matches an audio file iff the condition <val> ~ <num> is
-satisfied where val is the corresponding value of the audio file
-(value of the year tag, bitrate in kbit/s, frequency in Hz, channel
-count, play count).
-
-The year tag is special as its value is undefined if the audio file
-has no year tag or the content of the year tag is not a number. Such
-audio files never match. Another difference is the special treatment
-if the year tag is a two-digit number. In this case either 1900 or
-2000 is added to the tag value, depending on whether the number is
-greater than 2000 plus the current year.
-
-
-*Mood usage*
-
-To create a new mood called "my_mood", write its definition into
-some temporary file, say "tmpfile", and add it to the mood table
-by executing
-
-       para addmood my_mood < tmpfile
-
-If the mood definition is really short, you may just pipe it to the
-client instead of using temporary files. Like this:
-
-       echo "$MOOD_DEFINITION" | para addmood my_mood
-
-There is no need to keep the temporary file since you can always use
-the catmood command to get it back:
-
-       para catmood my_mood
-
-A mood can be activated by executing
-
-       para select m/my_mood
-
-Once active, the list of admissible files is shown by the ls command
-if the "-a" switch is given:
-
-       para ls -a
-
-
-*Example mood definition*
-
-Suppose you have defined attributes "punk" and "rock" and want to define
-a mood containing only Punk-Rock songs. That is, an audio file should be
-admissible if and only if both attributes are set. Since
-
-       punk and rock
-
-is obviously the same as
-
-       not (not punk or not rock)
-
-(de Morgan's rule), a mood definition that selects only Punk-Rock
-songs is
-
-       deny if not is_set punk
-       deny if not is_set rock
-
-
-
-File renames and content changes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Since the audio file selector knows the SHA1 of each audio file that
-has been added to the afs database, it recognizes if the content of
-a file has changed, e.g. because an ID3 tag was added or modified.
-Also, if a file has been renamed or moved to a different location,
-afs will detect that an entry with the same hash value already exists
-in the audio file table.
-
-In both cases it is enough to just re-add the new file. In the
-first case (file content changed), the audio table is updated, while
-metadata such as the num_played and last_played fields, as well as
-the attributes, remain unchanged. In the other case, when the file
-is moved or renamed, only the path information is updated, all other
-data remains as before.
-
-It is possible to change the behaviour of the add command by using the
-"-l" (lazy add) or the "-f" (force add) option.
-
-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.
-
-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.
-
-If you don't mind to recreate your database you can start
-from scratch by removing the entire database directory, i.e.
-
-       rm -rf ~/.paraslash/afs_database-0.4
-
-Be aware that this removes all attribute definitions, all playlists
-and all mood definitions and requires to re-initialize the tables.
-
-Although oslfsck fixes inconsistencies in database tables it doesn't
-care about the table contents. To check for invalid table contents, use
-
-       para_client check
-
-This prints out references to missing audio files as well as invalid
-playlists and mood definitions.
-
----------------------------------------
-Audio formats and audio format handlers
----------------------------------------
-
-Audio formats
-~~~~~~~~~~~~~
-
-The following audio formats are supported by paraslash:
-
-*MP3*
-
-Mp3, MPEG-1 Audio Layer 3, is a common audio format for audio storage,
-designed as part of its MPEG-1 standard.  An MP3 file is made up of
-multiple MP3 frames, which consist of a header and a data block. The
-size of an MP3 frame depends on the bit rate and on the number
-of channels. For a typical CD-audio file (sample rate of 44.1 kHz
-stereo), encoded with a bit rate of 128 kbit, an MP3 frame is about
-400 bytes large.
-
-*OGG/Vorbis*
-
-OGG is a standardized audio container format, while Vorbis is an
-open source codec for lossy audio compression. Since Vorbis is most
-commonly made available via the OGG container format, it is often
-referred to as OGG/Vorbis. The OGG container format divides data into
-chunks called OGG pages. A typical OGG page is about 4KB large. The
-Vorbis codec creates variable-bitrate (VBR) data, where the bitrate
-may vary considerably.
-
-*OGG/Speex*
-
-Speex is an open-source speech codec that is based on CELP (Code
-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.
-
-*AAC*
-
-Advanced Audio Coding (AAC) is a standardized, lossy compression
-and encoding scheme for digital audio which is the default audio
-format for Apple's iPhone, iPod, iTunes. Usually MPEG-4 is used as
-the container format and audio files encoded with AAC have the .m4a
-extension. A typical AAC frame is about 700 bytes large.
-
-*WMA*
-
-Windows Media Audio (WMA) is an audio data compression technology
-developed by Microsoft. A WMA file is usually encapsulated in the
-Advanced Systems Format (ASF) container format, which also specifies
-how meta data about the file is to be encoded. The bit stream of WMA
-is composed of superframes, each containing one or more frames of
-2048 samples. For 16 bit stereo a WMA superframe is about 8K large.
-
-*FLAC*
-
-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
-to 16K. Each frame starts with a header that contains all information
-necessary to decode the frame.
-
-Meta data
-~~~~~~~~~
-
-Unfortunately, each audio format has its own conventions how meta
-data is added as tags to the audio file.
-
-For MP3 files, ID3, version 1 and 2 are widely used. ID3 version 1
-is rather simple but also very limited as it supports only artist,
-title, album, year and comment tags. Each of these can only be at most
-32 characters long. ID3, version 2 is much more flexible but requires
-a separate library being installed for paraslash to support it.
-
-Ogg vorbis, ogg speex and flac files contain meta data as Vorbis
-comments, which are typically implemented as strings of the form
-"[TAG]=[VALUE]". Unlike ID3 version 1 tags, one may use whichever
-tags are appropriate for the content.
-
-AAC files usually use the MPEG-4 container format for storing meta
-data while WMA files wrap meta data as special objects within the
-ASF container format.
-
-paraslash only tracks the most common tags that are supported by
-all tag variants: artist, title, year, album, comment. When a file
-is added to the AFS database, the meta data of the file is extracted
-and stored in the audio file table.
-
-Chunks and chunk tables
-~~~~~~~~~~~~~~~~~~~~~~~
-
-paraslash uses the word "chunk" as common term for the building blocks
-of an audio file. For MP3 files, a chunk is the same as an MP3 frame,
-while for OGG files a chunk is an OGG page, etc.  Therefore the chunk
-size varies considerably between audio formats, from a few hundred
-bytes (MP3) up to 16K (FLAC).
-
-The chunk table contains the offsets within the audio file that
-correspond to the chunk boundaries of the file. Like the meta data,
-the chunk table is computed and stored in the database whenever an
-audio file is added.
-
-The paraslash senders (see below) always send complete chunks. The
-granularity for seeking is therefore determined by the chunk size.
-
-Audio format handlers
-~~~~~~~~~~~~~~~~~~~~~
-
-For each audio format paraslash contains an audio format handler whose
-first task is to tell whether a given file is a valid audio file of
-this type. If so, the audio file handler extracts some technical data
-(duration, sampling rate, number of channels etc.), computes the
-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.
-
-----------
-Networking
-----------
-
-Paraslash uses different network connections for control and data.
-para_client communicates with para_server over a dedicated TCP control
-connection. To transport audio data, separate data connections are
-used. For these data connections, a variety of transports (UDP, DCCP,
-HTTP) can be chosen.
-
-The chapter starts with the REFERENCE(The paraslash control
-service, control service), followed by a section on the various
-REFERENCE(Streaming protocols, streaming protocols) in which the data
-connections are described. The way audio file headers are embedded into
-the stream is discussed REFERENCE(Streams with headers and headerless
-streams, briefly) before the REFERENCE(Networking examples, example
-section) which illustrates typical commands for real-life scenarios.
-
-Both IPv4 and IPv6 are supported.
-
-The paraslash control service
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-para_server is controlled at runtime via the paraslash control
-connection. This connection is used for server commands (play, stop,
-...) as well as for afs commands (ls, select, ...).
-
-The server listens on a TCP port and accepts connections from clients
-that connect the open port. Each connection causes the server to fork
-off a client process which inherits the connection and deals with that
-client only. In this classical accept/fork approach the server process
-is unaffected if the child dies or goes crazy for whatever reason. In
-fact, the child process can not change address space of server process.
-
-The section on REFERENCE(Client-server authentication, client-server
-authentication) above described the early connection establishment
-from the crypto point of view. Here it is described what happens
-after the connection (including crypto setup) has been established.
-There are four processes involved during command dispatch as sketched
-in the following diagram.
-
-<<
-<pre>
- server_host                                   client_host
- ~~~~~~~~~~~                                   ~~~~~~~~~~~
-
- +-----------+             connect            +-----------+
- |para_server|<------------------------------ |para_client|
- +-----------+                                +-----------+
-      |                                             ^
-      |     fork   +---+                            |
-      +----------> |AFS|                            |
-      |            +---+                            |
-      |              ^                              |
-      |              |                              |
-      |              | connect (cookie)             |
-      |              |                              |
-      |              |                              |
-      |    fork   +-----+    inherited connection   |
-      +---------->|child|<--------------------------+
-                  +-----+
-</pre>
->>
-
-Note that the child process is not a child of the afs process,
-so communication of these two processes has to happen via local
-sockets. In order to avoid abuse of the local socket by unrelated
-processes, a magic cookie is created once at server startup time just
-before the server process forks off the AFS process. This cookie is
-known to the server, AFS and the child, but not to unrelated processes.
-
-There are two different kinds of commands: First there are commands
-that cause the server to respond with some answer such as the list
-of all audio files. All but the addblob commands (addimg, addlyr,
-addpl, addmood) are of this kind. The addblob commands add contents
-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.
-
-Streaming protocols
-~~~~~~~~~~~~~~~~~~~
-
-A network (audio) stream usually consists of one streaming source,
-the _sender_, and one or more _receivers_ which read data over the
-network from the streaming source.
-
-Senders are thus part of para_server while receivers are part of
-para_audiod. Moreover, there is the stand-alone tool para_recv which
-can be used to manually download a stream, either from para_server
-or from a web-based audio streaming service.
-
-The following three streaming protocols are supported by paraslash:
-
-       - HTTP. Recommended for public streams that can be played by
-       any player like mpg123, xmms, itunes, winamp, etc. The HTTP
-       sender is supported on all operating systems and all platforms.
-
-       - DCCP. Recommended for LAN streaming. DCCP is currently
-       available only for Linux.
-
-       - UDP. Recommended for multicast LAN streaming.
-
-See the Appendix on REFERENCE(Network protocols, network protocols)
-for brief descriptions of the various protocols relevant for network
-audio streaming with paraslash.
-
-It is possible to activate more than one sender simultaneously.
-Senders can be controlled at run time and via config file and command
-line options.
-
-Note that audio connections are _not_ encrypted. Transport or Internet
-layer encryption should be used if encrypted data connections are
-needed.
-
-Since DCCP and TCP are both connection-oriented protocols, connection
-establishment/teardown and access control are very similar between
-these two streaming protocols. UDP is the most lightweight option,
-since in contrast to TCP/DCCP it is connectionless. It is also the
-only protocol supporting IP multicast.
-
-The HTTP and the DCCP sender listen on a (TCP/DCCP) port waiting for
-clients to connect and establish a connection via some protocol-defined
-handshake mechanism. Both senders maintain two linked lists each:
-The list of all clients which are currently connected, and the list
-of access control entries which determines who is allowed to connect.
-IP-based access control may be configured through config file and
-command line options and via the "allow" and "deny" sender subcommands.
-
-Upon receiving a GET request from the client, the HTTP sender sends
-back a status line and a message. The body of this message is the
-audio stream. This is common practice and is supported by many popular
-clients which can thus be used to play a stream offered by para_server.
-For DCCP things are a bit simpler: No messages are exchanged between
-the receiver and sender. The client simply connects and the sender
-starts to stream.
-
-DCCP is an experimental protocol which offers a number of new features
-not available for TCP. Both ends can negotiate these features using
-a built-in negotiation mechanism. In contrast to TCP/HTTP, DCCP is
-datagram-based (no retransmissions) and thus should not be used over
-lossy media (e.g. WiFi networks). One useful feature offered by DCCP
-is access to a variety of different congestion-control mechanisms
-called CCIDs. Two different CCIDs are available per default on Linux:
-
-
-       - _CCID 2_. A Congestion Control mechanism similar to that
-       of TCP. The sender maintains a congestion window and halves
-       this window in response to congestion.
-
-
-       - _CCID-3_. Designed to be fair when competing for bandwidth.
-       It has lower variation of throughput over time compared with
-       TCP, which makes it suitable for streaming media.
-
-Unlike the HTTP and DCCP senders, the UDP sender maintains only a
-single list, the _target list_. This list describes the set of clients
-to which the stream is sent. There is no list for access control and
-no "allow" and "deny" commands for the UDP sender. Instead, the "add"
-and "delete" commands can be used to modify the target list.
-
-Since both UDP and DCCP offer an unreliable datagram-based transport,
-additional measures are necessary to guard against disruptions over
-networks that are lossy or which may be subject to interference (as
-is for instance the case with WiFi). Paraslash uses FEC (Forward
-Error Correction) to guard against packet losses and reordering. The
-stream is FEC-encoded before it is sent through the UDP socket and
-must be decoded accordingly on the receiver side.
-
-The packet size and the amount of redundancy introduced by FEC can
-be configured via the FEC parameters which are dictated by server
-and may also be configured through the "sender" command.  The FEC
-parameters are encoded in the header of each network packet, so no
-configuration is necessary on the receiver side. See the section on
-REFERENCE(Forward error correction, FEC) below.
-
-Streams with headers and headerless streams
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For OGG/Vorbis, OGG/Speex and wma streams, some of the information
-needed to decode the stream is only contained in the audio file
-header of the container format but not in each data chunk. Clients
-must be able to obtain this information in case streaming starts in
-the middle of the file or if para_audiod is started while para_server
-is already sending a stream.
-
-This is accomplished in different ways, depending on the streaming
-protocol. For connection-oriented streams (HTTP, DCCP) the audio file
-header is sent prior to audio file data. This technique however does
-not work for the connectionless UDP transport. Hence the audio file
-header is periodically being embedded into the UDP audio data stream.
-By default, the header is resent after five seconds. The receiver has
-to wait until the next header arrives before it can start decoding
-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.
-
--> Show client/target/access lists:
-
-       para_client si
-
--> Obtain general help for the sender command:
-
-       para_client help sender
-
--> Get help for a specific sender (contains further examples):
-
-       s=http # or dccp or udp
-       para_client sender $s help
-
-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.
-
--> List config file options for senders:
-
-       para_server -h
-
-All senders share the "on" and "off" commands, so senders may be
-activated and deactivated independently of each other.
-
--> Switch off the http sender:
-
-       para_client sender http off
-
--> Receive a DCCP stream using CCID2 and write the output into a file:
-
-       host=foo.org; ccid=2; filename=bar
-       para_recv --receiver "dccp --host $host --ccid $ccid" > $filename
-
-Note the quotes around the arguments for the dccp receiver. Each
-receiver has its own set of command line options and its own command
-line parser, so arguments for the dccp receiver must be protected
-from being interpreted by para_recv.
-
--> Start UDP multicast, using the default multicast address:
-
-       para_client sender udp add 224.0.1.38
-
--> Receive FEC-encoded multicast stream and write the output into a file:
-
-       filename=foo
-       para_recv -r udp > $filename
-
--> Add an UDP unicast for a client to the target list of the UDP sender:
-
-       t=client.foo.org
-       para_client sender udp add $t
-
--> Receive this (FEC-encoded) unicast stream:
-
-       filename=foo
-       para_recv -r 'udp -i 0.0.0.0' > $filename
-
--> Create a minimal config for para_audiod for HTTP streams:
-
-       c=$HOME/.paraslash/audiod.conf.min; s=server.foo.com
-       echo receiver \".:http -i $s\" > $c
-       para_audiod --config $c
-
--------
-Filters
--------
-
-A paraslash filter is a module which transforms an input stream into
-an output stream. Filters are included in the para_audiod executable
-and in the stand-alone tool para_filter which usually contains the
-same modules.
-
-While para_filter reads its input stream from STDIN and writes
-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:
-
-       Available filters:
-               compress wav amp fecdec wmadec prebuffer oggdec aacdec mp3dec
-
-Out of these filter modules, a chain of filters can be constructed,
-much in the way Unix pipes can be chained, and analogous to the use
-of modules in gstreamer: The output of the first filter becomes the
-input of the second filter. There is no limitation on the number of
-filters and the same filter may occur more than once.
-
-Like receivers, each filter has its own command line options which
-must be quoted to protect them from the command line options of
-the driving application (para_audiod or para_filter). Example:
-
-       para_filter -f 'mp3dec --ignore-crc' -f 'compress --damp 1'
-
-For para_audiod, each audio format has its own set of filters. The
-name of the audio format for which the filter should be applied can
-be used as the prefix for the filter option. Example:
-
-       para_audiod -f 'mp3:prebuffer --duration 300'
-
-The "mp3" prefix above is actually interpreted as a POSIX extended
-regular expression. Therefore
-
-       para_audiod -f '.:prebuffer --duration 300'
-
-activates the prebuffer filter for all supported audio formats (because
-"." matches all audio formats) while
-
-       para_audiod -f 'wma|ogg:prebuffer --duration 300'
-
-activates it only for wma and ogg streams.
-
-Decoders
-~~~~~~~~
-
-For each supported audio format there is a corresponding filter
-which decodes audio data in this format to 16 bit PCM data which
-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.
-
-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
-stream have to deal with much more data than filters that transform
-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.
-
-Forward error correction
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-As already mentioned REFERENCE(Streaming protocols, earlier),
-paraslash uses forward error correction (FEC) for the unreliable UDP
-and DCCP transports. FEC is a technique which was invented already
-in 1960 by Reed and Solomon and which is widely used for the parity
-calculations of storage devices (RAID arrays). It is based on the
-algebraic concept of finite fields, today called Galois fields, in
-honour of the mathematician Galois (1811-1832). The FEC implementation
-of paraslash is based on code by Luigi Rizzo.
-
-Although the details require a sound knowledge of the underlying
-mathematics, the basic idea is not hard to understand: For positive
-integers k and n with k < n it is possible to compute for any k given
-data bytes d_1, ..., d_k the corresponding r := n -k parity bytes p_1,
-..., p_r such that all data bytes can be reconstructed from *any*
-k bytes of the set
-
-       {d_1, ..., d_k, p_1, ..., p_r}.
-
-FEC-encoding for unreliable network transports boils down to slicing
-the audio stream into groups of k suitably sized pieces called _slices_
-and computing the r corresponding parity slices. This step is performed
-in para_server which then sends both the data and the parity slices
-over the unreliable network connection. If the client was able
-to receive at least k of the n = k + r slices, it can reconstruct
-(FEC-decode) the original audio stream.
-
-From these observations it is clear that there are three different
-FEC parameters: The slice size, the number of data slices k, and the
-total number of slices n. It is crucial to choose the slice size
-such that no fragmentation of network packets takes place because
-FEC only guards against losses and reordering but fails if slices are
-received partially.
-
-FEC decoding in paralash is performed through the fecdec filter which
-usually is the first filter (there can be other filters before fecdec
-if these do not alter the audio stream).
-
-
-Volume adjustment (amp and compress)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The amp and the compress filter both adjust the volume of the audio
-stream. These filters operate on uncompressed audio samples. Hence
-they are usually placed directly after the decoding filter. Each
-sample is multiplied with a scaling factor (>= 1) which makes amp
-and compress quite expensive in terms of computing power.
-
-*amp*
-
-The amp filter amplifies the audio stream by a fixed scaling factor
-that must be known in advance. For para_audiod this factor is derived
-from the amplification field of the audio file's entry in the audio
-file table while para_filter uses the value given at the command line.
-
-The optimal scaling factor F for an audio file is the largest real
-number F >= 1 such that after multiplication with F all samples still
-fit into the sample interval [-32768, 32767]. One can use para_filter
-in combination with the sox utility to compute F:
-
-       para_filter -f mp3dec -f wav < file.mp3 | sox -t wav - -e stat -v
-
-The amplification value V which is stored in the audio file table,
-however, is an integer between 0 and 255 which is connected to F
-through the formula
-
-       V = (F - 1) * 64.
-
-To store V in the audio file table, the command
-
-       para_client -- touch -a=V file.mp3
-
-is used. The reader is encouraged to write a script that performs
-these computations :)
-
-*compress*
-
-Unlike the amplification filter, the compress filter adjusts the volume
-of the audio stream dynamically without prior knowledge about the peak
-value. It maintains the maximal volume of the last n samples of the
-audio stream and computes a suitable amplification factor based on that
-value and the various configuration options. It tries to chose this
-factor such that the adjusted volume meets the desired target level.
-
-Note that it makes sense to combine amp and compress.
-
-Misc filters (wav and prebuffer)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These filters are rather simple and do not modify the audio stream at
-all. The wav filter is only useful with para_filter and in connection
-with a decoder. It asks the decoder for the number of channels and the
-sample rate of the stream and adds a Microsoft wave header containing
-this information at the beginning. This allows to write wav files
-rather than raw PCM files (which do not contain any information about
-the number of channels and the sample rate).
-
-The prebuffer filter simply delays the output until the given time has
-passed (starting from the time the first byte was available in its
-input queue) or until the given amount of data has accumulated. It
-is mainly useful for para_audiod if the standard parameters result
-in buffer underruns.
-
-Both filters require almost no additional computing time, even when
-operating on uncompressed audio streams, since data buffers are simply
-"pushed down" rather than copied.
-
-Examples
-~~~~~~~~
-
--> Decode an mp3 file to wav format:
-
-       para_filter -f mp3dec -f wav < file.mp3 > file.wav
-
--> Amplify a raw audio file by a factor of 1.5:
-
-       para_filter -f amp --amp 32 < foo.raw > bar.raw
-
-------
-Output
-------
-
-Once an audio stream has been received and decoded to PCM format,
-it can be sent to a sound device for playback. This part is performed
-by paraslash _writers_ which are described in this chapter.
-
-Writers
-~~~~~~~
-
-A paraslash writer acts as a data sink that consumes but does not
-produce audio data. Paraslash writers operate on the client side and
-are contained in para_audiod and in the stand-alone tool para_write.
-
-The para_write program reads uncompressed audio data from STDIN. If
-this data starts with a wav header, sample rate, sample format and
-channel count are read from the header. Otherwise CD audio (44.1KHz
-16 bit little endian, stereo) is assumed but this can be overridden
-by command line options. para_audiod, on the other hand, obtains
-the sample rate and the number of channels from the decoder.
-
-Like receivers and filters, each writer has an individual set of
-command line options, and for para_audiod writers can be configured
-per audio format separately. It is possible to activate more than
-one writer for the same stream simultaneously.
-
-OS-dependent APIs
-~~~~~~~~~~~~~~~~~
-
-Unfortunately, the various flavours of Unix on which paraslash
-runs on have different APIs for opening a sound device and starting
-playback. Hence for each such API there is a paraslash writer that
-can play the audio stream via this API.
-
-*ALSA*. The _Advanced Linux Sound Architecture_ is only available on
-Linux systems. Although there are several mid-layer APIs in use by
-the various Linux distributions (ESD, Jack, PulseAudio), paraslash
-currently supports only the low-level ALSA API which is not supposed
-to be change. ALSA is very feature-rich, in particular it supports
-software mixing via its DMIX plugin. ALSA is the default writer on
-Linux systems.
-
-*OSS*. The _Open Sound System_ is the only API on *BSD Unixes and
-is also available on Linux systems, usually provided by ALSA as an
-emulation for backwards compatibility. This API is rather simple but
-also limited. For example only one application can open the device
-at any time. The OSS writer is activated by default on BSD Systems.
-
-*OSX*. Mac OS X has yet another API called CoreAudio. The OSX writer
-for this API is only compiled in on such systems and is of course
-the default there.
-
-*FILE*. The file writer allows to capture the audio stream and
-write the PCM data to a file on the file system rather than playing
-it through a sound device. It is supported on all platforms and is
-always compiled in.
-
-*AO*. _Libao_ is a cross-platform audio library which supports a wide
-variety of platforms including PulseAudio (gnome), ESD (Enlightened
-Sound Daemon), AIX, Solaris and IRIX.  The ao writer plays audio
-through an output plugin of libao.
-
-Examples
-~~~~~~~~
-
--> Use the OSS writer to play a wav file:
-
-       para_write --writer oss < file.wav
-
--> Enable ALSA software mixing for mp3 streams
-
-       para_audiod --writer 'mp3:alsa -d plug:swmix'
-
-
----
-Gui
----
-
-para_gui executes an arbitrary command which is supposed to print
-status information to STDOUT. It then displays this information in
-a curses window. By default the command
-
-       para_audioc -- stat -p
-
-is executed, but this can be customized via the --stat_cmd option. In
-particular it possible to use
-
-       para_client -- stat -p
-
-to make para_gui work on systems on which para_audiod is not running.
-
-Key bindings
-~~~~~~~~~~~~
-
-It is possible to bind keys to arbitrary commands via custom
-key-bindings. Besides the internal keys which can not be changed (help,
-quit, loglevel, version...), the following flavours of key-bindings
-are supported:
-
-       - external: Shutdown curses before launching the given command.
-       Useful for starting other ncurses programs from within
-       para_gui, e.g. aumix or dialog scripts. Or, use the mbox
-       output format to write a mailbox containing one mail for each
-       (admissible) file the audio file selector knows about. Then
-       start mutt from within para_gui to browse your collection!
-
-       - display: Launch the command and display its stdout in
-       para_gui's bottom window.
-
-       - para: Like display, but start "para_client <specified
-       command>" instead of "<specified command>".
-
-The general form of a key binding is
-
-       key_map k:m:c
-
-which maps key k to command c using mode m. Mode may be x, d or p
-for external, display and paraslash commands, respectively.
-
-Themes
-~~~~~~
-
-Currently there are only two themes for para_gui. It is easy, however,
-to add more themes. To create a new theme one has to define the
-position, color and geometry for for each status item that should be
-shown by this theme. See gui_theme.c for examples.
-
-The "." and "," keys are used to switch between themes.
-
-Examples
-~~~~~~~~
-
--> Show server info:
-
-       key_map "i:p:si"
-
--> Jump to the middle of the current audio file by pressing F5:
-
-       key_map "<F5>:p:jmp 50"
-
--> vi-like bindings for jumping around:
-
-       key_map "l:p:ff 10"
-       key_map "h:p:ff 10-"
-       key_map "w:p:ff 60"
-       key_map "b:p:ff 60-"
-
--> Print the current date and time:
-
-       key_map "D:d:date"
-
--> Call other curses programs:
-
-       key_map "U:x:aumix"
-       key_map "!:x:/bin/bash"
-       key_map "^E:x:/bin/sh -c 'vi ~/.paraslash/gui.conf'"
-
------------
-Development
------------
-
-Tools
-~~~~~
-
-In order to compile the sources from the git repository (rather than
-from tar balls) and for contributing non-trivial changes to the
-paraslash project, some additional tools should be installed on a
-developer machine.
-
-http://git.or.cz/ (git). As described in more detail REFERENCE(Git
-branches, below), the git source code management tool is used for
-paraslash development. It is necessary for cloning the git repository
-and for getting updates.
-
-ftp://ftp.gnu.org/pub/gnu/m4/ (m4). Some input files for gengetopt
-are generated from templates by the m4 macro processor.
-
-ftp://ftp.gnu.org/pub/gnu/autoconf/ (autoconf) GNU autoconf creates
-the configure file which is shipped in the tarballs but has to be
-generated when compiling from git.
-
-http://www.triptico.com/software/grutatxt.html (grutatxt). The
-HTML version of this manual and some of the paraslash web pages are
-generated by the grutatxt plain text to HTML converter. If changes
-are made to these text files the grutatxt package must be installed
-to regenerate the HTML files.
-
-http://www.stack.nl/~dimitri/doxygen/ (doxygen). The documentation
-of paraslash's C sources uses the doxygen documentation system. The
-conventions for documenting the source code is described in the
-REFERENCE(Doxygen, Doxygen section).
-
-ftp://ftp.gnu.org/pub/gnu/global (global). This is used to generate
-browsable HTML from the C sources. It is needed by doxygen.
-
-Git branches
-~~~~~~~~~~~~
-
-Paraslash has been developed using the git source code management
-tool since 2006. Development is organized roughly in the same spirit
-as the git development itself, as described below.
-
-The following text passage is based on "A note from the maintainer",
-written by Junio C Hamano, the maintainer of git.
-
-There are four branches in the paraslash repository that track the
-source tree: "master", "maint", "next", and "pu".
-
-The "master" branch is meant to contain what is well tested and
-ready to be used in a production setting. There could occasionally be
-minor breakages or brown paper bag bugs but they are not expected to
-be anything major, and more importantly quickly and easily fixable.
-Every now and then, a "feature release" is cut from the tip of this
-branch, named with three dotted decimal digits, like 0.4.2.
-
-Whenever changes are about to be included that will eventually lead to
-a new major release (e.g. 0.5.0), a "maint" branch is forked off from
-"master" at that point. Obvious, safe and urgent fixes after the major
-release are applied to this branch and maintenance releases are cut
-from it. New features never go to this branch. This branch is also
-merged into "master" to propagate the fixes forward.
-
-A trivial and safe enhancement goes directly on top of "master".
-New development does not usually happen on "master", however.
-Instead, a separate topic branch is forked from the tip of "master",
-and it first is tested in isolation; Usually there are a handful such
-topic branches that are running ahead of "master". The tip of these
-branches is not published in the public repository to keep the number
-of branches that downstream developers need to worry about low.
-
-The quality of topic branches varies widely. Some of them start out as
-"good idea but obviously is broken in some areas" and then with some
-more work become "more or less done and can now be tested by wider
-audience". Luckily, most of them start out in the latter, better shape.
-
-The "next" branch is to merge and test topic branches in the latter
-category.  In general, this branch always contains the tip of "master".
-It might not be quite rock-solid production ready, but is expected to
-work more or less without major breakage. The maintainer usually uses
-the "next" version of paraslash for his own pleasure, so it cannot
-be _that_ broken. The "next" branch is where new and exciting things
-take place.
-
-The two branches "master" and "maint" are never rewound, and "next"
-usually will not be either (this automatically means the topics that
-have been merged into "next" are usually not rebased, and you can find
-the tip of topic branches you are interested in from the output of
-"git log next"). You should be able to safely build on top of them.
-
-However, at times "next" will be rebuilt from the tip of "master" to
-get rid of merge commits that will never be in "master". The commit
-that replaces "next" will usually have the identical tree, but it
-will have different ancestry from the tip of "master".
-
-The "pu" (proposed updates) branch bundles the remainder of the
-topic branches.  The "pu" branch, and topic branches that are only in
-"pu", are subject to rebasing in general.  By the above definition
-of how "next" works, you can tell that this branch will contain quite
-experimental and obviously broken stuff.
-
-When a topic that was in "pu" proves to be in testable shape, it
-graduates to "next".  This is done with
-
-        git checkout next
-       git merge that-topic-branch
-
-Sometimes, an idea that looked promising turns out to be not so good
-and the topic can be dropped from "pu" in such a case.
-
-A topic that is in "next" is expected to be polished to perfection
-before it is merged to "master".  Similar to the above, this is
-done with
-
-        git checkout master
-       git merge that-topic-branch
-       git branch -d that-topic-branch
-
-Note that being in "next" is not a guarantee to appear in the next
-release (being in "master" is such a guarantee, unless it is later
-found seriously broken and reverted), nor even in any future release.
-
-Coding Style
-~~~~~~~~~~~~
-
-The preferred coding style for paraslash coincides more or less
-with the style of the Linux kernel. So rather than repeating what is
-written XREFERENCE(http://www.kernel.org/doc/Documentation/CodingStyle,
-there), here are the most important points.
-
-       - Burn the GNU coding standards.
-       - Never use spaces for indentation.
-       - Tabs are 8 characters, and thus indentations are also 8 characters.
-       - Don't put multiple assignments on a single line.
-       - Avoid tricky expressions.
-       - Don't leave whitespace at the end of lines.
-       - The limit on the length of lines is 80 columns.
-       - Use K&R style for placing braces and spaces:
-
-               if (x is true) {
-                       we do y
-               }
-
-       - Use a space after (most) keywords.
-       - Do not add spaces around (inside) parenthesized expressions.
-       - Use one space around (on each side of) most binary and ternary operators.
-       - Do not use cute names like ThisVariableIsATemporaryCounter, call it tmp.
-       - Mixed-case names are frowned upon.
-       - Descriptive names for global variables are a must.
-       - Avoid typedefs.
-       - Functions should be short and sweet, and do just one thing.
-       - The number of local variables shouldn't exceed 10.
-       - Gotos are fine if they improve readability and reduce nesting.
-       - Don't use C99-style "// ..." comments.
-       - Names of macros defining constants and labels in enums are capitalized.
-       - Enums are preferred when defining several related constants.
-       - Always use the paraslash wrappers for allocating memory.
-       - If the name of a function is an action or an imperative.
-         command, the function should return an error-code integer
-         (<0 means error, >=0 means success). If the name is a
-         predicate, the function should return a "succeeded" boolean.
-
-
-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.
-
-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.
-
-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.
-
-No doxygen comments are necessary for static functions and for
-structures and enumerations in C files (which are used only within
-this file). This does not mean, however, that those entities need
-no documentation at all. Instead, common sense should be applied to
-document what is not obvious from reading the code.
-
---------
-Appendix
---------
-
-Network protocols
-~~~~~~~~~~~~~~~~~
-
-*IP*. The _Internet Protocol_ is the primary networking protocol
-used for the Internet. All protocols described below use IP as the
-underlying layer. Both the prevalent IPv4 and the next-generation
-IPv6 variant are being deployed actively worldwide.
-
-*Connection-oriented and connectionless protocols*. Connectionless
-protocols differ from connection-oriented ones in that state
-associated with the sending/receiving endpoints is treated
-implicitly. Connectionless protocols maintain no internal knowledge
-about the state of the connection. Hence they are not capable of
-reacting to state changes, such as sudden loss or congestion on the
-connection medium. Connection-oriented protocols, in contrast, make
-this knowledge explicit. The connection is established only after
-a bidirectional handshake which requires both endpoints to agree
-on the state of the connection, and may also involve negotiating
-specific parameters for the particular connection. Maintaining an
-up-to-date internal state of the connection also in general means
-that the sending endpoints perform congestion control, adapting to
-qualitative changes of the connection medium.
-
-*Reliability*. In IP networking, packets can be lost, duplicated,
-or delivered out of order, and different network protocols handle
-these problems in different ways. We call a transport-layer protocol
-_reliable_, if it turns the unreliable IP delivery into an ordered,
-duplicate- and loss-free delivery of packets. Sequence numbers
-are used to discard duplicates and re-arrange packets delivered
-out-of-order. Retransmission is used to guarantee loss-free
-delivery. Unreliable protocols, in contrast, do not guarantee ordering
-or data integrity.
-
-*Classification*. With these definitions the protocols which are used
-by paraslash for steaming audio data may be classified as follows.
-
-       - HTTP/TCP: connection-oriented, reliable,
-       - UDP: connectionless, unreliable,
-       - DCCP: connection-oriented, unreliable.
-
-Below we give a short descriptions of these protocols.
-
-*TCP*. The _Transmission Control Protocol_ provides reliable,
-ordered delivery of a stream and a classic window-based congestion
-control. In contrast to UDP and DCCP (see below), TCP does not have
-record-oriented or datagram-based syntax, i.e. it provides a stream
-which is unaware and independent of any record (packet) boundaries.
-TCP is used extensively by many application layers. Besides HTTP (the
-Hypertext Transfer Protocol), also FTP (the File Transfer protocol),
-SMTP (Simple Mail Transfer Protocol), SSH (Secure Shell) all sit on
-top of TCP.
-
-*UDP*. The _User Datagram Protocol_ is the simplest transport-layer
-protocol, built as a thin layer directly on top of IP. For this reason,
-it offers the same best-effort service as IP itself, i.e. there is no
-detection of duplicate or reordered packets. Being a connectionless
-protocol, only minimal internal state about the connection is
-maintained, which means that there is no protection against packet
-loss or network congestion. Error checking and correction (if at all)
-are performed in the application.
-
-*DCCP*. The _Datagram Congestion Control Protocol_ combines the
-connection-oriented state maintenance known from TCP with the
-unreliable, datagram-based transport of UDP. This means that it
-is capable of reacting to changes in the connection by performing
-congestion control, offering multiple alternative approaches. But it
-is bound to datagram boundaries (the maximum packet size supported
-by a medium), and like UDP it lacks retransmission to protect
-against loss. Due to the use of sequence numbers, it is however
-able to react to loss (interpreted as a congestion indication) and
-to ignore out-of-order and duplicate packets. Unlike TCP it allows
-to negotiate specific, binding features for a connection, such as
-the choice of congestion control: classic, window-based congestion
-control known from TCP is available as CCID-2, rate-based, "smooth"
-congestion control is offered as CCID-3.
-
-*HTTP*. _The Hypertext Transfer Protocol_ is an application layer
-protocol on top of TCP. It is spoken by web servers and is most often
-used for web services.  However, as can be seen by the many Internet
-radio stations and YouTube/Flash videos, http is by far not limited to
-the delivery of web pages only. Being a simple request/response based
-protocol, the semantics of the protocol also allow the delivery of
-multimedia content, such as audio over http.
-
-*Multicast*. IP multicast is not really a protocol but a technique
-for one-to-many communication over an IP network. The challenge is to
-deliver information to a group of destinations simultaneously using
-the most efficient strategy to send the messages over each link of
-the network only once. This has benefits for streaming multimedia:
-the standard one-to-one unicast offered by TCP/DCCP means that
-n clients listening to the same stream also consume n-times the
-resources, whereas multicast requires to send the stream just once,
-irrespective of the number of receivers.  Since it would be costly to
-maintain state for each listening receiver, multicast often implies
-connectionless transport, which is the reason that it is currently
-only available via UDP.
-
-License
-~~~~~~~
-
-Paraslash is licensed under the GPL, version 2. Most of the code
-base has been written from scratch, and those parts are GPL V2
-throughout. Notable exceptions are FEC and the WMA decoder. See the
-corresponding source files for licencing details for these parts. Some
-code sniplets of several other third party software packages have
-been incorporated into the paraslash sources, for example log message
-coloring was taken from the git sources. These third party software
-packages are all published under the GPL or some other license
-compatible to the GPL.
-
-Acknowledgements
-~~~~~~~~~~~~~~~~
-
-Many thanks to Gerrit Renker who read an early draft of this manual
-and contributed significant improvements.
-
-----------
-References
-----------
-
-Articles
-~~~~~~~~
-       - Reed, Irving S.; Solomon, Gustave (1960),
-       XREFERENCE(http://kom.aau.dk/~heb/kurser/NOTER/KOFA01.PDF,
-       Polynomial Codes over Certain Finite Fields), Journal of the
-       Society for Industrial and Applied Mathematics (SIAM) 8 (2):
-       300-304, doi:10.1137/0108018)
-
-RFCs
-~~~~
-
-       - XREFERENCE(http://www.ietf.org/rfc/rfc768.txt, RFC 768) (1980):
-       User Datagram Protocol
-       - XREFERENCE(http://www.ietf.org/rfc/rfc791.txt, RFC 791) (1981):
-       Internet Protocol
-       - XREFERENCE(http://www.ietf.org/rfc/rfc2437.txt, RFC 2437) (1998):
-       RSA Cryptography Specifications
-       - XREFERENCE(http://www.ietf.org/rfc/rfc4340.txt, RFC 4340)
-       (2006): Datagram Congestion Control Protocol (DCCP)
-       - XREFERENCE(http://www.ietf.org/rfc/rfc4341.txt, RFC 4341) (2006):
-       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)
-
-Application web pages
-~~~~~~~~~~~~~~~~~~~~~
-
-       - XREFERENCE(http://paraslash.systemlinux.org/, paraslash)
-       - XREFERENCE(http://xmms2.org/wiki/Main_Page, xmms)
-       - XREFERENCE(http://www.mpg123.de/, mpg123)
-       - XREFERENCE(http://gstreamer.freedesktop.org/, gstreamer)
-       - XREFERENCE(http://www.icecast.org/, icecast)
-       - XREFERENCE(http://beesbuzz.biz/code/audiocompress.php, Audio Compress)
-
-External documentation
-~~~~~~~~~~~~~~~~~~~~~~
-
-       - XREFERENCE(http://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf,
-       H. Peter Anvin: The mathematics of Raid6)
-       - XREFERENCE(http://info.iet.unipi.it/~luigi/fec_ccr.ps.gz,
-       Luigi Rizzo: Effective Erasure Codes for reliable Computer
-       Communication Protocols)
-
-Code
-~~~~
-       - XREFERENCE(http://info.iet.unipi.it/~luigi/vdm.tar.gz,
-       Original FEC implementation by Luigi Rizzo)
-
diff --git a/web/manual.md b/web/manual.md
new file mode 100644 (file)
index 0000000..12454ee
--- /dev/null
@@ -0,0 +1,2248 @@
+**Paraslash user manual**
+
+This document describes how to install, configure and use the paraslash
+network audio streaming system.  Most chapters start with a chapter
+overview and conclude with an example section. We try to focus on
+general concepts and on the interaction of the various pieces of the
+paraslash package. Hence this user manual is not meant as a replacement
+for the manual pages that describe all command line options of each
+paraslash executable.
+
+============
+Introduction
+============
+
+In this chapter we give an [overview](#Overview) of the interactions of
+the two main programs contained in the paraslash package, followed by
+[brief descriptions](#The.paraslash.executables) of all executables.
+
+Overview
+--------
+
+The core functionality of the para suite is provided by two main
+executables, para_server and para_audiod. The former maintains a
+database of audio files and streams these files to para_audiod which
+receives and plays the stream.
+
+In a typical setting, both para_server and para_audiod act as
+background daemons whose functionality is controlled by client
+programs: the para_audioc client controls para_audiod over a local
+socket while the para_client program connects to para_server over a
+local or remote networking connection.
+
+Typically, these two daemons run on different hosts but a local setup
+is also possible.
+
+A simplified picture of a typical setup is as follows
+
+       server_host                                  client_host
+       ~~~~~~~~~~~                                  ~~~~~~~~~~~
+
+       +-----------+         audio stream           +-----------+
+       |para_server| -----------------------------> |para_audiod|
+       +-----------+                                +-----------+
+            ^                                            ^
+            |                                            |
+            |                                            | connect
+            |                                            |
+            |                                            |
+            |                                       +-----------+
+            |                                       |para_audioc|
+            |                                       +-----------+
+            |
+            |
+            |                  connect              +-----------+
+            +-------------------------------------- |para_client|
+                                                    +-----------+
+The paraslash executables
+-------------------------
+
+### para_server ###
+
+para_server streams binary audio data (MP3, ...) over local and/or
+remote networks. It listens on a TCP port and accepts commands such
+as play, stop, pause, next from authenticated clients. There are
+many more commands though, see the man page of para_server for a
+description of all commands.
+
+It supports three built-in network streaming protocols
+(senders/receivers): HTTP, DCCP, or UDP. This is explained in more
+detail in the section on [networking](#Networking).
+
+The built-in audio file selector of paraslash is used to manage your
+audio files. It maintains statistics on the usage of all available
+audio files such as last-played time, and the number of times each
+file was selected.
+
+Additional information may be added to the database to allow
+fine-grained selection based on various properties of the audio file,
+including information found in (ID3) tags. However, old-fashioned
+playlists are also supported.
+
+It is also possible to store images (album covers) and lyrics in the
+database and associate these to the corresponding audio files.
+
+The section on the [audio file selector](#The.audio.file.selector)
+discusses this topic.
+
+
+### para_client ###
+
+The client program to connect to para_server. paraslash commands
+are sent to para_server and the response is dumped to STDOUT. This
+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 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
+session (shell) is started. Command history and command completion are
+supported through libreadline.
+
+### para_audiod ###
+
+The local daemon that collects information from para_server.
+
+It runs on the client side and connects to para_server. As soon as
+para_server announces the availability of an audio stream, para_audiod
+starts an appropriate receiver, any number of filters and a paraslash
+writer to play the stream.
+
+Moreover, para_audiod listens on a local socket and sends status
+information about para_server and para_audiod to local clients on
+request. Access via this local socket may be restricted by using Unix
+socket credentials, if available.
+
+
+### para_audioc ###
+
+The client program which talks to para_audiod. Used to control
+para_audiod, to receive status info, or to grab the stream at any
+point of the decoding process. Like para_client, para_audioc supports
+interactive sessions on systems with libreadline.
+
+### para_recv ###
+
+A command line HTTP/DCCP/UDP stream grabber. The http mode is
+compatible with arbitrary HTTP streaming sources (e.g. icecast).
+In addition to the three network streaming modes, para_recv can also
+operate in local (afh) mode. In this mode it writes the content of
+an audio file on the local file system in complete chunks to stdout,
+optionally 'just in time'. This allows to cut an audio file without
+first decoding it, and it enables third-party software which is unaware
+of the particular audio format to send complete frames in real time.
+
+### para_filter ###
+
+A filter program that reads from STDIN and writes to STDOUT.
+Like para_recv, this is an atomic building block which can be used to
+assemble higher-level audio receiving facilities. It combines several
+different functionalities in one tool: decoders for multiple audio
+formats and a number of processing filters, among these a normalizer
+for audio volume.
+
+### para_afh ###
+
+A small stand-alone program that prints tech info about the given
+audio file to STDOUT. It can be instructed to print a "chunk table",
+an array of offsets within the audio file.
+
+### para_write ###
+
+A modular audio stream writer. It supports a simple file writer
+output plug-in and optional WAV/raw players for ALSA (Linux) and for
+coreaudio (Mac OS). para_write can also be used as a stand-alone WAV
+or raw audio player.
+
+### para_play ###
+
+A command line audio player.
+
+### para_gui ###
+
+Curses-based gui that presents status information obtained in a curses
+window. Appearance can be customized via themes. para_gui provides
+key-bindings for the most common server commands and new key-bindings
+can be added easily.
+
+### para_fade ###
+
+An alarm clock and volume-fader for OSS and ALSA.
+
+===========
+Quick start
+===========
+
+This chapter lists the [necessary software](#Requirements)
+that must be installed to compile the paraslash package, describes
+how to [compile and install](#Installation) the paraslash
+source code and the steps that have to be performed in order to
+[set up](#Configuration) a typical server and client.
+
+Requirements
+------------
+### For the impatient ###
+
+       git clone git://git.tuebingen.mpg.de/osl
+       cd osl && make && sudo make install && sudo ldconfig
+       sudo apt-get install autoconf libssl-dev help2man gengetopt m4 \
+              libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
+              libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev realpath \
+              libasound2-dev libao-dev libreadline-dev libncurses-dev \
+              libopus-dev
+
+### Detailed description ###
+
+In any case you will need
+
+- [libosl](http://people.tuebingen.mpg.de/maan/osl/). The _object
+storage layer_ library is used by para_server. To clone the source
+code repository, execute
+
+               git clone git://git.tuebingen.mpg.de/osl
+
+- [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
+[clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
+supported. Clang version 1.1 or newer should work as well.
+
+- [gnu make](ftp://ftp.gnu.org/pub/gnu/make) is also shipped with the
+disto. On BSD systems the gnu make executable is often called gmake.
+
+- [bash](ftp://ftp.gnu.org/pub/gnu/bash). Some scripts which run
+during compilation require the _Bourne again shell_.  It is most
+likely already installed.
+
+- [gengetopt](ftp://ftp.gnu.org/pub/gnu/gengetopt/) is needed to
+generate the C code for the command line parsers of all paraslash
+executables.
+
+- [help2man](ftp://ftp.gnu.org/pub/gnu/help2man) is used to create
+the man pages.
+
+- [m4](ftp://ftp.gnu.org/pub/gnu/m4/). Some source files are generated
+from templates by the m4 macro processor.
+
+Optional:
+
+- [openssl](http://www.openssl.org/) or
+[libgcrypt](ftp://ftp.gnupg.org/gcrypt/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.
+
+- [libmad](http://www.underbit.com/products/mad/). To compile in MP3
+support for paraslash, the development package must be installed. It
+is called `libmad0-dev` on debian-based systems. Note that libmad is
+not necessary on the server side, i.e., for sending MP3 files.
+
+- [libid3tag](http://www.underbit.com/products/mad/). For version-2
+ID3 tag support, you willl need the libid3tag development package
+`libid3tag0-dev`. Without libid3tag, only version-1 tags are
+recognized. The mp3 tagger also needs this library for modifying
+(id3v1 and id3v2) tags.
+
+- [ogg vorbis](http://www.xiph.org/downloads/). For ogg vorbis streams
+you need libogg, libvorbis, libvorbisfile. The corresponding Debian
+packages are called `libogg-dev` and `libvorbis-dev`.
+
+- [libfaad](http://www.audiocoding.com/). For aac files (m4a) you
+need libfaad (`libfaad-dev`).
+
+- [speex](http://www.speex.org/). In order to stream or decode speex
+files, libspeex (`libspeex-dev`) is required.
+
+- [flac](http://flac.sourceforge.net/). To stream or decode files
+encoded with the _Free Lossless Audio Codec_, libFLAC (`libFLAC-dev`)
+must be installed.
+
+- [libsamplerate](http://www.mega-nerd.com/SRC/index.html). The
+resample filter will only be compiled if this library is
+installed. Debian package: `libsamplerate-dev`.
+
+- [alsa-lib](ftp://ftp.alsa-project.org/pub/lib/). On Linux, you will
+need to have the ALSA development package `libasound2-dev` installed.
+
+- [libao](http://downloads.xiph.org/releases/ao/). Needed to build
+the ao writer (ESD, PulseAudio,...).  Debian package: `libao-dev`.
+
+- [curses](ftp://ftp.gnu.org/pub/gnu/ncurses). Needed for
+para_gui. Debian package: `libncurses-dev`.
+
+- [GNU
+Readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html). If
+this library (`libreadline-dev`) is installed, para_client, para_audioc
+and para_play support interactive sessions.
+
+Installation
+------------
+To build the sources from a tarball, execute
+
+       ./configure && make
+
+To build from git or a gitweb snapshot, run this command instead:
+
+       ./autogen.sh
+
+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
+might need to tell the configure script where to find them. Try
+
+       ./configure --help
+
+to see a list of options. If the paraslash package was compiled
+successfully, execute (optionally)
+
+       make test
+
+to run the paraslash test suite. If all tests pass, execute as root
+
+       make install
+
+to install executables under /usr/local/bin and the man pages under
+/usr/local/man.
+
+Configuration
+-------------
+
+### Create a paraslash user ###
+
+In order to control para_server at runtime you must create a paraslash
+user. As authentication is based on the RSA crypto system you'll have
+to create an RSA key pair. If you already have a user and an RSA key
+pair, you may skip this step.
+
+In this section we'll assume a typical setup: You would like to run
+para_server on some host called server_host as user foo, and you want
+to connect to para_server from another machine called client_host as
+user bar.
+
+As foo@server_host, create ~/.paraslash/server.users by typing the
+following commands:
+
+       user=bar
+       target=~/.paraslash/server.users
+       key=~/.paraslash/id_rsa.pub.$user
+       perms=AFS_READ,AFS_WRITE,VSS_READ,VSS_WRITE
+       mkdir -p ~/.paraslash
+       echo "user $user $key $perms" >> $target
+
+Next, change to the "bar" account on client_host and generate the
+key pair with the commands
+
+       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 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:
+
+       src=~/.ssh/id_rsa.pub
+       dest=.paraslash/id_rsa.pub.$LOGNAME
+       scp $src foo@server_host:$dest
+
+Finally, tell para_client to connect to server_host:
+
+       conf=~/.paraslash/client.conf
+       echo 'hostname server_host' > $conf
+
+
+### Start para_server ###
+
+For this first try, we'll use the info loglevel to make the output
+of para_server more verbose.
+
+       para_server -l info
+
+Now you can use para_client to connect to the server and issue
+commands. Open a new shell as bar@client_host and try
+
+       para_client help
+       para_client si
+
+to retrieve the list of available commands and some server info.
+Don't proceed if this doesn't work.
+
+### Create and populate the database ###
+
+An empty database is created with
+
+       para_client init
+
+This initializes a couple of empty tables under
+~/.paraslash/afs_database-0.4. You normally don't need to look at these
+tables, but it's good to know that you can start from scratch with
+
+       rm -rf ~/.paraslash/afs_database-0.4
+
+in case something went wrong.
+
+Next, you need to add some audio files to that database so that
+para_server knows about them. Choose an absolute path to a directory
+containing some audio files and add them to the audio file table:
+
+       para_client add /my/mp3/dir
+
+This might take a while, so it is a good idea to start with a directory
+containing not too many files. Note that the table only contains data
+about the audio files found, not the files themselves.
+
+You may print the list of all known audio files with
+
+       para_client ls
+
+### Configure para_audiod ###
+
+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'
+
+You should now be able to listen to the audio stream once para_server
+starts streaming. To activate streaming, execute
+
+       para_client play
+
+Since no playlist has been specified yet, the "dummy" mode which
+selects all known audio files is activated automatically. See the
+section on the [audio file selector](#The.audio.file.selector) for how
+to use playlists and moods to specify which files should be streamed
+in which order.
+
+Troubleshooting
+---------------
+
+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.
+
+       para_recv -r 'http -i server_host' > file.mp3
+       # (interrupt with CTRL+C after a few seconds)
+       ls -l file.mp3 # should not be empty
+       para_filter -f mp3dec -f wav < file.mp3 > file.wav
+       ls -l file.wav # should be much bigger than file.mp3
+       para_write -w alsa < file.wav
+
+Double check what is logged by para_server and use the --loglevel
+option of para_recv, para_filter and para_write to increase verbosity.
+
+===============
+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
+a stream cipher, either RC4 or AES in integer counter mode.
+
+In this chapter we briefly describe RSA, RC4 and AES, and sketch the
+[authentication handshake](#Client-server.authentication)
+between para_client and para_server. User management is discussed
+in the section on [the user_list file](#The.user_list.file).
+These sections are all about communication between the client and the
+server. Connecting para_audiod is a different matter and is described
+in a [separate section](#Connecting.para_audiod).
+
+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,
+called the public key and the private key. A message can be encrypted
+with either key and only the counterpart of that key can decrypt
+the message. While RSA can be used for both signing and encrypting
+a message, paraslash uses RSA only for the latter purpose. The
+RSA public key encryption and signatures algorithms are defined in
+detail in RFC 2437.
+
+RC4 is a stream cipher, i.e. the input is XORed with a pseudo-random
+key stream to produce the output. Decryption uses the same function
+calls as encryption. While RC4 supports variable key lengths,
+paraslash uses a fixed length of 256 bits, which is considered a
+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
+----------------------------
+
+The authentication handshake between para_client and para_server goes
+as follows:
+
+- para_client connects to para_server and sends an authentication
+request for a user. It does so by connecting to TCP port 2990 of the
+server host. This port is called the para_server _control port_.
+
+- para_server accepts the connection and forks a child process which
+handles the incoming request. The parent process keeps listening on the
+control port while the child process (also called para_server below)
+continues as follows.
+
+- para_server loads the RSA public key of that user, fills a
+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 session key.
+
+- para_client receives the encrypted buffer and decrypts it with the
+user's private key, thereby obtaining the challenge buffer and the
+session key. It sends the SHA1 hash value of the challenge back to
+para_server and stores the session key for further use.
+
+- para_server also computes the SHA1 hash of the challenge and compares
+it against what was sent back by the client.
+
+- If the two hashes do not match, the authentication has failed and
+para_server closes the connection.
+
+- 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 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, 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 has to be done once for each user as sketched in
+[Quick start](#Quick.start) and discussed in more detail
+[below](#The.user_list.file).
+
+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.
+
+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
+while the private key is needed by para_client. Each line of the
+user list file must be of the form
+
+       user <username> <key> <perms>
+
+where _username_ is an arbitrary string (usually the user's login
+name), _key_ is the full path to that user's public RSA key, and
+_perms_ is a comma-separated list of zero or more of the following
+permission bits:
+
+       +---------------------------------------------------------+
+       | AFS_READ  | read the contents of the databases          |
+       +-----------+---------------------------------------------+
+       | AFS_WRITE | change database contents                    |
+       +-----------+---------------------------------------------+
+       | VSS_READ  | obtain information about the current stream |
+       +-----------+---------------------------------------------+
+       | VSS_WRITE | change the current stream                   |
+       +---------------------------------------------------------+
+
+The permission bits specify which commands the user is allowed to
+execute. The output of
+
+       para_client help
+
+contains in the third column the permissions needed to execute the
+command.
+
+It is possible to make para_server reread the user_list file by
+executing the paraslash "hup" command or by sending SIGHUP to the
+PID of para_server.
+
+Connecting para_audiod
+----------------------
+
+para_audiod listens on a Unix domain socket. Those sockets are
+for local communication only, so only local users can connect to
+para_audiod. The default is to let any user connect but this can be
+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
+a limited set of users.
+
+=======================
+The audio file selector
+=======================
+
+paraslash comes with a sophisticated audio file selector (AFS),
+whose main task is to determine which file to stream next, based on
+information on the audio files stored in a database. It communicates
+also with para_client whenever an AFS command is executed, for example
+to answer a database query.
+
+Besides the traditional playlists, AFS supports audio file selection
+based on _moods_ which act as a filter that limits the set of all
+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 employs [libosl](http://people.tuebingen.mpg.de/maan/osl/), 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
+lightweight than a full database backend.
+
+In this chapter we sketch the setup of the [AFS
+process](#The.AFS.process) during server startup and proceed with the
+description of the [layout](#Database.layout) of the various database
+tables. The section on [playlists and moods](#Playlists.and.moods)
+explains these two audio file selection mechanisms in detail
+and contains pratical examples. The way [file renames and content
+changes](#File.renames.and.content.changes) are detected is discussed
+briefly before the [Troubleshooting](#Troubleshooting) section
+concludes the chapter.
+
+The AFS process
+---------------
+
+On startup, para_server forks to create the AFS process which opens
+the OSL database tables. The server process communicates with the
+AFS process via pipes and shared memory. Usually, the AFS process
+awakes only briefly whenever the current audio file changes. The AFS
+process determines the next audio file, opens it, verifies it has
+not been changed since it was added to the database and passes the
+open file descriptor to the server process, along with audio file
+meta-data such as file name, duration, audio format and so on. The
+server process then starts to stream the audio file.
+
+The AFS process also accepts connections from local clients via
+a well-known socket. However, only child processes of para_server
+may connect through this socket. All server commands that have the
+AFS_READ or AFS_WRITE permission bits use this mechanism to query or
+change the database.
+
+Database layout
+---------------
+
+### The audio file table ###
+
+This is the most important and usually also the largest table of the
+AFS database. It contains the information needed to stream each audio
+file. In particular the following data is stored for each audio file.
+
+- SHA1 hash value of the audio file contents. This is computed once
+when the file is added to the database. Whenever AFS selects this
+audio file for streaming the hash value is recomputed and checked
+against the value stored in the database to detect content changes.
+
+- The time when this audio file was last played.
+
+- The number of times the file has been played so far.
+
+- The attribute bitmask.
+
+- The image id which describes the image associated with this audio
+file.
+
+- The lyrics id which describes the lyrics associated with this
+audio file.
+
+- The audio format id (MP3, OGG, ...).
+
+- An amplification value that can be used by the amplification filter
+to pre-amplify the decoded audio stream.
+
+- The chunk table. It describes the location and the timing of the
+building blocks of the audio file. This is used by para_server to
+send chunks of the file at appropriate times.
+
+- The duration of the audio file.
+
+- Tag information contained in the audio file (ID3 tags, Vorbis
+comments, ...).
+
+- The number of channels
+
+- The encoding bitrate.
+
+- The sampling frequency.
+
+To add or refresh the data contained in the audio file table, the _add_
+command is used. It takes the full path of either an audio file or a
+directory. In the latter case, the directory is traversed recursively
+and all files which are recognized as valid audio files are added to
+the database.
+
+### The attribute table ###
+
+The attribute table contains two columns, _name_ and _bitnum_. An
+attribute is simply a name for a certain bit number in the attribute
+bitmask of the audio file table.
+
+Each of the 64 bits of the attribute bitmask can be set for each
+audio file individually. Hence up to 64  different attributes may be
+defined. For example, "pop", "rock", "blues", "jazz", "instrumental",
+"german_lyrics", "speech", whatever. You are free to choose as
+many attributes as you like and there are no naming restrictions
+for attributes.
+
+A new attribute "test" is created by
+
+       para_client addatt test
+and
+       para_client lsatt
+
+lists all available attributes. You can set the "test" attribute for
+an audio file by executing
+
+       para_client setatt test+ /path/to/the/audio/file
+
+Similarly, the "test" bit can be removed from an audio file with
+
+       para_client setatt test- /path/to/the/audio/file
+
+Instead of a path you may use a shell wildcard pattern. The attribute
+is applied to all audio files matching this pattern:
+
+       para_client setatt test+ '/test/directory/*'
+
+The command
+
+       para_client -- ls -l=v
+
+gives you a verbose listing of your audio files also showing which
+attributes are set.
+
+In case you wonder why the double-dash in the above command is needed:
+It tells para_client to not interpret the options after the dashes. If
+you find this annoying, just say
+
+       alias para='para_client --'
+
+and be happy. In what follows we shall use this alias.
+
+The "test" attribute can be dropped from the database with
+
+       para rmatt test
+
+Read the output of
+
+       para help ls
+       para help setatt
+
+for more information and a complete list of command line options to
+these commands.
+
+### Blob tables ###
+
+The image, lyrics, moods and playlists tables are all blob tables.
+Blob tables consist of three columns each: The identifier which is
+a positive number that is auto-incremented, the name (an arbitrary
+string) and the content (the blob).
+
+All blob tables support the same set of actions: cat, ls, mv, rm
+and add. Of course, _add_ is used for adding new blobs to the table
+while the other actions have the same meaning as the corresponding
+Unix commands. The paraslash commands to perform these actions are
+constructed as the concatenation of the table name and the action. For
+example addimg, catimg, lsimg, mvimg, rmimg are the commands that
+manipulate or query the image table.
+
+The add variant of these commands is special as these commands read
+the blob contents from stdin. To add an image to the image table the
+command
+
+       para addimg image_name < file.jpg
+
+can be used.
+
+Note that the images and lyrics are not interpreted at all, and also
+the playlist and the mood blobs are only investigated when the mood
+or playlist is activated with the select command.
+
+### The score table ###
+
+The score table describes those audio files which are admissible for
+the current mood or playlist (see below). The table has two columns:
+a pointer to a row of the audio file table and a score value.
+
+Unlike all other tables of the database, the score table remains in
+memory and is never stored on disk. It is initialized at startup and
+recomputed when the select command loads a new mood or playlist.
+
+When the audio file selector is asked to open the next audio file,
+it picks the row with the highest score, opens the corresponding
+file and passes the file descriptor to the virtual streaming system.
+At this point the last_played and the num_played fields of the selected
+file are updated and the score is recomputed.
+
+Playlists and moods
+-------------------
+
+Playlists and moods offer two different ways of specifying the set of
+admissible files. A playlist in itself describes a set of admissible
+files. A mood, in contrast, describes the set of admissible files in
+terms of attributes and other type of information available in the
+audio file table. As an example, a mood can define a filename pattern,
+which is then matched against the names of audio files in the table.
+
+### Playlists ###
+
+Playlists are accommodated in the playlist table of the afs database,
+using the aforementioned blob format for tables. A new playlist is
+created with the addpl command by specifying the full (absolute)
+paths of all desired audio files, separated by newlines. Example:
+
+       find /my/mp3/dir -name "*.mp3" | para addpl my_playlist
+
+If _my_playlist_ already exists it is overwritten. To activate the
+new playlist, execute
+
+       para select p/my_playlist
+
+The audio file selector will assign scores to each entry of the list,
+in descending order so that files will be selected in order. If a
+file could not be opened for streaming, its entry is removed from
+the score table (but not from the playlist).
+
+### Moods ###
+
+A mood consists of a unique name and its *mood definition*, which is
+a set of *mood lines* containing expressions in terms of attributes
+and other data contained in the database.
+
+At any time at most one mood can be *active* which means that
+para_server is going to select only files from that subset of
+admissible files.
+
+So in order to create a mood definition one has to write a set of
+mood lines. Mood lines come in three flavours: Accept lines, deny
+lines and score lines.
+
+The general syntax of the three types of mood lines is
+
+
+       accept [with score <score>] [if] [not] <mood_method> [options]
+       deny [with score <score>] [if] [not] <mood_method> [options]
+       score <score>  [if] [not] <mood_method> [options]
+
+
+Here <score> is either an integer or the string "random" which assigns
+a random score to all matching files. The score value changes the
+order in which admissible files are going to be selected, but is of
+minor importance for this introduction.
+
+So we concentrate on the first two forms, i.e. accept and deny
+lines. As usual, everything in square brackets is optional, i.e.
+accept/deny lines take the following form when ignoring scores:
+
+       accept [if] [not] <mood_method> [options]
+
+and analogously for the deny case. The "if" keyword is only syntactic
+sugar and has no function. The "not" keyword just inverts the result,
+so the essence of a mood line is the mood method part and the options
+following thereafter.
+
+A *mood method* is realized as a function which takes an audio file
+and computes a number from the data contained in the database.
+If this number is non-negative, we say the file *matches* the mood
+method. The file matches the full mood line if it either
+
+       - matches the mood method and the "not" keyword is not given,
+or
+       - does not match the mood method, but the "not" keyword is given.
+
+The set of admissible files for the whole mood is now defined as those
+files which match at least one accept mood line, but no deny mood line.
+More formally, an audio file F is admissible if and only if
+
+       (F ~ AL1 or F ~ AL2...) and not (F ~ DL1 or F ~ DN2 ...)
+
+where AL1, AL2... are the accept lines, DL1, DL2... are the deny
+lines and "~" means "matches".
+
+The cases where no mood lines of accept/deny type are defined need
+special treatment:
+
+       - Neither accept nor deny lines: This treats all files as
+       admissible (in fact, that is the definition of the dummy mood
+       which is activated automatically if no moods are available).
+
+       - Only accept lines: A file is admissible iff it matches at
+       least one accept line:
+
+               F ~ AL1 or F ~ AL2 or ...
+
+       - Only deny lines: A file is admissible iff it matches no
+       deny line:
+
+               not (F ~ DL1 or F ~ DN2 ...)
+
+
+
+### List of mood_methods ###
+
+       no_attributes_set
+
+Takes no arguments and matches an audio file if and only if no
+attributes are set.
+
+       is_set <attribute_name>
+
+Takes the name of an attribute and matches iff that attribute is set.
+
+       path_matches <pattern>
+
+Takes a filename pattern and matches iff the path of the audio file
+matches the pattern.
+
+       artist_matches <pattern>
+       album_matches <pattern>
+       title_matches <pattern>
+       comment_matches <pattern>
+
+Takes an extended regular expression and matches iff the text of the
+corresponding tag of the audio file matches the pattern. If the tag
+is not set, the empty string is matched against the pattern.
+
+       year ~ <num>
+       bitrate ~ <num>
+       frequency ~ <num>
+       channels ~ <num>
+       num_played ~ <num>
+       image_id ~ <num>
+       lyrics_id ~ <num>
+
+Takes a comparator ~ of the set {<, =, <=, >, >=, !=} and a number
+<num>. Matches an audio file iff the condition <val> ~ <num> is
+satisfied where val is the corresponding value of the audio file
+(value of the year tag, bitrate in kbit/s, etc.).
+
+The year tag is special as its value is undefined if the audio file
+has no year tag or the content of the year tag is not a number. Such
+audio files never match. Another difference is the special treatment
+if the year tag is a two-digit number. In this case either 1900 or
+2000 is added to the tag value, depending on whether the number is
+greater than 2000 plus the current year.
+
+
+### Mood usage ###
+
+To create a new mood called "my_mood", write its definition into
+some temporary file, say "tmpfile", and add it to the mood table
+by executing
+
+       para addmood my_mood < tmpfile
+
+If the mood definition is really short, you may just pipe it to the
+client instead of using temporary files. Like this:
+
+       echo "$MOOD_DEFINITION" | para addmood my_mood
+
+There is no need to keep the temporary file since you can always use
+the catmood command to get it back:
+
+       para catmood my_mood
+
+A mood can be activated by executing
+
+       para select m/my_mood
+
+Once active, the list of admissible files is shown by the ls command
+if the "-a" switch is given:
+
+       para ls -a
+
+
+### Example mood definition ###
+
+Suppose you have defined attributes "punk" and "rock" and want to define
+a mood containing only Punk-Rock songs. That is, an audio file should be
+admissible if and only if both attributes are set. Since
+
+       punk and rock
+
+is obviously the same as
+
+       not (not punk or not rock)
+
+(de Morgan's rule), a mood definition that selects only Punk-Rock
+songs is
+
+       deny if not is_set punk
+       deny if not is_set rock
+
+
+
+File renames and content changes
+--------------------------------
+
+Since the audio file selector knows the SHA1 of each audio file that
+has been added to the afs database, it recognizes if the content of
+a file has changed, e.g. because an ID3 tag was added or modified.
+Also, if a file has been renamed or moved to a different location,
+afs will detect that an entry with the same hash value already exists
+in the audio file table.
+
+In both cases it is enough to just re-add the new file. In the
+first case (file content changed), the audio table is updated, while
+metadata such as the num_played and last_played fields, as well as
+the attributes, remain unchanged. In the other case, when the file
+is moved or renamed, only the path information is updated, all other
+data remains as before.
+
+It is possible to change the behaviour of the add command by using the
+"-l" (lazy add) or the "-f" (force add) option.
+
+Troubleshooting
+---------------
+
+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:
+
+       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.
+
+       rm -rf ~/.paraslash/afs_database-0.4
+
+Be aware that this removes all attribute definitions, all playlists
+and all mood definitions and requires to re-initialize the tables.
+
+Although oslfsck fixes inconsistencies in database tables it doesn't
+care about the table contents. To check for invalid table contents, use
+
+       para_client check
+
+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
+=======================================
+
+Audio formats
+-------------
+
+The following audio formats are supported by paraslash:
+
+### MP3 ###
+
+Mp3, MPEG-1 Audio Layer 3, is a common audio format for audio storage,
+designed as part of its MPEG-1 standard.  An MP3 file is made up of
+multiple MP3 frames, which consist of a header and a data block. The
+size of an MP3 frame depends on the bit rate and on the number
+of channels. For a typical CD-audio file (sample rate of 44.1 kHz
+stereo), encoded with a bit rate of 128 kbit, an MP3 frame is about
+400 bytes large.
+
+### OGG/Vorbis ###
+
+OGG is a standardized audio container format, while Vorbis is an
+open source codec for lossy audio compression. Since Vorbis is most
+commonly made available via the OGG container format, it is often
+referred to as OGG/Vorbis. The OGG container format divides data into
+chunks called OGG pages. A typical OGG page is about 4KB large. The
+Vorbis codec creates variable-bitrate (VBR) data, where the bitrate
+may vary considerably.
+
+### OGG/Speex ###
+
+Speex is an open-source speech codec that is based on CELP (Code
+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. 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 ###
+
+Advanced Audio Coding (AAC) is a standardized, lossy compression
+and encoding scheme for digital audio which is the default audio
+format for Apple's iPhone, iPod, iTunes. Usually MPEG-4 is used as
+the container format and audio files encoded with AAC have the .m4a
+extension. A typical AAC frame is about 700 bytes large.
+
+### WMA ###
+
+Windows Media Audio (WMA) is an audio data compression technology
+developed by Microsoft. A WMA file is usually encapsulated in the
+Advanced Systems Format (ASF) container format, which also specifies
+how meta data about the file is to be encoded. The bit stream of WMA
+is composed of superframes, each containing one or more frames of
+2048 samples. For 16 bit stereo a WMA superframe is about 8K large.
+
+### FLAC ###
+
+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 consists of frames of varying size, up
+to 16K. Each frame starts with a header that contains all information
+necessary to decode the frame.
+
+Meta data
+---------
+
+Unfortunately, each audio format has its own conventions how meta
+data is added as tags to the audio file.
+
+For MP3 files, ID3, version 1 and 2 are widely used. ID3 version 1
+is rather simple but also very limited as it supports only artist,
+title, album, year and comment tags. Each of these can only be at most
+32 characters long. ID3, version 2 is much more flexible but requires
+a separate library being installed for paraslash to support it.
+
+Ogg vorbis, ogg speex and flac files contain meta data as Vorbis
+comments, which are typically implemented as strings of the form
+"[TAG]=[VALUE]". Unlike ID3 version 1 tags, one may use whichever
+tags are appropriate for the content.
+
+AAC files usually use the MPEG-4 container format for storing meta
+data while WMA files wrap meta data as special objects within the
+ASF container format.
+
+paraslash only tracks the most common tags that are supported by
+all tag variants: artist, title, year, album, comment. When a file
+is added to the AFS database, the meta data of the file is extracted
+and stored in the audio file table.
+
+Chunks and chunk tables
+-----------------------
+
+paraslash uses the word "chunk" as common term for the building blocks
+of an audio file. For MP3 files, a chunk is the same as an MP3 frame,
+while for OGG files a chunk is an OGG page, etc.  Therefore the chunk
+size varies considerably between audio formats, from a few hundred
+bytes (MP3) up to 16K (FLAC).
+
+The chunk table contains the offsets within the audio file that
+correspond to the chunk boundaries of the file. Like the meta data,
+the chunk table is computed and stored in the database whenever an
+audio file is added.
+
+The paraslash senders (see below) always send complete chunks. The
+granularity for seeking is therefore determined by the chunk size.
+
+Audio format handlers
+---------------------
+
+For each audio format paraslash contains an audio format handler whose
+first task is to tell whether a given file is a valid audio file of
+this type. If so, the audio file handler extracts some technical data
+(duration, sampling rate, number of channels etc.), computes the
+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 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
+==========
+
+Paraslash uses different network connections for control and data.
+para_client communicates with para_server over a dedicated TCP control
+connection. To transport audio data, separate data connections are
+used. For these data connections, a variety of transports (UDP, DCCP,
+HTTP) can be chosen.
+
+The chapter starts with the [control
+service](#The.paraslash.control.service), followed by a section
+on the various [streaming protocols](#Streaming.protocols)
+in which the data connections are described. The way
+audio file headers are embedded into the stream is discussed
+[briefly](#Streams.with.headers.and.headerless.streams) before the
+[example section](#Networking.examples) which illustrates typical
+commands for real-life scenarios.
+
+Both IPv4 and IPv6 are supported.
+
+The paraslash control service
+-----------------------------
+
+para_server is controlled at runtime via the paraslash control
+connection. This connection is used for server commands (play, stop,
+...) as well as for afs commands (ls, select, ...).
+
+The server listens on a TCP port and accepts connections from clients
+that connect the open port. Each connection causes the server to fork
+off a client process which inherits the connection and deals with that
+client only. In this classical accept/fork approach the server process
+is unaffected if the child dies or goes crazy for whatever reason. In
+fact, the child process can not change address space of server process.
+
+The section on [client-server
+authentication](#Client-server.authentication) above described the
+early connection establishment from the crypto point of view. Here
+it is described what happens after the connection (including crypto
+setup) has been established.  There are four processes involved during
+command dispatch as sketched in the following diagram.
+
+       server_host                                   client_host
+       ~~~~~~~~~~~                                   ~~~~~~~~~~~
+
+       +-----------+             connect            +-----------+
+       |para_server|<------------------------------ |para_client|
+       +-----------+                                +-----------+
+            |                                             ^
+            |     fork   +---+                            |
+            +----------> |AFS|                            |
+            |            +---+                            |
+            |              ^                              |
+            |              |                              |
+            |              | connect (cookie)             |
+            |              |                              |
+            |              |                              |
+            |    fork   +-----+    inherited connection   |
+            +---------->|child|<--------------------------+
+                        +-----+
+
+Note that the child process is not a child of the afs process,
+so communication of these two processes has to happen via local
+sockets. In order to avoid abuse of the local socket by unrelated
+processes, a magic cookie is created once at server startup time just
+before the server process forks off the AFS process. This cookie is
+known to the server, AFS and the child, but not to unrelated processes.
+
+There are two different kinds of commands: First there are commands
+that cause the server to respond with some answer such as the list
+of all audio files. All but the addblob commands (addimg, addlyr,
+addpl, addmood) are of this kind. The addblob commands add contents
+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, 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
+-------------------
+
+A network (audio) stream usually consists of one streaming source,
+the _sender_, and one or more _receivers_ which read data over the
+network from the streaming source.
+
+Senders are thus part of para_server while receivers are part of
+para_audiod. Moreover, there is the stand-alone tool para_recv which
+can be used to manually download a stream, either from para_server
+or from a web-based audio streaming service.
+
+The following three streaming protocols are supported by paraslash:
+
+- HTTP. Recommended for public streams that can be played by any
+player like mpg123, xmms, itunes, winamp, etc. The HTTP sender is
+supported on all operating systems and all platforms.
+
+- DCCP. Recommended for LAN streaming. DCCP is currently available
+only for Linux.
+
+- UDP. Recommended for multicast LAN streaming.
+
+See the Appendix on [network protocols](/#Network.protocols)
+for brief descriptions of the various protocols relevant for network
+audio streaming with paraslash.
+
+It is possible to activate more than one sender simultaneously.
+Senders can be controlled at run time and via config file and command
+line options.
+
+Note that audio connections are _not_ encrypted. Transport or Internet
+layer encryption should be used if encrypted data connections are
+needed.
+
+Since DCCP and TCP are both connection-oriented protocols, connection
+establishment/teardown and access control are very similar between
+these two streaming protocols. UDP is the most lightweight option,
+since in contrast to TCP/DCCP it is connectionless. It is also the
+only protocol supporting IP multicast.
+
+The HTTP and the DCCP sender listen on a (TCP/DCCP) port waiting for
+clients to connect and establish a connection via some protocol-defined
+handshake mechanism. Both senders maintain two linked lists each:
+The list of all clients which are currently connected, and the list
+of access control entries which determines who is allowed to connect.
+IP-based access control may be configured through config file and
+command line options and via the "allow" and "deny" sender subcommands.
+
+Upon receiving a GET request from the client, the HTTP sender sends
+back a status line and a message. The body of this message is the
+audio stream. This is common practice and is supported by many popular
+clients which can thus be used to play a stream offered by para_server.
+For DCCP things are a bit simpler: No messages are exchanged between
+the receiver and sender. The client simply connects and the sender
+starts to stream.
+
+DCCP is an experimental protocol which offers a number of new features
+not available for TCP. Both ends can negotiate these features using
+a built-in negotiation mechanism. In contrast to TCP/HTTP, DCCP is
+datagram-based (no retransmissions) and thus should not be used over
+lossy media (e.g. WiFi networks). One useful feature offered by DCCP
+is access to a variety of different congestion-control mechanisms
+called CCIDs. Two different CCIDs are available per default on Linux:
+
+
+- _CCID 2_. A Congestion Control mechanism similar to that of TCP. The
+sender maintains a congestion window and halves this window in response
+to congestion.
+
+
+- _CCID-3_. Designed to be fair when competing for bandwidth.
+It has lower variation of throughput over time compared with TCP,
+which makes it suitable for streaming media.
+
+Unlike the HTTP and DCCP senders, the UDP sender maintains only a
+single list, the _target list_. This list describes the set of clients
+to which the stream is sent. There is no list for access control and
+no "allow" and "deny" commands for the UDP sender. Instead, the "add"
+and "delete" commands can be used to modify the target list.
+
+Since both UDP and DCCP offer an unreliable datagram-based transport,
+additional measures are necessary to guard against disruptions over
+networks that are lossy or which may be subject to interference (as
+is for instance the case with WiFi). Paraslash uses FEC (Forward
+Error Correction) to guard against packet losses and reordering. The
+stream is FEC-encoded before it is sent through the UDP socket and
+must be decoded accordingly on the receiver side.
+
+The packet size and the amount of redundancy introduced by FEC can
+be configured via the FEC parameters which are dictated by server
+and may also be configured through the "sender" command.  The FEC
+parameters are encoded in the header of each network packet, so no
+configuration is necessary on the receiver side. See the section on
+[FEC](#Forward.error.correction) below.
+
+Streams with headers and headerless streams
+-------------------------------------------
+
+For OGG/Vorbis, OGG/Speex and wma streams, some of the information
+needed to decode the stream is only contained in the audio file
+header of the container format but not in each data chunk. Clients
+must be able to obtain this information in case streaming starts in
+the middle of the file or if para_audiod is started while para_server
+is already sending a stream.
+
+This is accomplished in different ways, depending on the streaming
+protocol. For connection-oriented streams (HTTP, DCCP) the audio file
+header is sent prior to audio file data. This technique however does
+not work for the connectionless UDP transport. Hence the audio file
+header is periodically being embedded into the UDP audio data stream.
+By default, the header is resent after five seconds. The receiver has
+to wait until the next header arrives before it can start decoding
+the stream.
+
+Networking examples
+-------------------
+
+The "si" (server info) command lists some information about the
+currently running server process.
+
+-> 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
+
+-> Get help for a specific sender (contains further examples):
+
+       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.
+
+-> List config file options for senders:
+
+       para_server -h
+
+All senders share the "on" and "off" commands, so senders may be
+activated and deactivated independently of each other.
+
+-> Switch off the http sender:
+
+       para_client sender http off
+
+-> Receive a DCCP stream using CCID2 and write the output into a file:
+
+       host=foo.org; ccid=2; filename=bar
+       para_recv --receiver "dccp --host $host --ccid $ccid" > $filename
+
+Note the quotes around the arguments for the dccp receiver. Each
+receiver has its own set of command line options and its own command
+line parser, so arguments for the dccp receiver must be protected
+from being interpreted by para_recv.
+
+-> Start UDP multicast, using the default multicast address:
+
+       para_client sender udp add 224.0.1.38
+
+-> Receive FEC-encoded multicast stream and write the output into a file:
+
+       filename=foo
+       para_recv -r udp > $filename
+
+-> Add an UDP unicast for a client to the target list of the UDP sender:
+
+       t=client.foo.org
+       para_client sender udp add $t
+
+-> Receive this (FEC-encoded) unicast stream:
+
+       filename=foo
+       para_recv -r 'udp -i 0.0.0.0' > $filename
+
+-> Create a minimal config for para_audiod for HTTP streams:
+
+       c=$HOME/.paraslash/audiod.conf.min; s=server.foo.com
+       echo receiver \".:http -i $s\" > $c
+       para_audiod --config $c
+
+=======
+Filters
+=======
+
+A paraslash filter is a module which transforms an input stream into
+an output stream. Filters are included in the para_audiod executable
+and in the stand-alone tool para_filter which usually contains the
+same modules.
+
+While para_filter reads its input stream from STDIN and writes
+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 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
+
+Out of these filter modules, a chain of filters can be constructed,
+much in the way Unix pipes can be chained, and analogous to the use
+of modules in gstreamer: The output of the first filter becomes the
+input of the second filter. There is no limitation on the number of
+filters and the same filter may occur more than once.
+
+Like receivers, each filter has its own command line options which
+must be quoted to protect them from the command line options of
+the driving application (para_audiod or para_filter). Example:
+
+       para_filter -f 'mp3dec --ignore-crc' -f 'compress --damp 1'
+
+For para_audiod, each audio format has its own set of filters. The
+name of the audio format for which the filter should be applied can
+be used as the prefix for the filter option. Example:
+
+       para_audiod -f 'mp3:prebuffer --duration 300'
+
+The "mp3" prefix above is actually interpreted as a POSIX extended
+regular expression. Therefore
+
+       para_audiod -f '.:prebuffer --duration 300'
+
+activates the prebuffer filter for all supported audio formats (because
+"." matches all audio formats) while
+
+       para_audiod -f 'wma|ogg:prebuffer --duration 300'
+
+activates it only for wma and ogg streams.
+
+Decoders
+--------
+
+For each supported audio format there is a corresponding filter
+which decodes audio data in this format to 16 bit PCM data which
+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 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
+stream have to deal with much more data than filters that transform
+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
+executables. For example, the mp3dec filter depends on the mad library.
+
+Forward error correction
+------------------------
+
+As already mentioned [earlier](#Streaming.protocols), paraslash
+uses forward error correction (FEC) for the unreliable UDP and
+DCCP transports. FEC is a technique which was invented already in
+1960 by Reed and Solomon and which is widely used for the parity
+calculations of storage devices (RAID arrays). It is based on the
+algebraic concept of finite fields, today called Galois fields, in
+honour of the mathematician Galois (1811-1832). The FEC implementation
+of paraslash is based on code by Luigi Rizzo.
+
+Although the details require a sound knowledge of the underlying
+mathematics, the basic idea is not hard to understand: For positive
+integers k and n with k < n it is possible to compute for any k given
+data bytes d_1, ..., d_k the corresponding r := n -k parity bytes p_1,
+..., p_r such that all data bytes can be reconstructed from *any*
+k bytes of the set
+
+       {d_1, ..., d_k, p_1, ..., p_r}.
+
+FEC-encoding for unreliable network transports boils down to slicing
+the audio stream into groups of k suitably sized pieces called _slices_
+and computing the r corresponding parity slices. This step is performed
+in para_server which then sends both the data and the parity slices
+over the unreliable network connection. If the client was able
+to receive at least k of the n = k + r slices, it can reconstruct
+(FEC-decode) the original audio stream.
+
+From these observations it is clear that there are three different
+FEC parameters: The slice size, the number of data slices k, and the
+total number of slices n. It is crucial to choose the slice size
+such that no fragmentation of network packets takes place because
+FEC only guards against losses and reordering but fails if slices are
+received partially.
+
+FEC decoding in paralash is performed through the fecdec filter which
+usually is the first filter (there can be other filters before fecdec
+if these do not alter the audio stream).
+
+Volume adjustment (amp and compress)
+------------------------------------
+
+The amp and the compress filter both adjust the volume of the audio
+stream. These filters operate on uncompressed audio samples. Hence
+they are usually placed directly after the decoding filter. Each
+sample is multiplied with a scaling factor (>= 1) which makes amp
+and compress quite expensive in terms of computing power.
+
+### amp ###
+
+The amp filter amplifies the audio stream by a fixed scaling factor
+that must be known in advance. For para_audiod this factor is derived
+from the amplification field of the audio file's entry in the audio
+file table while para_filter uses the value given at the command line.
+
+The optimal scaling factor F for an audio file is the largest real
+number F >= 1 such that after multiplication with F all samples still
+fit into the sample interval [-32768, 32767]. One can use para_filter
+in combination with the sox utility to compute F:
+
+       para_filter -f mp3dec -f wav < file.mp3 | sox -t wav - -e stat -v
+
+The amplification value V which is stored in the audio file table,
+however, is an integer between 0 and 255 which is connected to F
+through the formula
+
+       V = (F - 1) * 64.
+
+To store V in the audio file table, the command
+
+       para_client -- touch -a=V file.mp3
+
+is used. The reader is encouraged to write a script that performs
+these computations :)
+
+### compress ###
+
+Unlike the amplification filter, the compress filter adjusts the volume
+of the audio stream dynamically without prior knowledge about the peak
+value. It maintains the maximal volume of the last n samples of the
+audio stream and computes a suitable amplification factor based on that
+value and the various configuration options. It tries to chose this
+factor such that the adjusted volume meets the desired target level.
+
+Note that it makes sense to combine amp and compress.
+
+Misc filters (wav and prebuffer)
+--------------------------------
+
+These filters are rather simple and do not modify the audio stream at
+all. The wav filter is only useful with para_filter and in connection
+with a decoder. It asks the decoder for the number of channels and the
+sample rate of the stream and adds a Microsoft wave header containing
+this information at the beginning. This allows to write wav files
+rather than raw PCM files (which do not contain any information about
+the number of channels and the sample rate).
+
+The prebuffer filter simply delays the output until the given time has
+passed (starting from the time the first byte was available in its
+input queue) or until the given amount of data has accumulated. It
+is mainly useful for para_audiod if the standard parameters result
+in buffer underruns.
+
+Both filters require almost no additional computing time, even when
+operating on uncompressed audio streams, since data buffers are simply
+"pushed down" rather than copied.
+
+Examples
+--------
+
+-> Decode an mp3 file to wav format:
+
+       para_filter -f mp3dec -f wav < file.mp3 > file.wav
+
+-> Amplify a raw audio file by a factor of 1.5:
+
+       para_filter -f amp --amp 32 < foo.raw > bar.raw
+
+======
+Output
+======
+
+Once an audio stream has been received and decoded to PCM format,
+it can be sent to a sound device for playback. This part is performed
+by paraslash _writers_ which are described in this chapter.
+
+Writers
+-------
+
+A paraslash writer acts as a data sink that consumes but does not
+produce audio data. Paraslash writers operate on the client side and
+are contained in para_audiod and in the stand-alone tool para_write.
+
+The para_write program reads uncompressed audio data from STDIN. If
+this data starts with a wav header, sample rate, sample format and
+channel count are read from the header. Otherwise CD audio (44.1KHz
+16 bit little endian, stereo) is assumed but this can be overridden
+by command line options. para_audiod, on the other hand, obtains
+the sample rate and the number of channels from the decoder.
+
+Like receivers and filters, each writer has an individual set of
+command line options, and for para_audiod writers can be configured
+per audio format separately. It is possible to activate more than
+one writer for the same stream simultaneously.
+
+OS-dependent APIs
+-----------------
+
+Unfortunately, the various flavours of Unix on which paraslash
+runs on have different APIs for opening a sound device and starting
+playback. Hence for each such API there is a paraslash writer that
+can play the audio stream via this API.
+
+- *ALSA*. The _Advanced Linux Sound Architecture_ is only available on
+Linux systems. Although there are several mid-layer APIs in use by
+the various Linux distributions (ESD, Jack, PulseAudio), paraslash
+currently supports only the low-level ALSA API which is not supposed
+to be change. ALSA is very feature-rich, in particular it supports
+software mixing via its DMIX plugin. ALSA is the default writer on
+Linux systems.
+
+- *OSS*. The _Open Sound System_ is the only API on \*BSD Unixes and
+is also available on Linux systems, usually provided by ALSA as an
+emulation for backwards compatibility. This API is rather simple but
+also limited. For example only one application can open the device
+at any time. The OSS writer is activated by default on BSD Systems.
+
+- *OSX*. Mac OS X has yet another API called CoreAudio. The OSX writer
+for this API is only compiled in on such systems and is of course
+the default there.
+
+- *FILE*. The file writer allows to capture the audio stream and
+write the PCM data to a file on the file system rather than playing
+it through a sound device. It is supported on all platforms and is
+always compiled in.
+
+- *AO*. _Libao_ is a cross-platform audio library which supports a wide
+variety of platforms including PulseAudio (gnome), ESD (Enlightened
+Sound Daemon), AIX, Solaris and IRIX.  The ao writer plays audio
+through an output plugin of libao.
+
+Examples
+--------
+
+-> Use the OSS writer to play a wav file:
+
+       para_write --writer oss < file.wav
+
+-> Enable ALSA software mixing for mp3 streams:
+
+       para_audiod --writer 'mp3:alsa -d plug:swmix'
+
+
+===
+Gui
+===
+
+para_gui executes an arbitrary command which is supposed to print
+status information to STDOUT. It then displays this information in
+a curses window. By default the command
+
+       para_audioc -- stat -p
+
+is executed, but this can be customized via the --stat-cmd option. In
+particular it possible to use
+
+       para_client -- stat -p
+
+to make para_gui work on systems on which para_audiod is not running.
+
+Key bindings
+------------
+
+It is possible to bind keys to arbitrary commands via custom
+key-bindings. Besides the internal keys which can not be changed (help,
+quit, loglevel, version...), the following flavours of key-bindings
+are supported:
+
+- external: Shutdown curses before launching the given command.
+Useful for starting other ncurses programs from within para_gui,
+e.g. aumix or dialog scripts. Or, use the mbox output format to write
+a mailbox containing one mail for each (admissible) file the audio
+file selector knows about. Then start mutt from within para_gui to
+browse your collection!
+
+- display: Launch the command and display its stdout in para_gui's
+bottom window.
+
+- para: Like display, but start "para_client <specified command>"
+instead of "<specified command>".
+
+The general form of a key binding is
+
+       key_map k:m:c
+
+which maps key k to command c using mode m. Mode may be x, d or p
+for external, display and paraslash commands, respectively.
+
+Themes
+------
+
+Currently there are only two themes for para_gui. It is easy, however,
+to add more themes. To create a new theme one has to define the
+position, color and geometry for for each status item that should be
+shown by this theme. See gui_theme.c for examples.
+
+The "." and "," keys are used to switch between themes.
+
+Examples
+--------
+
+-> Show server info:
+
+       key_map "i:p:si"
+
+-> Jump to the middle of the current audio file by pressing F5:
+
+       key_map "<F5>:p:jmp 50"
+
+-> vi-like bindings for jumping around:
+
+       key_map "l:p:ff 10"
+       key_map "h:p:ff 10-"
+       key_map "w:p:ff 60"
+       key_map "b:p:ff 60-"
+
+-> Print the current date and time:
+
+       key_map "D:d:date"
+
+-> Call other curses programs:
+
+       key_map "U:x:aumix"
+       key_map "!:x:/bin/bash"
+       key_map "^E:x:/bin/sh -c 'vi ~/.paraslash/gui.conf'"
+
+===========
+Development
+===========
+
+Contributing
+------------
+
+Paraslash is an open source project and contributions are
+welcome. Here's a list of things you can do to help the project:
+
+- Report problems with building, installing or running the software.
+  In particular, test the experimental git branches ("next" and "pu").
+  This helps to identify and fix problems before the code gets merged
+  and thus keeps the master branch as stable as possible.
+- Proofread the documentation (manual, web pages, man pages, source
+  code documentation) and point out unclear or poorly written parts. If
+  you are a native English speaker you will easily find a lot of text
+  that could be improved.
+- Run analysis tools (coverity, afl, sparse, etc.) and report issues
+  found by those tools.
+- Suggest new features you would like to see implemented.
+- Compile and test on your favorite architecture or operating
+  system. The code is tested only on a limited set of systems, so you
+  will probably encounter problems when building on different systems.
+- Post about about paraslash on your blog or on social networks.
+- Build and maintain Debian/RPM packages for your favorite distribution.
+
+Note that there is no mailing list, no bug tracker and no discussion
+forum for paraslash. If you'd like to contribute, or have questions
+about contributing, send email to Andre Noll <maan@tuebingen.mpg.de>.
+
+Tools
+-----
+
+In order to compile the sources from the git repository (rather than
+from tar balls) and for contributing non-trivial changes to the
+paraslash project, some additional tools should be installed on a
+developer machine.
+
+- [git](http://git.or.cz/). As described in more detail
+[below](#Git.branches), the git source code management tool is used for
+paraslash development. It is necessary for cloning the git repository
+and for getting updates.
+
+- [autoconf](ftp://ftp.gnu.org/pub/gnu/autoconf/) GNU autoconf creates
+the configure file which is shipped in the tarballs but has to be
+generated when compiling from git.
+
+- [discount](http://www.pell.portland.or.us/~orc/Code/discount). The
+HTML version of this manual and some of the paraslash web pages are
+written in the Markdown markup language and are translated into html
+with the converter of the *Discount* package.
+
+- [doxygen](http://www.stack.nl/~dimitri/doxygen/). The documentation
+of paraslash's C sources uses the doxygen documentation system. The
+conventions for documenting the source code is described in the
+[Doxygen section](#Doxygen).
+
+- [global](ftp://ftp.gnu.org/pub/gnu/global). This is used to generate
+browsable HTML from the C sources. It is needed by doxygen.
+
+Git branches
+------------
+
+Paraslash has been developed using the git source code management
+tool since 2006. Development is organized roughly in the same spirit
+as the git development itself, as described below.
+
+The following text passage is based on "A note from the maintainer",
+written by Junio C Hamano, the maintainer of git.
+
+There are four branches in the paraslash repository that track the
+source tree: "master", "maint", "next", and "pu".
+
+The "master" branch is meant to contain what is well tested and
+ready to be used in a production setting. There could occasionally be
+minor breakages or brown paper bag bugs but they are not expected to
+be anything major, and more importantly quickly and easily fixable.
+Every now and then, a "feature release" is cut from the tip of this
+branch, named with three dotted decimal digits, like 0.4.2.
+
+Whenever changes are about to be included that will eventually lead to
+a new major release (e.g. 0.5.0), a "maint" branch is forked off from
+"master" at that point. Obvious, safe and urgent fixes after the major
+release are applied to this branch and maintenance releases are cut
+from it. New features never go to this branch. This branch is also
+merged into "master" to propagate the fixes forward.
+
+A trivial and safe enhancement goes directly on top of "master".
+New development does not usually happen on "master", however.
+Instead, a separate topic branch is forked from the tip of "master",
+and it first is tested in isolation; Usually there are a handful such
+topic branches that are running ahead of "master". The tip of these
+branches is not published in the public repository to keep the number
+of branches that downstream developers need to worry about low.
+
+The quality of topic branches varies widely. Some of them start out as
+"good idea but obviously is broken in some areas" and then with some
+more work become "more or less done and can now be tested by wider
+audience". Luckily, most of them start out in the latter, better shape.
+
+The "next" branch is to merge and test topic branches in the latter
+category.  In general, this branch always contains the tip of "master".
+It might not be quite rock-solid production ready, but is expected to
+work more or less without major breakage. The maintainer usually uses
+the "next" version of paraslash for his own pleasure, so it cannot
+be _that_ broken. The "next" branch is where new and exciting things
+take place.
+
+The two branches "master" and "maint" are never rewound, and "next"
+usually will not be either (this automatically means the topics that
+have been merged into "next" are usually not rebased, and you can find
+the tip of topic branches you are interested in from the output of
+"git log next"). You should be able to safely build on top of them.
+
+However, at times "next" will be rebuilt from the tip of "master" to
+get rid of merge commits that will never be in "master". The commit
+that replaces "next" will usually have the identical tree, but it
+will have different ancestry from the tip of "master".
+
+The "pu" (proposed updates) branch bundles the remainder of the
+topic branches.  The "pu" branch, and topic branches that are only in
+"pu", are subject to rebasing in general.  By the above definition
+of how "next" works, you can tell that this branch will contain quite
+experimental and obviously broken stuff.
+
+When a topic that was in "pu" proves to be in testable shape, it
+graduates to "next".  This is done with
+
+               git checkout next
+               git merge that-topic-branch
+
+Sometimes, an idea that looked promising turns out to be not so good
+and the topic can be dropped from "pu" in such a case.
+
+A topic that is in "next" is expected to be polished to perfection
+before it is merged to "master".  Similar to the above, this is
+done with
+
+               git checkout master
+               git merge that-topic-branch
+               git branch -d that-topic-branch
+
+Note that being in "next" is not a guarantee to appear in the next
+release (being in "master" is such a guarantee, unless it is later
+found seriously broken and reverted), nor even in any future release.
+
+Coding Style
+------------
+
+The preferred coding style for paraslash coincides more or less
+with the style of the Linux kernel. So rather than repeating what is
+written [there](http://www.kernel.org/doc/Documentation/process/coding-style.rst),
+here are the most important points.
+
+- Burn the GNU coding standards.
+- Never use spaces for indentation.
+- Tabs are 8 characters, and thus indentations are also 8 characters.
+- Don't put multiple assignments on a single line.
+- Avoid tricky expressions.
+- Don't leave whitespace at the end of lines.
+- The limit on the length of lines is 80 columns.
+- Use K&R style for placing braces and spaces:
+
+               if (x is true) {
+                       we do y
+               }
+
+- Use a space after (most) keywords.
+- Do not add spaces around (inside) parenthesized expressions.
+- Use one space around (on each side of) most binary and ternary operators.
+- Do not use cute names like ThisVariableIsATemporaryCounter, call it tmp.
+- Mixed-case names are frowned upon.
+- Descriptive names for global variables are a must.
+- Avoid typedefs.
+- Functions should be short and sweet, and do just one thing.
+- The number of local variables shouldn't exceed 10.
+- Gotos are fine if they improve readability and reduce nesting.
+- Don't use C99-style "// ..." comments.
+- Names of macros defining constants and labels in enums are capitalized.
+- Enums are preferred when defining several related constants.
+- Always use the paraslash wrappers for allocating memory.
+- If the name of a function is an action or an imperative.
+  command, the function should return an error-code integer
+  (<0 means error, >=0 means success). If the name is a
+  predicate, the function should return a "succeeded" boolean.
+
+Doxygen
+-------
+
+Doxygen is a documentation system for various programming
+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 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 doxygen comment.
+
+No doxygen comments are necessary for static functions and for
+structures and enumerations in C files (which are used only within
+this file). This does not mean, however, that those entities need
+no documentation at all. Instead, common sense should be applied to
+document what is not obvious from reading the code.
+
+========
+Appendix
+========
+
+Network protocols
+-----------------
+
+### IP ###
+
+The _Internet Protocol_ is the primary networking protocol used for
+the Internet. All protocols described below use IP as the underlying
+layer. Both the prevalent IPv4 and the next-generation IPv6 variant
+are being deployed actively worldwide.
+
+### Connection-oriented and connectionless protocols ###
+
+Connectionless protocols differ from connection-oriented ones in
+that state associated with the sending/receiving endpoints is treated
+implicitly. Connectionless protocols maintain no internal knowledge
+about the state of the connection. Hence they are not capable of
+reacting to state changes, such as sudden loss or congestion on the
+connection medium. Connection-oriented protocols, in contrast, make
+this knowledge explicit. The connection is established only after
+a bidirectional handshake which requires both endpoints to agree
+on the state of the connection, and may also involve negotiating
+specific parameters for the particular connection. Maintaining an
+up-to-date internal state of the connection also in general means
+that the sending endpoints perform congestion control, adapting to
+qualitative changes of the connection medium.
+
+### Reliability ###
+
+In IP networking, packets can be lost, duplicated, or delivered
+out of order, and different network protocols handle these
+problems in different ways. We call a transport-layer protocol
+_reliable_, if it turns the unreliable IP delivery into an ordered,
+duplicate- and loss-free delivery of packets. Sequence numbers
+are used to discard duplicates and re-arrange packets delivered
+out-of-order. Retransmission is used to guarantee loss-free
+delivery. Unreliable protocols, in contrast, do not guarantee ordering
+or data integrity.
+
+### Classification ###
+
+With these definitions the protocols which are used by paraslash for
+steaming audio data may be classified as follows.
+
+       - HTTP/TCP: connection-oriented, reliable,
+       - UDP: connectionless, unreliable,
+       - DCCP: connection-oriented, unreliable.
+
+Below we give a short descriptions of these protocols.
+
+### TCP ###
+
+The _Transmission Control Protocol_ provides reliable, ordered delivery
+of a stream and a classic window-based congestion control. In contrast
+to UDP and DCCP (see below), TCP does not have record-oriented or
+datagram-based syntax, i.e. it provides a stream which is unaware
+and independent of any record (packet) boundaries.  TCP is used
+extensively by many application layers. Besides HTTP (the Hypertext
+Transfer Protocol), also FTP (the File Transfer protocol), SMTP (Simple
+Mail Transfer Protocol), SSH (Secure Shell) all sit on top of TCP.
+
+### UDP ###
+
+The _User Datagram Protocol_ is the simplest transport-layer protocol,
+built as a thin layer directly on top of IP. For this reason, it offers
+the same best-effort service as IP itself, i.e. there is no detection
+of duplicate or reordered packets. Being a connectionless protocol,
+only minimal internal state about the connection is maintained, which
+means that there is no protection against packet loss or network
+congestion. Error checking and correction (if at all) are performed
+in the application.
+
+### DCCP ###
+
+The _Datagram Congestion Control Protocol_ combines the
+connection-oriented state maintenance known from TCP with the
+unreliable, datagram-based transport of UDP. This means that it
+is capable of reacting to changes in the connection by performing
+congestion control, offering multiple alternative approaches. But it
+is bound to datagram boundaries (the maximum packet size supported
+by a medium), and like UDP it lacks retransmission to protect
+against loss. Due to the use of sequence numbers, it is however
+able to react to loss (interpreted as a congestion indication) and
+to ignore out-of-order and duplicate packets. Unlike TCP it allows
+to negotiate specific, binding features for a connection, such as
+the choice of congestion control: classic, window-based congestion
+control known from TCP is available as CCID-2, rate-based, "smooth"
+congestion control is offered as CCID-3.
+
+### HTTP ###
+
+The _Hypertext Transfer Protocol_ is an application layer protocol
+on top of TCP. It is spoken by web servers and is most often used
+for web services.  However, as can be seen by the many Internet radio
+stations and YouTube/Flash videos, http is by far not limited to the
+delivery of web pages only. Being a simple request/response based
+protocol, the semantics of the protocol also allow the delivery of
+multimedia content, such as audio over http.
+
+### Multicast ###
+
+IP multicast is not really a protocol but a technique for one-to-many
+communication over an IP network. The challenge is to deliver
+information to a group of destinations simultaneously using the
+most efficient strategy to send the messages over each link of the
+network only once. This has benefits for streaming multimedia: the
+standard one-to-one unicast offered by TCP/DCCP means that n clients
+listening to the same stream also consume n-times the resources,
+whereas multicast requires to send the stream just once, irrespective
+of the number of receivers.  Since it would be costly to 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
+-------
+
+Paraslash is licensed under the GPL, version 2. Most of the code
+base has been written from scratch, and those parts are GPL V2
+throughout. Notable exceptions are FEC and the WMA decoder. See the
+corresponding source files for licencing details for these parts. Some
+code sniplets of several other third party software packages have
+been incorporated into the paraslash sources, for example log message
+coloring was taken from the git sources. These third party software
+packages are all published under the GPL or some other license
+compatible to the GPL.
+
+Acknowledgements
+----------------
+
+Many thanks to Gerrit Renker who read an early draft of this manual
+and contributed significant improvements.
+
+==========
+References
+==========
+
+Articles
+--------
+- [Polynomial Codes over Certain Finite
+Fields](http://kom.aau.dk/~heb/kurser/NOTER/KOFA01.PDF) by Reed, Irving
+S.; Solomon, Gustave (1960), Journal of the Society for Industrial
+and Applied Mathematics (SIAM) 8 (2): 300-304, doi:10.1137/0108018)
+
+RFCs
+----
+
+- [RFC 768](http://www.ietf.org/rfc/rfc768.txt) (1980): User Datagram
+Protocol
+
+- [RFC 791](http://www.ietf.org/rfc/rfc791.txt) (1981): Internet
+Protocol
+
+- [RFC 2437](http://www.ietf.org/rfc/rfc2437.txt) (1998): RSA
+Cryptography Specifications
+
+- [RFC 4340](http://www.ietf.org/rfc/rfc4340.txt) (2006): Datagram
+Congestion Control Protocol (DCCP)
+
+- [RFC 4341](http://www.ietf.org/rfc/rfc4341.txt) (2006): Congestion
+Control ID 2: TCP-like Congestion Control
+
+- [RFC 4342](http://www.ietf.org/rfc/rfc4342.txt) (2006): Congestion
+Control ID 3: TCP-Friendly Rate Control (TFRC)
+
+- [RFC 6716](http://www.ietf.org/rfc/rfc6716.txt) (2012): Definition
+of the Opus Audio Codec
+
+Application web pages
+---------------------
+
+- [paraslash](http://people.tuebingen.mpg.de/maan/paraslash/)
+- [xmms](http://xmms2.org/wiki/Main_Page)
+- [mpg123](http://www.mpg123.de/)
+- [gstreamer](http://gstreamer.freedesktop.org/)
+- [icecast](http://www.icecast.org/)
+- [Audio Compress](http://beesbuzz.biz/code/audiocompress.php)
+
+External documentation
+----------------------
+
+- [The mathematics of
+Raid6](http://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf)
+by H. Peter Anvin
+
+- [Effective Erasure Codes for reliable Computer Communication
+Protocols](http://info.iet.unipi.it/~luigi/fec_ccr.ps.gz) by Luigi
+Rizzo
+
+Code
+----
+- [Original FEC
+implementation](http://info.iet.unipi.it/~luigi/vdm.tar.gz) by
+Luigi Rizzo)
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..b260feb99d76ea58511696cd0c04ddb4ec01b26c 100644 (file)
--- a/wma.h
+++ b/wma.h
@@ -20,8 +20,16 @@ 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;
+       /** Obtained from the file properties object. */
+       uint32_t packet_size;
 };
 
 /* wma_common.c */
index 207b0ba8cae25c024172023d58d3691e7c4f5e9b..4c9d87e047af8d1d5873096db54ab4a9d17ab966 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"
 #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); \
-       _f += (_ba) + WMA_FRAME_SKIP)
+#define FOR_EACH_FRAME(_f, _buf, _size, _ps) for (_f = (_buf); \
+       _f + (_ps) < (_buf) + (_size); \
+       _f += (_ps))
 
 /*
  * Must be called on a frame boundary, e.g. start + header_len.
  * \return Frame count, superframe count via *num_superframes.
  */
-static int count_frames(const char *buf, int buf_size, int block_align,
+static int count_frames(const char *buf, int buf_size, uint32_t packet_size,
        int *num_superframes)
 {
        int fc = 0, sfc = 0; /* frame count, superframe count */
        const uint8_t *p;
 
 
-       FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align) {
+       FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, packet_size) {
                fc += p[WMA_FRAME_SKIP] & 0x0f;
                sfc++;
        }
@@ -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);
@@ -197,7 +192,7 @@ static void set_chunk_tv(int frames_per_chunk, int frequency,
 }
 
 /* Must be called on a frame boundary. */
-static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
+static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size,
                struct afh_info *afhi)
 {
        const uint8_t *f, *start = (uint8_t *)buf;
@@ -209,7 +204,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
 
-       num_frames = count_frames(buf, buf_size, block_align,
+       num_frames = count_frames(buf, buf_size, packet_size,
                &num_superframes);
        ret = -E_NO_WMA;
        if (num_frames == 0 || num_superframes == 0)
@@ -219,7 +214,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
        frames_per_chunk = num_frames / num_superframes / 2;
        PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk);
        j = 1;
-       FOR_EACH_FRAME(f, start, buf_size, block_align) {
+       FOR_EACH_FRAME(f, start, buf_size, packet_size) {
                count += f[WMA_FRAME_SKIP] & 0x0f;
                while (count > j * frames_per_chunk) {
                        j++;
@@ -229,7 +224,8 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
                                        afhi->chunk_table,
                                        ct_size * sizeof(uint32_t));
                        }
-                       afhi->chunk_table[j] = f - start + afhi->header_len + block_align + WMA_FRAME_SKIP;
+                       afhi->chunk_table[j] = f - start + afhi->header_len
+                               + packet_size;
                }
        }
        afhi->chunks_total = j;
@@ -257,13 +253,397 @@ 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);
+               ahi.packet_size, afhi);
        read_asf_tags(map, ahi.header_len, &afhi->tags);
        return 0;
 }
 
-static const char* wma_suffixes[] = {"wma", NULL};
+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 * const wma_suffixes[] = {"wma", NULL};
 
 /**
  * The init function of the wma audio format handler.
@@ -274,4 +654,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..6d57c00be2d2627624f5642a74948723bc2726fb 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"
@@ -48,6 +42,17 @@ const char *search_pattern(const char *pattern, int pattern_len,
        return NULL;
 }
 
+static int find_file_properties(const char *buf, int len)
+{
+       const char pattern[] = {0xa1, 0xdc, 0xab, 0x8c};
+       const char *p = search_pattern(pattern, sizeof(pattern), buf, len);
+
+       if (!p)
+               return -E_WMA_NO_GUID;
+       PARA_DEBUG_LOG("found file property guid@%0x\n", (unsigned)(p - buf));
+       return p - buf + 16;
+}
+
 /*
    40 9e 69 f8 4d 5b cf 11  a8 fd 00 80 5f 5c 44 2b
  */
@@ -58,7 +63,7 @@ static int find_audio_stream_info(const char *buf, int len)
 
        if (!p)
                return -E_WMA_NO_GUID;
-       PARA_DEBUG_LOG("found audio stream guid@%0x\n", (int)(p - buf));
+       PARA_DEBUG_LOG("found audio stream guid@%0x\n", (unsigned)(p - buf));
        return p - buf + 16;
 }
 
@@ -106,15 +111,33 @@ int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi)
                ahi->sample_rate);
 
        ahi->bit_rate = 8 * read_u16(start + 46);
-       PARA_INFO_LOG("bit rate: %d\n", ahi->bit_rate);
+       PARA_INFO_LOG("bit rate: %u\n", ahi->bit_rate);
 
        ahi->block_align = read_u16(start + 50);
        PARA_INFO_LOG("block_align: %d\n", ahi->block_align);
 
        ahi->flags1 = read_u32(start + 56);
        ahi->flags2 = read_u16(start + 60);
-       PARA_INFO_LOG("read_asf_header: flags1: %d, flag2: %d\n",
+       PARA_INFO_LOG("read_asf_header: flags1: %u, flags2: %u\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;
+
+       ret = find_file_properties(buf, ahi->header_len);
+       if (ret < 0)
+               return ret;
+       /* file property header is always 88 bytes (sans GUID) */
+       if (ret + 88 > loaded)
+               return 0;
+       start = buf + ret;
+       ahi->packet_size = read_u32(start + 76); /* min packet size */
+       /* we only support fixed packet sizes */
+       if (ahi->packet_size != read_u32(start + 80)) /* min != max */
+               return -E_BAD_ASF_FILE_PROPS;
+       if (ahi->packet_size <= ahi->block_align)
+               return -E_BAD_ASF_FILE_PROPS;
+       PARA_INFO_LOG("packet size: %u\n", ahi->packet_size);
        return 1;
 }
 
index 8b751f04592eff0f7d6b3e5fe73ade1938c17382..4c7c047a482e2b3f256cfb8c12dcd6b4de3a7500 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,10 +86,10 @@ 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;
+       /* Whether to update block lengths from getbit context. */
+       bool reset_block_lengths;
        /** log2 of current block length. */
        int block_len_bits;
        /** log2 of next block length. */
@@ -141,13 +131,10 @@ struct private_wmadec_data {
 };
 
 #define EXPVLCBITS 8
-#define EXPMAX DIV_ROUND_UP(19, EXPVLCBITS)
-
 #define HGAINVLCBITS 9
-#define HGAINMAX DIV_ROUND_UP(13, HGAINVLCBITS)
-
 #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)
 
@@ -161,6 +148,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 +165,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 +299,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)
@@ -383,8 +371,8 @@ static int wma_init(struct private_wmadec_data *pwd)
                else
                        high_freq = high_freq * 0.5;
        }
-       PARA_INFO_LOG("channels=%d sample_rate=%d "
-               "bitrate=%d block_align=%d\n",
+       PARA_INFO_LOG("channels=%u sample_rate=%u "
+               "bitrate=%u block_align=%d\n",
                ahi->channels, ahi->sample_rate,
                ahi->bit_rate, ahi->block_align);
        PARA_INFO_LOG("frame_len=%d, bps=%f bps1=%f "
@@ -392,7 +380,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 */
@@ -403,11 +391,11 @@ static int wma_init(struct private_wmadec_data *pwd)
                pwd->windows[i] = sine_windows[pwd->frame_len_bits - i - 7];
        }
 
-       pwd->reset_block_lengths = 1;
+       pwd->reset_block_lengths = true;
 
        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;
@@ -441,13 +429,13 @@ static int wma_init(struct private_wmadec_data *pwd)
        return 0;
 }
 
-static void wma_lsp_to_curve_init(struct private_wmadec_data *pwd, int frame_len)
+static void wma_lsp_to_curve_init(struct private_wmadec_data *pwd)
 {
        float wdel, a, b;
        int i, e, m;
 
-       wdel = M_PI / frame_len;
-       for (i = 0; i < frame_len; i++)
+       wdel = M_PI / pwd->frame_len;
+       for (i = 0; i < pwd->frame_len; i++)
                pwd->lsp_cos_table[i] = 2.0f * cos(wdel * i);
 
        /* tables for x^-0.25 computation */
@@ -481,10 +469,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,13 +485,13 @@ 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);
        } else {
                PARA_INFO_LOG("using curve\n");
-               wma_lsp_to_curve_init(pwd, pwd->frame_len);
+               wma_lsp_to_curve_init(pwd);
        }
        *result = pwd;
        return pwd->ahi.header_len;
@@ -597,7 +581,7 @@ static int decode_exp_vlc(struct private_wmadec_data *pwd, int ch)
        last_exp = 36;
 
        while (q < q_end) {
-               code = get_vlc(&pwd->gb, pwd->exp_vlc.table, EXPVLCBITS, EXPMAX);
+               code = get_vlc(&pwd->gb, pwd->exp_vlc.table, EXPVLCBITS);
                if (code < 0)
                        return code;
                /* NOTE: this offset is the same as MPEG4 AAC ! */
@@ -725,8 +709,7 @@ static int compute_high_band_values(struct private_wmadec_data *pwd,
                                val = get_bits(&pwd->gb, 7) - 19;
                        else {
                                int code = get_vlc(&pwd->gb,
-                                       pwd->hgain_vlc.table, HGAINVLCBITS,
-                                       HGAINMAX);
+                                       pwd->hgain_vlc.table, HGAINVLCBITS);
                                if (code < 0)
                                        return code;
                                val += code - 18;
@@ -827,7 +810,7 @@ static void compute_mdct_coefficients(struct private_wmadec_data *pwd,
                }
                /* very high freqs: noise */
                n = pwd->block_len - pwd->coefs_end[bsize];
-               mult1 = mult * exponents[((-1 << bsize)) >> esize];
+               mult1 = mult * exponents[(-(1 << bsize)) >> esize];
                for (i = 0; i < n; i++) {
                        *coefs++ = pwd->noise_table[pwd->noise_index] * mult1;
                        pwd->noise_index = (pwd->noise_index + 1)
@@ -847,11 +830,11 @@ 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) {
-                       pwd->reset_block_lengths = 0;
+                       pwd->reset_block_lengths = false;
                        v = get_bits(&pwd->gb, n);
                        if (v >= pwd->nb_block_sizes)
                                return -E_WMA_BLOCK_SIZE;
@@ -924,7 +907,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;
@@ -957,8 +940,7 @@ static int wma_decode_block(struct private_wmadec_data *pwd)
                eptr = ptr + nb_coefs[ch];
                memset(ptr, 0, pwd->block_len * sizeof(int16_t));
                for (;;) {
-                       code = get_vlc(&pwd->gb, coef_vlc->table,
-                               VLCBITS, VLCMAX);
+                       code = get_vlc(&pwd->gb, coef_vlc->table, VLCBITS);
                        if (code < 0)
                                return code;
                        if (code == 1) /* EOB */
@@ -1046,7 +1028,7 @@ static inline int16_t av_clip_int16(int a)
 /* Decode a frame of frame_len samples. */
 static int wma_decode_frame(struct private_wmadec_data *pwd, int16_t *samples)
 {
-       int ret, i, n, ch, incr;
+       int ret, i, ch;
        int16_t *ptr;
        float *iptr;
 
@@ -1061,15 +1043,13 @@ static int wma_decode_frame(struct private_wmadec_data *pwd, int16_t *samples)
        }
 
        /* convert frame to integer */
-       n = pwd->frame_len;
-       incr = pwd->ahi.channels;
        for (ch = 0; ch < pwd->ahi.channels; ch++) {
                ptr = samples + ch;
                iptr = pwd->frame_out[ch];
 
-               for (i = 0; i < n; i++) {
+               for (i = 0; i < pwd->frame_len; i++) {
                        *ptr = av_clip_int16(lrintf(*iptr++));
-                       ptr += incr;
+                       ptr += pwd->ahi.channels;
                }
                /* prepare for next block */
                memmove(&pwd->frame_out[ch][0], &pwd->frame_out[ch][pwd->frame_len],
@@ -1086,14 +1066,17 @@ static int wma_decode_superframe(struct private_wmadec_data *pwd, void *data,
 
        if (buf_size == 0) {
                pwd->last_superframe_len = 0;
+               *data_size = 0;
                return 0;
        }
-       if (buf_size < pwd->ahi.block_align)
+       if (buf_size < pwd->ahi.block_align) {
+               *data_size = 0;
                return 0;
+       }
        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;
 
@@ -1147,7 +1130,7 @@ static int wma_decode_superframe(struct private_wmadec_data *pwd, void *data,
                if (len > 0)
                        skip_bits(&pwd->gb, len);
 
-               pwd->reset_block_lengths = 1;
+               pwd->reset_block_lengths = true;
                for (i = 0; i < nb_frames; i++) {
                        ret = wma_decode_frame(pwd, samples);
                        if (ret < 0)
@@ -1210,9 +1193,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;
@@ -1227,7 +1210,7 @@ next_buffer:
        if (ret == 0)
                return 0;
        btr_merge(btrn, fn->min_iqs);
-       len = btr_next_buffer(btrn, (char **)&in);
+       len = btr_next_buffer(btrn, &in);
        ret = -E_WMADEC_EOF;
        if (len < fn->min_iqs)
                goto err;
@@ -1239,12 +1222,12 @@ next_buffer:
                        fn->min_iqs += 4096;
                        goto next_buffer;
                }
-               fn->min_iqs = 2 * (WMA_FRAME_SKIP + pwd->ahi.block_align);
+               fn->min_iqs = 2 * pwd->ahi.packet_size;
                fn->private_data = pwd;
                converted = pwd->ahi.header_len;
                goto success;
        }
-       fn->min_iqs = WMA_FRAME_SKIP + pwd->ahi.block_align;
+       fn->min_iqs = pwd->ahi.packet_size;
        if (fn->min_iqs > len)
                goto success;
        out_size = WMA_OUTPUT_BUFFER_SIZE;
@@ -1255,10 +1238,12 @@ next_buffer:
                free(out);
                goto err;
        }
-       out = para_realloc(out, out_size);
-       if (out_size > 0)
+       if (out_size > 0) {
+               out = para_realloc(out, out_size);
                btr_add_output(out, out_size, btrn);
-       converted += ret + WMA_FRAME_SKIP;
+       } else
+               free(out);
+       converted += pwd->ahi.packet_size;
 success:
        btr_consume(btrn, converted);
        return 0;
diff --git a/write.c b/write.c
index 6799db4175282b779c2fd454d11988be31b933d8..62caf09757ec52d45f825698d7f4656696d7bcff 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.
  */
@@ -24,7 +24,8 @@
 #include "version.h"
 #include "check_wav.h"
 
-INIT_WRITE_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
 static struct write_args_info conf;
 
@@ -71,19 +72,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 +95,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 +124,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 +149,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.
  */