Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Fri, 28 Jul 2017 08:03:34 +0000 (10:03 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 28 Jul 2017 08:03:34 +0000 (10:03 +0200)
A fix for an old bug which should be propagated to master and next.

* maint:
  vss: Avoid use after free in vss_send().

222 files changed:
.gitignore
Doxyfile
INSTALL
Makefile.in
Makefile.real
NEWS.md
aac.h [deleted file]
aac_afh.c
aac_common.c [deleted file]
aacdec_filter.c
acl.c
acl.h
afh.c
afh.h
afh_common.c
afh_recv.c
afs.c
afs.cmd [deleted file]
afs.h
aft.c
alsa_mix.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.cmd [deleted file]
audiod.h
audiod_command.c
autogen.sh
bitstream.c
bitstream.h
blob.c
buffer_tree.c
check_wav.h
client.c
client.h
client_common.c
close_on_fork.c
command.c
command.h
command_util.bash [deleted file]
compress_filter.c
configure.ac
crypt.c
crypt.h
crypt_backend.h
crypt_common.c
daemon.c
dccp_recv.c
dccp_send.c
error.h
fade.c [deleted file]
fd.c
fecdec_filter.c
file_write.c
filter.c
filter.h
filter_common.c
flac_afh.c
flacdec_filter.c
gcrypt.c
ggo.c [deleted file]
ggo.h [deleted file]
grab_client.c
grab_client.h
gui.c
http_recv.c
http_send.c
imdct.c
interactive.c
interactive.h
ipc.c
ipc.h
m4/gengetopt/afh.m4 [deleted file]
m4/gengetopt/afh_recv.m4 [deleted file]
m4/gengetopt/alsa_write.m4 [deleted file]
m4/gengetopt/amp_filter.m4 [deleted file]
m4/gengetopt/ao_write.m4 [deleted file]
m4/gengetopt/audioc.m4 [deleted file]
m4/gengetopt/audiod.m4 [deleted file]
m4/gengetopt/channels.m4 [deleted file]
m4/gengetopt/client.m4 [deleted file]
m4/gengetopt/color.m4 [deleted file]
m4/gengetopt/complete.m4 [deleted file]
m4/gengetopt/compress_filter.m4 [deleted file]
m4/gengetopt/config_file.m4 [deleted file]
m4/gengetopt/daemon.m4 [deleted file]
m4/gengetopt/dccp_recv.m4 [deleted file]
m4/gengetopt/fade.m4 [deleted file]
m4/gengetopt/file_write.m4 [deleted file]
m4/gengetopt/filter.m4 [deleted file]
m4/gengetopt/group.m4 [deleted file]
m4/gengetopt/gui.m4 [deleted file]
m4/gengetopt/header.m4 [deleted file]
m4/gengetopt/history_file.m4 [deleted file]
m4/gengetopt/http_recv.m4 [deleted file]
m4/gengetopt/log_timing.m4 [deleted file]
m4/gengetopt/logfile.m4 [deleted file]
m4/gengetopt/loglevel.m4 [deleted file]
m4/gengetopt/makefile [deleted file]
m4/gengetopt/mp3dec_filter.m4 [deleted file]
m4/gengetopt/oss_write.m4 [deleted file]
m4/gengetopt/osx_write.m4 [deleted file]
m4/gengetopt/play.m4 [deleted file]
m4/gengetopt/prebuffer_filter.m4 [deleted file]
m4/gengetopt/priority.m4 [deleted file]
m4/gengetopt/recv.m4 [deleted file]
m4/gengetopt/resample_filter.m4 [deleted file]
m4/gengetopt/sample_format.m4 [deleted file]
m4/gengetopt/sample_rate.m4 [deleted file]
m4/gengetopt/server.m4 [deleted file]
m4/gengetopt/sync_filter.m4 [deleted file]
m4/gengetopt/udp_recv.m4 [deleted file]
m4/gengetopt/user.m4 [deleted file]
m4/gengetopt/write.m4 [deleted file]
m4/lls/afh.suite.m4 [new file with mode: 0644]
m4/lls/audioc.suite.m4 [new file with mode: 0644]
m4/lls/audiod.suite.m4 [new file with mode: 0644]
m4/lls/audiod_cmd.suite.m4 [new file with mode: 0644]
m4/lls/client.suite.m4 [new file with mode: 0644]
m4/lls/copyright.m4 [new file with mode: 0644]
m4/lls/filter.suite.m4 [new file with mode: 0644]
m4/lls/filter_cmd.suite.m4 [new file with mode: 0644]
m4/lls/gui.suite.m4 [new file with mode: 0644]
m4/lls/include/channels.m4 [new file with mode: 0644]
m4/lls/include/color.m4 [new file with mode: 0644]
m4/lls/include/common-option-section.m4 [new file with mode: 0644]
m4/lls/include/complete.m4 [new file with mode: 0644]
m4/lls/include/config-file.m4 [new file with mode: 0644]
m4/lls/include/daemon.m4 [new file with mode: 0644]
m4/lls/include/detailed-help.m4 [new file with mode: 0644]
m4/lls/include/group.m4 [new file with mode: 0644]
m4/lls/include/help.m4 [new file with mode: 0644]
m4/lls/include/history-file.m4 [new file with mode: 0644]
m4/lls/include/host.m4 [new file with mode: 0644]
m4/lls/include/log-timing.m4 [new file with mode: 0644]
m4/lls/include/logfile.m4 [new file with mode: 0644]
m4/lls/include/loglevel.m4 [new file with mode: 0644]
m4/lls/include/per-command-options-section.m4 [new file with mode: 0644]
m4/lls/include/port.m4 [new file with mode: 0644]
m4/lls/include/priority.m4 [new file with mode: 0644]
m4/lls/include/sample-format.m4 [new file with mode: 0644]
m4/lls/include/sample-rate.m4 [new file with mode: 0644]
m4/lls/include/user.m4 [new file with mode: 0644]
m4/lls/include/version.m4 [new file with mode: 0644]
m4/lls/makefile [new file with mode: 0644]
m4/lls/mixer.suite.m4 [new file with mode: 0644]
m4/lls/play.suite.m4 [new file with mode: 0644]
m4/lls/play_cmd.suite.m4 [new file with mode: 0644]
m4/lls/recv.suite.m4 [new file with mode: 0644]
m4/lls/recv_cmd.suite.m4 [new file with mode: 0644]
m4/lls/server.suite.m4 [new file with mode: 0644]
m4/lls/server_cmd.suite.m4 [new file with mode: 0644]
m4/lls/write.suite.m4 [new file with mode: 0644]
m4/lls/write_cmd.suite.m4 [new file with mode: 0644]
man_util.bash [deleted file]
mix.h
mixer.c [new file with mode: 0644]
mm.c
mm.h
mood.c
mp3_afh.c
mp3dec_filter.c
net.c
net.h
ogg_afh.c
ogg_afh_common.c
oggdec_filter.c
opusdec_filter.c
oss_mix.c
oss_write.c
osx_write.c [deleted file]
para.h
play.c
play.cmd [deleted file]
playlist.c
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
sched.c
score.c
send.h
send_common.c
server.c
server.cmd [deleted file]
server.h
spxdec_filter.c
stdout.c
string.c
string.h
sync_filter.c
t/makefile.test
t/t0001-oggdec-correctness.sh
t/t0004-server.sh
t/t0005-man.sh
t/test-lib.sh
time.c
udp_recv.c
udp_send.c
user_list.c
user_list.h
vss.c
wav_filter.c
web/.htaccess
web/about.in.html
web/dia/overview.dia [deleted file]
web/documentation.in.html
web/download.in.html
web/images/paraslash.ico
web/images/paraslash.png
web/manual.md
wma_afh.c
wma_common.c
wmadec_filter.c
write.c
write.h
write_common.c
write_common.h [deleted file]

index 10a7a8a91d570d5d9d16a18c1c1d896334adc7b4..10d2572d926ab11ec8de95fa32649aa00f4bf1c6 100644 (file)
@@ -15,6 +15,7 @@ config.status
 Makefile
 TODO
 paraslash-*.tar.bz2
+paraslash-*.tar.xz
 web/dia/overview.pdf
 *.swp
 *.rej
index c607b8eab59985d84cc8ac382536c4d45e76890d..d8960dded71cd867917c0a7589df413cd2321aa1 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.6
+# Doxyfile 1.8.11
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
@@ -46,10 +46,10 @@ PROJECT_NUMBER         =
 
 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.
+# With the PROJECT_LOGO tag one can specify a logo or an 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           =
 
@@ -60,7 +60,7 @@ PROJECT_LOGO           =
 
 OUTPUT_DIRECTORY       = web_sync/doxygen
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# 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
@@ -70,6 +70,14 @@ OUTPUT_DIRECTORY       = web_sync/doxygen
 
 CREATE_SUBDIRS         = NO
 
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = 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.
@@ -85,14 +93,14 @@ CREATE_SUBDIRS         = NO
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# 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 doxygen will prepend the brief
+# 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
@@ -127,7 +135,7 @@ ALWAYS_DETAILED_SEC    = NO
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# 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.
@@ -197,9 +205,9 @@ MULTILINE_CPP_IS_BRIEF = NO
 
 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
@@ -261,11 +269,14 @@ OPTIMIZE_OUTPUT_VHDL   = NO
 # 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.
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), 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: 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.
@@ -284,8 +295,8 @@ 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.
+# be prevented in individual cases 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
@@ -325,13 +336,20 @@ SIP_SUPPORT            = NO
 IDL_PROPERTY_SUPPORT   = YES
 
 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
+# 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
 
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
 # 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
@@ -390,7 +408,7 @@ LOOKUP_CACHE_SIZE      = 0
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# 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 respectively EXTRACT_STATIC tags are set to YES.
@@ -400,35 +418,35 @@ LOOKUP_CACHE_SIZE      = 0
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# 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_PACKAGE tag is set to YES all members with package or internal
+# 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
+# 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
+# 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,
+# This flag is only useful for Objective-C code. If 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 in the documentation. If set to NO, only methods in the interface are
 # included.
 # The default value is: NO.
 
@@ -453,21 +471,21 @@ 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 these classes will be included in the various overviews. This option has
-# no effect if EXTRACT_ALL is enabled.
+# 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 these declarations will be
+# (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 these
+# 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.
 
@@ -481,7 +499,7 @@ HIDE_IN_BODY_DOCS      = 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
+# 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.
@@ -490,12 +508,19 @@ INTERNAL_DOCS          = NO
 CASE_SENSE_NAMES       = YES
 
 # 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
+# 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 HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
 # 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.
@@ -523,14 +548,14 @@ INLINE_INFO            = YES
 
 # 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.
+# 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
 # descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order. Note that
+# 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.
 
@@ -575,27 +600,25 @@ SORT_BY_SCOPE_NAME     = 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 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
+# 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 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.
@@ -620,8 +643,8 @@ ENABLED_SECTIONS       =
 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.
+# 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
@@ -669,8 +692,7 @@ LAYOUT_FILE            =
 # 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.
+# search path. See also \cite for info how to create references.
 
 CITE_BIB_FILES         =
 
@@ -686,7 +708,7 @@ CITE_BIB_FILES         =
 QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# 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.
@@ -694,7 +716,7 @@ QUIET                  = YES
 
 WARNINGS               = YES
 
-# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# 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.
@@ -711,12 +733,18 @@ WARN_IF_DOC_ERROR      = YES
 
 # 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.
+# 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
 
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
 # 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
@@ -740,7 +768,7 @@ WARN_LOGFILE           =
 # 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.
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
 INPUT                  = .
@@ -756,12 +784,17 @@ 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 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.
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# 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, *.pyw, *.f90, *.f, *.for, *.tcl,
+# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
 
 FILE_PATTERNS          = *.c \
                          *.h
@@ -795,10 +828,7 @@ EXCLUDE_SYMLINKS       = NO
 # 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.* \
-                         gcc-compat.h \
-                         *.command_list.h \
-                         *.completion.h
+EXCLUDE_PATTERNS       = gcc-compat.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
@@ -851,6 +881,10 @@ IMAGE_PATH             =
 # 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.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
 
 INPUT_FILTER           =
 
@@ -860,11 +894,15 @@ INPUT_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.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
 
 FILTER_PATTERNS        =
 
 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER ) will also be used to filter the input files that are used for
+# 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.
 
@@ -896,7 +934,7 @@ USE_MDFILE_AS_MAINPAGE =
 # also VERBATIM_HEADERS is set to NO.
 # The default value is: NO.
 
-SOURCE_BROWSER         = YES
+SOURCE_BROWSER         = NO
 
 # Setting the INLINE_SOURCES tag to YES will include the body of functions,
 # classes and enums directly into the documentation.
@@ -924,7 +962,7 @@ REFERENCED_BY_RELATION = YES
 REFERENCES_RELATION    = YES
 
 # 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
+# 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.
@@ -961,7 +999,7 @@ SOURCE_TOOLTIPS        = YES
 # The default value is: NO.
 # This tag requires that the tag SOURCE_BROWSER is set to YES.
 
-USE_HTAGS              = YES
+USE_HTAGS              = NO
 
 # 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
@@ -969,7 +1007,7 @@ USE_HTAGS              = YES
 # See also: Section \class.
 # The default value is: YES.
 
-VERBATIM_HEADERS       = YES
+VERBATIM_HEADERS       = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the alphabetical class index
@@ -1001,7 +1039,7 @@ IGNORE_PREFIX          =
 # Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES 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
@@ -1063,13 +1101,15 @@ HTML_FOOTER            = web/footer.html
 
 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
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are 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.
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_EXTRA_STYLESHEET  =
@@ -1085,7 +1125,7 @@ HTML_EXTRA_STYLESHEET  =
 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
+# will adjust the colors in the style sheet 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
@@ -1116,8 +1156,9 @@ 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.
-# The default value is: YES.
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_TIMESTAMP         = YES
@@ -1213,28 +1254,29 @@ GENERATE_HTMLHELP      = NO
 CHM_FILE               =
 
 # 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
+# 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           =
 
-# 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
 
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# 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     =
 
-# 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. Furthermore it
+# enables the Previous and Next buttons.
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
@@ -1347,7 +1389,7 @@ DISABLE_INDEX          = NO
 # 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
+# HTML help feature. Via custom style sheets (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
@@ -1375,7 +1417,7 @@ ENUM_VALUES_PER_LINE   = 4
 
 TREEVIEW_WIDTH         = 250
 
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# If 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.
@@ -1404,7 +1446,7 @@ 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
+# instead of using pre-rendered 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.
@@ -1474,11 +1516,11 @@ 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.
+# are two flavors 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.
 
@@ -1490,7 +1532,7 @@ SERVER_BASED_SEARCH    = NO
 # 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
+# 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/).
 #
@@ -1503,7 +1545,7 @@ 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
+# 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.
@@ -1541,7 +1583,7 @@ EXTRA_SEARCH_MAPPINGS  =
 # Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES 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
@@ -1572,7 +1614,7 @@ LATEX_CMD_NAME         = latex
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# 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.
@@ -1590,9 +1632,12 @@ COMPACT_LATEX          = NO
 PAPER_TYPE             = a4wide
 
 # 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
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
 # If left blank no extra packages will be included.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
@@ -1606,23 +1651,36 @@ EXTRA_PACKAGES         =
 #
 # 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).
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HEADER           =
 
 # 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.
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the 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_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
 # 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
@@ -1640,8 +1698,8 @@ LATEX_EXTRA_FILES      =
 
 PDF_HYPERLINKS         = NO
 
-# 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
+# If the USE_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.
@@ -1682,11 +1740,19 @@ LATEX_SOURCE_CODE      = NO
 
 LATEX_BIB_STYLE        = plain
 
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# 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: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
 #---------------------------------------------------------------------------
 # Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# 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.
@@ -1701,7 +1767,7 @@ GENERATE_RTF           = NO
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES doxygen generates more compact 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.
 # The default value is: NO.
@@ -1738,11 +1804,21 @@ RTF_STYLESHEET_FILE    =
 
 RTF_EXTENSIONS_FILE    =
 
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF 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_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
 #---------------------------------------------------------------------------
 # Configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
 # classes and files.
 # The default value is: NO.
 
@@ -1766,6 +1842,13 @@ MAN_OUTPUT             = man
 
 MAN_EXTENSION          = .3
 
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
 # 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
@@ -1779,7 +1862,7 @@ MAN_LINKS              = NO
 # Configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# 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.
 
@@ -1793,19 +1876,7 @@ GENERATE_XML           = NO
 
 XML_OUTPUT             = xml
 
-# 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 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
+# 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.
@@ -1818,7 +1889,7 @@ XML_PROGRAMLISTING     = YES
 # Configuration options related to the DOCBOOK output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# 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.
 
@@ -1832,14 +1903,23 @@ GENERATE_DOCBOOK       = NO
 
 DOCBOOK_OUTPUT         = docbook
 
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
 #---------------------------------------------------------------------------
 # 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.
+# 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
@@ -1848,7 +1928,7 @@ GENERATE_AUTOGEN_DEF   = NO
 # Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# 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.
@@ -1856,7 +1936,7 @@ GENERATE_AUTOGEN_DEF   = NO
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# 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.
@@ -1864,9 +1944,9 @@ GENERATE_PERLMOD       = NO
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# 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
+# 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.
@@ -1886,14 +1966,14 @@ PERLMOD_MAKEVAR_PREFIX =
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# 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 only conditional compilation will be
+# 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.
@@ -1909,7 +1989,7 @@ MACRO_EXPANSION        = YES
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# If the SEARCH_INCLUDES tag is set to YES, the include 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.
@@ -1952,9 +2032,9 @@ PREDEFINED             = __GNUC__=4 \
 EXPAND_AS_DEFINED      =
 
 # 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
+# remove all references 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.
@@ -1974,7 +2054,7 @@ SKIP_FUNCTION_MACROS   = YES
 # 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
+# Note: 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.
 
@@ -1986,20 +2066,21 @@ TAGFILES               =
 
 GENERATE_TAGFILE       =
 
-# 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.
+# 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
+# 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
+# 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.
@@ -2016,7 +2097,7 @@ PERL_PATH              = /usr/bin/perl
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# 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
@@ -2041,7 +2122,7 @@ MSCGEN_PATH            =
 
 DIA_PATH               =
 
-# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# 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.
 
@@ -2066,7 +2147,7 @@ HAVE_DOT               = NO
 
 DOT_NUM_THREADS        = 0
 
-# When you want a differently looking font n the dot files that doxygen
+# When you want a differently looking font in 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
@@ -2114,7 +2195,7 @@ COLLABORATION_GRAPH    = YES
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# 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.
@@ -2166,7 +2247,8 @@ INCLUDED_BY_GRAPH      = YES
 #
 # 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.
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
 # The default value is: NO.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2177,7 +2259,8 @@ CALL_GRAPH             = NO
 #
 # 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.
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
 # The default value is: NO.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2200,11 +2283,15 @@ GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot.
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
 # 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.
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
 # The default value is: png.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2247,6 +2334,19 @@ MSCFILE_DIRS           =
 
 DIAFILE_DIRS           =
 
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
 # 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
@@ -2283,7 +2383,7 @@ MAX_DOT_GRAPH_DEPTH    = 0
 
 DOT_TRANSPARENT        = NO
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# Set the DOT_MULTI_TARGETS tag to YES to 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.
@@ -2300,7 +2400,7 @@ DOT_MULTI_TARGETS      = NO
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# 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.
diff --git a/INSTALL b/INSTALL
index 85b4fab1ff04219e136ea00fe6cfb2d1a5ea97ab..4a86e967ee72955a32ed37e2fd88b849f808053e 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,11 @@
 Any knowledge of how to work with mouse and icons is not required.
 
+Installing lopsub
+~~~~~~~~~~~~~~~~~
+       git clone git://git.tuebingen.mpg.de/lopsub
+       cd lopsub && make && sudo make install
+       (see http://people.tuebingen.mpg.de/maan/lopsub/)
+
 Installing osl
 ~~~~~~~~~~~~~~
        git clone git://git.tuebingen.mpg.de/osl
@@ -16,10 +22,8 @@ Installing paraslash from git
 
 Example for cross-compiling
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       export CROSS_COMPILE='armv6j-hardfloat-linux-gnueabi-'
+       export CC='armv6j-hardfloat-linux-gnueabi-gcc'
        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
@@ -29,7 +33,7 @@ Example for cross-compiling
        autoconf
        autoheader
        ./configure --host=arm-linux-gnueabi --prefix /usr/sysroot/arm/usr/local
-       make CROSS_COMPILE=$CROSS_COMPILE
+       make
 
 For details see the user manual:
 
index ec55c8e388a78a4fb88164aa0030320738e62c6e..b3e3be5cc82deb414f85404af830759ccd0e0963 100644 (file)
@@ -8,17 +8,9 @@ datarootdir := @datarootdir@
 PACKAGE_TARNAME := @PACKAGE_TARNAME@
 PACKAGE_VERSION := @PACKAGE_VERSION@
 
-INSTALL := @INSTALL@
 M4 := @M4@
-GENGETOPT := @GENGETOPT@
-HELP2MAN := @HELP2MAN@
-
-ggo_descriptions_declared := @ggo_descriptions_declared@
 
 executables := @executables@
-receivers := @receivers@
-filters := @filters@
-writers := @writers@
 
 recv_objs := @recv_objs@
 filter_objs := @filter_objs@
@@ -26,7 +18,7 @@ client_objs := @client_objs@
 gui_objs := @gui_objs@
 audiod_objs := @audiod_objs@
 audioc_objs := @audioc_objs@
-fade_objs := @fade_objs@
+mixer_objs := @mixer_objs@
 server_objs := @server_objs@
 write_objs := @write_objs@
 afh_objs := @afh_objs@
@@ -39,6 +31,7 @@ osl_cppflags := @osl_cppflags@
 id3tag_cppflags := @id3tag_cppflags@
 openssl_cppflags := @openssl_cppflags@
 gcrypt_cppflags := @gcrypt_cppflags@
+lopsub_cppflags := @lopsub_cppflags@
 ogg_cppflags := @ogg_cppflags@
 mad_cppflags := @mad_cppflags@
 faad_cppflags := @faad_cppflags@
@@ -50,9 +43,7 @@ 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@
@@ -61,6 +52,7 @@ opus_ldflags := @opus_ldflags@
 faad_ldflags := @faad_ldflags@
 mad_ldflags := @mad_ldflags@
 flac_ldflags := @flac_ldflags@
+lopsub_ldflags := @lopsub_ldflags@
 oss_ldflags := @oss_ldflags@
 alsa_ldflags := @alsa_ldflags@
 pthread_ldflags := @pthread_ldflags@
@@ -69,9 +61,7 @@ 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
index 8ededf6aff8300608a5939429779ecd7b1753626..3631a5c9f47569c48d38df3877cea286a12e468b 100644 (file)
@@ -7,9 +7,9 @@ ifeq ("$(origin CC)", "default")
         CC := cc
 endif
 
+LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
 vardir := /var/paraslash
 mandir := $(datarootdir)/man/man1
-STRIP := $(CROSS_COMPILE)strip
 MKDIR_P := mkdir -p
 prefixed_executables := $(addprefix para_, $(executables))
 
@@ -25,23 +25,37 @@ ifeq ("$(origin O)", "command line")
 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
+lls_suite_dir := $(build_dir)/lls
+lls_m4_dir := m4/lls
 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) \
+       $(audiod_objs) $(audioc_objs) $(mixer_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)))
+deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d))
+
+afh_objs += afh.lsg.o
+audioc_objs += audioc.lsg.o
+audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) \
+       client.lsg.o audiod.lsg.o
+client_objs += client.lsg.o
+mixer_objs += mixer.lsg.o
+filter_objs += filter_cmd.lsg.o filter.lsg.o
+gui_objs += gui.lsg.o
+play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
+recv_objs += recv_cmd.lsg.o recv.lsg.o
+server_objs += server_cmd.lsg.o server.lsg.o
+write_objs += write_cmd.lsg.o write.lsg.o
+
+cmd_suites := $(addsuffix _cmd, audiod server play recv filter write)
+suites := $(addprefix $(lls_suite_dir)/, $(cmd_suites) $(executables))
+m4_lls_deps := $(addsuffix .m4d, $(suites))
+lsg_h := $(addsuffix .lsg.h, $(suites))
 
 # now prefix all objects with object dir
 recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
@@ -50,7 +64,7 @@ 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))
+mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs))
 server_objs := $(addprefix $(object_dir)/, $(server_objs))
 write_objs := $(addprefix $(object_dir)/, $(write_objs))
 afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
@@ -61,59 +75,46 @@ 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
+tarball := $(tarball_pfx).tar.xz
 
-.PHONY: all clean clean2 distclean maintainer-clean install man tarball
 all: $(prefixed_executables) $(man_pages)
+.PHONY: all mostlyclean clean distclean maintainer-clean install \
+       install-strip man dist tarball
+
 man: $(man_pages)
-tarball: $(tarball)
 
-include $(m4_ggo_dir)/makefile
+include $(lls_m4_dir)/makefile
 include $(test_dir)/makefile.test
 ifeq ($(findstring clean, $(MAKECMDGOALS)),)
 -include $(deps)
--include $(m4_deps)
+-include $(m4_lls_deps)
 endif
 
-$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir) \
-               $(help2man_dir) $(cmdlist_dir):
+$(object_dir) $(man_dir) $(dep_dir) $(m4depdir) $(lls_suite_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 += -DLOGLEVELS='$(LOGLEVELS)'
 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
+CPPFLAGS += -I$(lls_suite_dir)
+CPPFLAGS += $(lopsub_cppflags)
+
+STRICT_CFLAGS += -fno-strict-aliasing
+STRICT_CFLAGS += -g
+STRICT_CFLAGS += -Os
+STRICT_CFLAGS += -Wundef -W -Wuninitialized
+STRICT_CFLAGS += -Wchar-subscripts
+STRICT_CFLAGS += -Werror-implicit-function-declaration
+STRICT_CFLAGS += -Wmissing-noreturn
+STRICT_CFLAGS += -Wbad-function-cast
 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)
+STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
@@ -140,149 +141,107 @@ 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 $@
+audiod_commands := $(addprefix $(lls_suite_dir)/, \
+       $(addsuffix _cmd.lsg.man, audiod recv filter write))
+filter_commands := $(lls_suite_dir)/filter_cmd.lsg.man
+play_commands := $(lls_suite_dir)/play_cmd.lsg.man
+recv_commands := $(lls_suite_dir)/recv_cmd.lsg.man
+server_commands := $(lls_suite_dir)/server_cmd.lsg.man
+write_commands := $(lls_suite_dir)/write_cmd.lsg.man
+
+$(man_dir)/para_audiod.1: $(audiod_commands)
+$(man_dir)/para_filter.1: $(filter_commands)
+$(man_dir)/para_play.1: $(play_commands)
+$(man_dir)/para_recv.1: $(recv_commands)
+$(man_dir)/para_server.1: $(server_commands)
+$(man_dir)/para_write.1: $(write_commands)
+
+$(man_dir)/para_audiod.1: all_commands := $(audiod_commands)
+$(man_dir)/para_filter.1: all_commands := $(filter_commands)
+$(man_dir)/para_play.1: all_commands := $(play_commands)
+$(man_dir)/para_recv.1: all_commands := $(recv_commands)
+$(man_dir)/para_server.1: all_commands := $(server_commands)
+$(man_dir)/para_write.1: all_commands := $(write_commands)
+
+$(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
+               $(lls_m4_dir)/copyright.m4 | $(man_dir)
+       @[ -z "$(Q)" ] || echo 'LLSMAN $<'
+       $(Q) cat $< $(all_commands) > $@
+       $(Q) $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@
 
 $(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 \
+$(object_dir)/opus%.o: CPPFLAGS += $(opus_cppflags)
+$(object_dir)/gui.o $(object_dir)/gui%.o \
 : 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)/spx%.o: CPPFLAGS += $(speex_cppflags)
+$(object_dir)/flac%.o: 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)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
+$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
+$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
+$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
 
-$(object_dir)/interactive.o $(dep_dir)/interactive.d \
+$(object_dir)/interactive.o \
 : CPPFLAGS += $(readline_cppflags)
 
-$(object_dir)/resample_filter.o $(dep_dir)/resample_filter.d \
+$(object_dir)/resample_filter.o \
 : CPPFLAGS += $(samplerate_cppflags)
 
-$(object_dir)/oss_write.o $(dep_dir)/oss_write.d \
+$(object_dir)/oss_write.o \
 : CPPFLAGS += $(oss_cppflags)
 
-$(object_dir)/ao_write.o $(dep_dir)/ao_write.d \
+$(object_dir)/ao_write.o \
 : CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
 
-$(object_dir)/mp3dec_filter.o $(dep_dir)/mp3dec_filter.d \
+$(object_dir)/mp3dec_filter.o \
 : 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 \
+$(object_dir)/aacdec_filter.o \
+$(object_dir)/aac_afh.o \
 : CPPFLAGS += $(faad_cppflags)
 
-$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
-$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/oggdec_filter.o \
 : 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 \
+$(object_dir)/spx_common.o \
+$(object_dir)/spxdec_filter.o \
+$(object_dir)/spx_afh.o \
+$(object_dir)/oggdec_filter.o \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/ogg_afh_common.o \
+$(object_dir)/opus%.o \
 : 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 \
+$(object_dir)/afs.o \
+$(object_dir)/aft.o \
+$(object_dir)/attribute.o \
+$(object_dir)/blob.o  \
+$(object_dir)/mood.o \
+$(object_dir)/playlist.o \
+$(object_dir)/score.o \
+$(object_dir)/server.o \
+$(object_dir)/vss.o \
+$(object_dir)/command.o \
+$(object_dir)/http_send.o \
+$(object_dir)/dccp_send.o \
+$(object_dir)/udp_send.o \
+$(object_dir)/send_common.o \
+$(object_dir)/mm.o \
 : 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)
+$(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h)
        @[ -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" > $@
+       $(Q) $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
+               $(STRICT_CFLAGS) $(CFLAGS) $<
 
 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)
+: LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
 para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
 para_server: LDFLAGS += $(osl_ldflags)
 para_gui: LDFLAGS += $(curses_ldflags)
@@ -301,14 +260,29 @@ para_play \
        $(samplerate_ldflags) \
        -lm
 
+para_mixer: LDFLAGS += -lm
+
 para_write \
 para_play \
 para_audiod \
-para_fade \
+para_mixer \
 : LDFLAGS += \
        $(oss_ldflags) \
        $(alsa_ldflags)
 
+para_afh \
+para_audioc \
+para_audiod \
+para_client \
+para_mixer \
+para_filter \
+para_gui \
+para_play \
+para_recv \
+para_server \
+para_write \
+: LDFLAGS += $(lopsub_ldflags)
+
 para_server \
 para_filter \
 para_audiod \
@@ -323,13 +297,6 @@ para_recv \
        $(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)))
@@ -337,31 +304,36 @@ $(prefixed_executables):
        @[ -z "$(Q)" ] || echo 'LD $@'
        $(Q) $(CC) $^ -o $@ $(LDFLAGS)
 
-clean:
-       @[ -z "$(Q)" ] || echo 'CLEAN'
+mostlyclean:
+       @[ -z "$(Q)" ] || echo 'MOSTLYCLEAN'
        $(Q) rm -f para_*
        $(Q) rm -rf $(object_dir)
-
-clean2: clean
-       @[ -z "$(Q)" ] || echo 'CLEAN2'
+clean: mostlyclean
+       @[ -z "$(Q)" ] || echo 'CLEAN'
        $(Q) rm -rf $(build_dir)
-distclean: clean2 test-clean
+distclean: clean
        @[ -z "$(Q)" ] || echo 'DISTCLEAN'
        $(Q) rm -f Makefile autoscan.log config.status config.log
-       $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
-
+       $(Q) rm -f config.h configure config.h.in
 maintainer-clean: distclean
        @[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN'
-       $(Q) rm -f *.tar.bz2 config.h configure config.h.in
+       $(Q) rm -f *.tar.bz2 *.tar.xz
+       $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
+
+INSTALL ?= install
+INSTALL_PROGRAM ?= $(INSTALL)
+INSTALL_DATA ?= $(INSTALL) -m 644
+ifneq ($(findstring strip, $(MAKECMDGOALS)),)
+       strip_option := -s
+endif
 
-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
+install install-strip: all man
+       $(MKDIR_P) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)
+       $(INSTALL) $(strip_option) $(prefixed_executables) $(DESTDIR)$(bindir)
+       $(INSTALL_DATA) $(man_pages) $(DESTDIR)$(mandir)
+       $(MKDIR_P) $(DESTDIR)$(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
 
-$(tarball):
+$(tarball) dist tarball:
        $(Q) rm -rf $(tarball) $(tarball_pfx)
        $(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
                | tar --delete $(tarball_delete) > $(tarball_pfx).tar
@@ -369,7 +341,7 @@ $(tarball):
        $(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) xz -9 $(tarball_pfx).tar
        $(Q) ls -l $(tarball)
-       $(Q) ln -sf $(tarball) paraslash-git.tar.bz2
+       $(Q) ln -sf $(tarball) paraslash-git.tar.xz
        $(Q) rm -rf $(tarball_pfx)
diff --git a/NEWS.md b/NEWS.md
index 6cb098c56adae3ce61ca39ce8b73d2a81ef6c7c4..894922f885ad14ac5c25f3f203706d5246889ad2 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,9 +1,93 @@
 NEWS
 ====
 
-------------------------------------------
-0.5.7 (to be announced) "semantic density"
-------------------------------------------
+---------------------
+current master branch
+---------------------
+
+- New sort order for the ls command: -s=h sorts the ls output by hash
+  value of the audio file.
+- The contents of overview.pdf have been integrated into the user
+  manual.
+- The doxygen source browser has been disabled temporarily. The
+  API reference is still online, though.
+- Overhaul of the source code documentation.
+
+-------------------------------
+0.6.0 (2017-04-28) "fuzzy flux"
+-------------------------------
+
+The highlights of this release are the conversion of all option parsers
+to the lopsub library and the improved AAC support. But there are
+many other user-visible changes all over the place. Also a lot of
+old cruft has been removed, leading to incompatible changes. Hence
+the new major version number.
+
+- Support for Mac OS X has been removed.
+- On Linux systems, glibc-2.17 or newer is required to build the
+  source tree.
+- Support for RSA public keys in ASN format (as generated by openssl
+  genrsa) has been removed. These keys have been deprecated since
+  2011, so users should have long switched to keys generated with
+  ssh-keygen(1).
+- If libgcrypt is used as the crypto library, we now require version
+  1.5.0 (released in 2011) or later.
+- The insecure RC4 stream cipher has been removed. It was superseded
+  by aes_ctr128 three years ago but the RC4 code had been kept for
+  backwards compatibility.
+- On Linux, abstract unix domain sockets are used unconditionally.
+- The "install" target no longer strips executables, the new
+  install-strip target can be used to get the old behaviour.
+- The clean targets have been renamed: clean2 is gone, and the new
+  mostlyclean removes only the executables and object files.
+- New target: check (identical to test).
+- The DESTDIR make variable is honored to prepend a path to the
+  installation directory. This feature is orthogonal to the --prefix
+  option to configure.
+- Minor WMA cleanups.
+- The aac audio format handler has been rewritten to use the mp4ff library.
+  See the manual for how to install the library on your system.
+- New status item: max_chunk_size. The value is stored in a previously
+  unused field of the afhi object of the aft table. Although backwards
+  compatible, users are encouraged to re-add m4a files to populate
+  the new field.
+- No more chunk tables for aac. Chunk boundaries are determined
+  dynamically at stream time.
+- Release and master branch tarballs are now compressed with xz rather
+  than bzip2.
+- The lopsub package is required to install the paraslash package.
+  Gengetopt is no longer needed.
+- make dep is gone. Dependencies have been created automatically for
+  a long time and it was never necessary to run make dep manually.
+- para_gui lost its --timeout option.
+- Most manual pages have been extended to include an overall
+  description of the command.
+- The --stream-delay option of para_audiod has been removed. It had
+  been a no-op for many years.
+- The deprecated --path option of the server ls command has been
+  removed.  The command now prints full paths by default, making
+  --full-path a no-op. Hence --full-path has been depreacted and is
+  scheduled for removal in v0.6.1.
+- It is now possible to use 'CFLAGS' to override the default compiler
+  options.
+- para_fade has been renamed to para_mixer. The four modes of operation
+  (set, fade, snooze, sleep) are implemented as subcommands of the
+  new program.
+- The sleep subcommand of para_mixer (former sleep mode of para_fade)
+  lost its --wake-hour and --wake-min options in favor of the new
+  --wake-time option which also accepts relative wakeup times like
+  "+9:30".
+- The new --fade-exponent option of para_mixer allows for non-linear
+  channel fading.
+- The new logo.
+
+Downloads:
+[tarball](./releases/paraslash-0.6.0.tar.xz),
+[signature](./releases/paraslash-0.6.0.tar.xz.asc)
+
+-------------------------------------
+0.5.7 (2016-12-31) "semantic density"
+-------------------------------------
 
 Mostly a bug fix release, and a bunch of internal improvements.
 The only user-visible changes are the sanity checks for the touch
@@ -24,7 +108,9 @@ command and the new options to the ls command.
 - New section on contributing for the user manual.
 - Major simplification of the error subsystem.
 
-Download: [tarball](./releases/paraslash-git.tar.bz2)
+Downloads:
+[tarball](./releases/paraslash-0.5.7.tar.bz2),
+[signature](./releases/paraslash-0.5.7.tar.bz2.asc)
 
 -------------------------------------------
 0.4.14 (2016-12-31) "branching oscillation"
diff --git a/aac.h b/aac.h
deleted file mode 100644 (file)
index eeed252..0000000
--- a/aac.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file aac.h Exported symbols from aac_common.c. */
-
-#include <neaacdec.h>
-
-NeAACDecHandle aac_open(void);
-int aac_find_esds(char *buf, size_t buflen, size_t *skip,
-               unsigned long *decoder_length);
-ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip);
index 1c7fd706f0a7c9fe8c9d4d8db43846be18a9304d..8550a8ac6801d421e5cf186b48360922face11e8 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
 /** \file aac_afh.c para_server's aac audio format handler. */
 
 #include <regex.h>
-#include <mp4v2/mp4v2.h>
+#include <neaacdec.h>
 
 #include "para.h"
+
+/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
+#define USE_TAGGING
+#include <mp4ff.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(char *buf, size_t buflen, off_t *skip)
+
+struct aac_afh_context {
+       const void *map;
+       size_t mapsize;
+       size_t fpos;
+       int32_t track;
+       mp4ff_t *mp4ff;
+       mp4AudioSpecificConfig masc;
+       mp4ff_callback_t cb;
+};
+
+static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
 {
-       int i;
-
-       for (i = 0; i + 16 < buflen; 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 = read_u32_be(buf + i);
-               PARA_DEBUG_LOG("sample size: %u\n", sample_size);
-               i += 4;
-               sample_count = read_u32_be(buf + i);
-               i += 4;
-               PARA_DEBUG_LOG("sample count: %u\n", sample_count);
-               *skip = i;
-               return sample_count;
+       struct aac_afh_context *c = user_data;
+       uint32_t have, rv;
+
+       if (want == 0 || c->fpos >= c->mapsize) {
+               PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
+                       c->fpos);
+               errno = EAGAIN;
+               return -1;
        }
-       return -E_STSZ;
+       have = c->mapsize - c->fpos;
+       rv = PARA_MIN(have, want);
+       PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
+       memcpy(dest, c->map + c->fpos, rv);
+       c->fpos += rv;
+       return rv;
 }
 
-static int atom_cmp(const char *buf1, const char *buf2)
+static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
 {
-       return memcmp(buf1, buf2, 4)? 1 : 0;
+       struct aac_afh_context *c = user_data;
+       c->fpos = pos;
+       return 0;
 }
 
-static int read_atom_header(char *buf, uint64_t *subsize, char type[5])
+static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
 {
-       uint64_t size = read_u32_be(buf);
-
-       memcpy(type, buf + 4, 4);
-       type[4] = '\0';
-
-       PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
-       if (size != 1) {
-               *subsize = size;
-               return 8;
+       int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
+
+       assert(num_tracks >= 0);
+       for (i = 0; i < num_tracks; i++) {
+               unsigned char *buf = NULL;
+               unsigned buf_size = 0;
+
+               mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
+               if (buf) {
+                       rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
+                       free(buf);
+                       if (rc < 0)
+                               continue;
+                       return i;
+               }
        }
-       buf += 4;
-       size = 0;
-       size = read_u64_be(buf);
-       *subsize = size;
-       return 16;
+       return -1; /* no audio track */
 }
 
-static char *get_tag(char *p, int size)
+static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
 {
-       char *buf;
-
-       assert(size > 0);
-       buf = para_malloc(size + 1);
-
-       memcpy(buf, p, size);
-       buf[size] = '\0';
-       PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
-       return buf;
+       int ret;
+       struct aac_afh_context *c = para_malloc(sizeof(*c));
+
+       c->map = map;
+       c->mapsize = mapsize;
+       c->fpos = 0;
+       c->cb.read = aac_afh_read_cb;
+       c->cb.seek = aac_afh_seek_cb;
+       c->cb.user_data = c;
+
+       ret = -E_MP4FF_OPEN;
+       c->mp4ff = mp4ff_open_read(&c->cb);
+       if (!c->mp4ff)
+               goto free_ctx;
+       c->track = aac_afh_get_track(c->mp4ff, &c->masc);
+       ret = -E_MP4FF_TRACK;
+       if (c->track < 0)
+               goto close_mp4ff;
+       *afh_context = c;
+       return 0;
+close_mp4ff:
+       mp4ff_close(c->mp4ff);
+free_ctx:
+       free(c);
+       *afh_context = NULL;
+       return ret;
 }
 
-static void read_tags(char *buf, size_t buflen, struct afh_info *afhi)
+static void aac_afh_close(void *afh_context)
 {
-       char *p = buf;
+       struct aac_afh_context *c = afh_context;
+       mp4ff_close(c->mp4ff);
+       free(c);
+}
 
-       while (p + 32 < buf + buflen) {
-               char *q, type1[5], type2[5];
-               uint64_t size1, size2;
-               int ret, ret2;
+/**
+ * Libmp4ff function to reposition the file to the given sample.
+ *
+ * \param f The opaque handle returned by mp4ff_open_read().
+ * \param track The number of the (audio) track.
+ * \param sample Destination.
+ *
+ * We need this function to obtain the offset of the sample within the audio
+ * file. Unfortunately, it is not exposed in the mp4ff header.
+ *
+ * \return This function always returns 0.
+ */
+int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
 
-               ret = read_atom_header(p, &size1, type1);
-               ret2 = read_atom_header(p + ret, &size2, type2);
+static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
+               const char **buf, size_t *len)
+{
+       struct aac_afh_context *c = afh_context;
+       int32_t ss;
+       size_t offset;
+
+       assert(chunk_num <= INT_MAX);
+       /* this function always returns zero */
+       mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+       offset = c->fpos;
+       ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
+       if (ss <= 0)
+               return -E_MP4FF_BAD_SAMPLE;
+       assert(ss + offset <= c->mapsize);
+       *buf = c->map + offset;
+       *len = ss;
+       return 1;
+}
 
-               if (size2 <= 16 || atom_cmp(type2, "data")) {
-                       p += size1;
-                       continue;
-               }
-               size2 -= 16;
-               q = p + ret + ret2 + 8;
-               if (q + size2 > buf + buflen)
-                       break;
-               if (!atom_cmp(type1, "\xa9" "ART"))
-                       afhi->tags.artist = get_tag(q, size2);
-               else if (!atom_cmp(type1, "\xa9" "alb"))
-                       afhi->tags.album = get_tag(q, size2);
-               else if (!atom_cmp(type1, "\xa9" "nam"))
-                       afhi->tags.title = get_tag(q, size2);
-               else if (!atom_cmp(type1, "\xa9" "cmt"))
-                       afhi->tags.comment = get_tag(q, size2);
-               else if (!atom_cmp(type1, "\xa9" "day"))
-                       afhi->tags.year = get_tag(q, size2);
-               p += size1;
-       }
+static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+{
+       mp4ff_meta_get_artist(mp4ff, &tags->artist);
+       mp4ff_meta_get_title(mp4ff, &tags->title);
+       mp4ff_meta_get_date(mp4ff, &tags->year);
+       mp4ff_meta_get_album(mp4ff, &tags->album);
+       mp4ff_meta_get_comment(mp4ff, &tags->comment);
 }
 
-static void read_meta(char *buf, size_t buflen, struct afh_info *afhi)
+/*
+ * Init m4a file and write some tech data to given pointers.
+ */
+static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+               struct afh_info *afhi)
 {
-       char *p = buf;
+       int ret;
+       int32_t rv;
+       struct aac_afh_context *c;
+       int64_t tmp;
+       const char *buf;
+       size_t sz;
+       uint32_t n;
+
+       ret = aac_afh_open(map, numbytes, (void **)&c);
+       if (ret < 0)
+               return ret;
 
-       while (p + 4 < buf + buflen) {
+       ret = -E_MP4FF_BAD_SAMPLERATE;
+       rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
+       if (rv <= 0)
+               goto close;
+       afhi->frequency = rv;
 
-               if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
-                       p++;
-                       continue;
-               }
-               p += 4;
-               return read_tags(p, buflen - (p - buf), afhi);
+       ret = -E_MP4FF_BAD_CHANNEL_COUNT;
+       rv = mp4ff_get_channel_count(c->mp4ff, c->track);
+       if (rv <= 0)
+               goto close;
+       afhi->channels = rv;
+
+       ret = -E_MP4FF_BAD_SAMPLE_COUNT;
+       rv = mp4ff_num_samples(c->mp4ff, c->track);
+       if (rv <= 0)
+               goto close;
+       afhi->chunks_total = rv;
+       afhi->max_chunk_size = 0;
+       for (n = 0; n < afhi->chunks_total; n++) {
+               if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
+                       break;
+               afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
        }
+
+       tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
+       afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
+       ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
+
+       if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
+               numbytes -= buf - map;
+       afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
+       _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+       ret = 1;
+close:
+       aac_afh_close(c);
+       return ret;
 }
 
-static void aac_get_taginfo(char *buf, size_t buflen, struct afh_info *afhi)
+static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
 {
-       int i;
-       uint64_t subsize;
-       char type[5];
-
-       for (i = 0; i + 24 < buflen; 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);
-               i += 8;
-               p = buf + i;
-               i += read_atom_header(p, &subsize, type);
-               p = buf + i;
-               return read_meta(p, buflen - i, afhi);
-       }
-       PARA_INFO_LOG("no meta data\n");
+       int fd = *(int *)user_data;
+       return read(fd, dest, want);
 }
 
-static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
-               char *map, size_t numbytes)
+static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
 {
-       int ret, i;
-       size_t sum = 0;
-       off_t skip;
+       int fd = *(int *)user_data;
+       return lseek(fd, pos, SEEK_SET);
+}
 
-       ret = aac_find_stsz(map, numbytes, &skip);
-       if (ret < 0)
-               return ret;
-       afhi->chunks_total = ret;
-       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 += read_u32_be(map + skip);
-               afhi->chunk_table[i] = sum;
-               skip += 4;
-//             if (i < 10 || i + 10 > afhi->chunks_total)
-//                     PARA_DEBUG_LOG("offset #%d: %zu\n", i, afhi->chunk_table[i]);
-       }
-       return skip;
+static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+{
+       int fd = *(int *)user_data;
+       return write(fd, dest, want);
 }
 
-static int aac_set_chunk_tv(struct afh_info *afhi,
-               mp4AudioSpecificConfig *mp4ASC, uint32_t *seconds)
+static uint32_t aac_afh_meta_truncate_cb(void *user_data)
 {
-       float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023;
-       struct timeval total;
-       long unsigned ms;
-
-       if (!mp4ASC->samplingFrequency)
-               return -E_MP4ASC;
-       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 (%" PRIu32 " x %lums)\n",
-               mp4ASC->samplingFrequency, ms / 1000,
-               afhi->chunks_total, tv2ms(&afhi->chunk_tv));
-       if (ms < 1000)
-               return -E_MP4ASC;
-       *seconds = ms / 1000;
-       return 1;
+       int fd = *(int *)user_data;
+       off_t offset = lseek(fd, 0, SEEK_CUR);
+       return ftruncate(fd, offset);
 }
 
-/*
- * Init m4a file and write some tech data to given pointers.
- */
-static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
-               struct afh_info *afhi)
+static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
 {
-       int i;
-       size_t skip;
-       ssize_t ret;
-       unsigned long rate = 0, decoder_len;
-       unsigned char channels = 0;
-       mp4AudioSpecificConfig mp4ASC;
-       NeAACDecHandle handle = NULL;
-
-       ret = aac_find_esds(map, numbytes, &skip, &decoder_len);
-       if (ret < 0)
-               goto out;
-       aac_get_taginfo(map, numbytes, afhi);
-       handle = aac_open();
-       ret = -E_AAC_AFH_INIT;
-       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((unsigned char *)map + skip,
-                       numbytes - skip, &mp4ASC))
-               goto out;
-       if (!mp4ASC.samplingFrequency)
-               goto out;
-       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(map + skip, numbytes - skip, &skip);
-       if (ret < 0)
-               goto out;
-       afhi->chunk_table[0] = ret;
-       for (i = 1; i<= afhi->chunks_total; i++)
-               afhi->chunk_table[i] += ret;
-       afhi->channels = channels;
-       afhi->frequency = rate;
-       ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
-       ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
-       afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
-       ret = 1;
-out:
-       if (handle)
-               NeAACDecClose(handle);
-       return ret;
+       free(tag->value);
+       tag->value = para_strdup(new_val);
+       *found = true;
 }
 
-static int aac_rewrite_tags(const char *map, size_t mapsize,
-               struct taginfo *tags, int fd, const char *filename)
+static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
 {
-       MP4FileHandle h;
-       const MP4Tags *mdata;
-       int ret = write_all(fd, map, mapsize);
+       md->tags[md->count].item = para_strdup(item);
+       md->tags[md->count].value = para_strdup(value);
+       md->count++;
+}
 
+static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
+               struct taginfo *tags, int fd, __a_unused const char *filename)
+{
+       int ret, i;
+       int32_t rv;
+       mp4ff_metadata_t metadata;
+       mp4ff_t *mp4ff;
+       mp4ff_callback_t cb = {
+               .read = aac_afh_meta_read_cb,
+               .seek = aac_afh_meta_seek_cb,
+               .write = aac_afh_meta_write_cb,
+               .truncate = aac_afh_meta_truncate_cb,
+               .user_data = &fd
+       };
+       bool found_artist = false, found_title = false, found_album = false,
+               found_year = false, found_comment = false;
+
+       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;
-       }
+       mp4ff = mp4ff_open_read_metaonly(&cb);
+       if (!mp4ff)
+               return -E_MP4FF_OPEN;
 
-       if (!MP4TagsStore(mdata, h)) {
-               PARA_ERROR_LOG("Could not store tags\n");
-               ret = -E_MP4V2;
-               goto tags_free;
+       ret = -E_MP4FF_META_READ;
+       rv = mp4ff_meta_get_num_items(mp4ff);
+       if (rv < 0)
+               goto close;
+       metadata.count = rv;
+       PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
+
+       metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
+       for (i = 0; i < metadata.count; i++) {
+               mp4ff_tag_t *tag = metadata.tags + i;
+
+               ret = -E_MP4FF_META_READ;
+               if (mp4ff_meta_get_by_index(mp4ff, i,
+                               &tag->item, &tag->value) < 0)
+                       goto free_tags;
+               PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
+               if (!strcmp(tag->item, "artist"))
+                       replace_tag(tag, tags->artist, &found_artist);
+               else if (!strcmp(tag->item, "title"))
+                       replace_tag(tag, tags->title, &found_title);
+               else if (!strcmp(tag->item, "album"))
+                       replace_tag(tag, tags->album, &found_album);
+               else if (!strcmp(tag->item, "date"))
+                       replace_tag(tag, tags->year, &found_year);
+               else if (!strcmp(tag->item, "comment"))
+                       replace_tag(tag, tags->comment, &found_comment);
        }
+       if (!found_artist)
+               add_tag(&metadata, "artist", tags->artist);
+       if (!found_title)
+               add_tag(&metadata, "title", tags->title);
+       if (!found_album)
+               add_tag(&metadata, "album", tags->album);
+       if (!found_year)
+               add_tag(&metadata, "date", tags->year);
+       if (!found_comment)
+               add_tag(&metadata, "comment", tags->comment);
+       ret = -E_MP4FF_META_WRITE;
+       if (mp4ff_meta_update(&cb, &metadata) < 0)
+               goto free_tags;
        ret = 1;
-tags_free:
-       MP4TagsFree(mdata);
+free_tags:
+       for (; i > 0; i--) {
+               free(metadata.tags[i - 1].item);
+               free(metadata.tags[i - 1].value);
+       }
+       free(metadata.tags);
 close:
-       MP4Close(h, 0);
+       mp4ff_close(mp4ff);
        return ret;
 }
 
@@ -326,5 +346,8 @@ 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;
+       afh->rewrite_tags = aac_afh_rewrite_tags;
+       afh->open = aac_afh_open;
+       afh->get_chunk = aac_afh_get_chunk;
+       afh->close = aac_afh_close;
 }
diff --git a/aac_common.c b/aac_common.c
deleted file mode 100644 (file)
index 812c742..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/*
- * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
- * Ahead Software AG
- */
-
-/** \file aac_common.c Common functions of aac_afh and aadcec. */
-
-#include "para.h"
-#include "aac.h"
-#include "error.h"
-#include "portable_io.h"
-
-/**
- * Get a new libfaad decoder handle.
- *
- * \return The handle returned by NeAACDecOpen().
- */
-NeAACDecHandle aac_open(void)
-{
-       NeAACDecHandle h = NeAACDecOpen();
-       NeAACDecConfigurationPtr c = NeAACDecGetCurrentConfiguration(h);
-
-       c->defObjectType = LC;
-       c->outputFormat = FAAD_FMT_16BIT;
-       c->downMatrix = 0;
-       NeAACDecSetConfiguration(h, c);
-       return h;
-}
-
-static unsigned long aac_read_decoder_length(char *buf, int *description_len)
-{
-       uint8_t b;
-       uint8_t numBytes = 0;
-       unsigned long length = 0;
-
-       do {
-               b = buf[numBytes];
-               numBytes++;
-               length = (length << 7) | (b & 0x7F);
-       } while
-               ((b & 0x80) && numBytes < 4);
-       *description_len = numBytes;
-       return length;
-}
-
-/**
- * search for the position and the length of the decoder configuration
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the offset in \a buf where
- * the decoder config starts.
- * \param decoder_length result pointer that is filled in with the length of
- * the decoder configuration on success.
- *
- * \return positive on success, negative on errors
- */
-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++) {
-               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, (unsigned)*p);
-               if (*p == 3)
-                       i += 8;
-               else
-                       i += 6;
-               p = buf + i;
-               PARA_INFO_LOG("next: %x\n", (unsigned)*p);
-               if (*p != 4)
-                       continue;
-               i += 18;
-               p = buf + i;
-               PARA_INFO_LOG("next: %x\n", (unsigned)*p);
-               if (*p != 5)
-                       continue;
-               i++;
-               p = buf + i;
-               *decoder_length = aac_read_decoder_length(p, &description_len);
-               PARA_INFO_LOG("decoder length: %lu\n", *decoder_length);
-               i += description_len;
-               *skip = i;
-               return 1;
-       }
-       return -E_ESDS;
-}
-
-/**
- * search for the first entry in the stco table
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the number
- * of bytes to skip from the input buffer.
- *
- * \return the position of the first entry in the table on success,
- * -E_STCO on errors.
- */
-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++) {
-               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 = read_u32_be(buf + i); /* first offset */
-               i += 4;
-               PARA_INFO_LOG("entry point: %zd\n", ret);
-               *skip = i;
-               return ret;
-       }
-       PARA_WARNING_LOG("stco not found, buflen: %zu\n", buflen);
-       return -E_STCO;
-}
index 5725ce043089361092ddb68e05bb3c60fcf66801..ce3eb3bd93dc315afc91a01737a3a1a2195e1b36 100644 (file)
 /** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
 
 #include <regex.h>
+#include <neaacdec.h>
 
 #include "para.h"
+#include "portable_io.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
-#include "aac.h"
 
 /** Give up decoding after that many errors. */
 #define MAX_ERRORS 20
 /**
  * data specific to the aacdec filter
  *
- * \sa filter, filter_node
+ * \sa \ref filter, \ref filter_node.
  */
 struct private_aacdec_data {
        /** the return value of aac_open */
        NeAACDecHandle handle;
-       /** info about the currently decoded frame */
-       NeAACDecFrameInfo frame_info;
        /** whether this instance of the aac decoder is already initialized */
-       int initialized;
-       /**
-        * return value of aac_find_esds(). Used to call the right aacdec
-        * init function
-        */
-       unsigned long decoder_length;
+       bool initialized;
        /** number of times the decoder returned an error */
        unsigned error_count;
        /** number of bytes already consumed from the imput stream */
        size_t consumed_total;
-       /** return value of aac_find_entry_point */
-       size_t entry;
        /** The number of channels of the current stream. */
        unsigned int channels;
        /** Current sample rate in Hz. */
@@ -64,11 +55,18 @@ static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result)
 
 static void aacdec_open(struct filter_node *fn)
 {
+       NeAACDecConfigurationPtr c;
        struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
 
+       padd->handle = NeAACDecOpen();
+       c = NeAACDecGetCurrentConfiguration(padd->handle);
+       c->defObjectType = LC;
+       c->outputFormat = FAAD_FMT_16BIT;
+       c->downMatrix = 0;
+       NeAACDecSetConfiguration(padd->handle, c);
+
        fn->private_data = padd;
        fn->min_iqs = 2048;
-       padd->handle = aac_open();
 }
 
 static void aacdec_close(struct filter_node *fn)
@@ -86,9 +84,9 @@ static int aacdec_post_select(__a_unused struct sched *s, void *context)
        struct btr_node *btrn = fn->btrn;
        struct private_aacdec_data *padd = fn->private_data;
        int i, ret;
-       char *p, *inbuf, *outbuffer;
-       char *btr_buf;
-       size_t len, skip, consumed, loaded;
+       char *inbuf, *outbuf, *btrbuf;
+       size_t len, consumed, loaded = 0;
+       NeAACDecFrameInfo frame_info;
 
 next_buffer:
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -103,121 +101,68 @@ next_buffer:
        if (!padd->initialized) {
                unsigned long rate = 0;
                unsigned char channels = 0;
-               ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
+               ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
+                       len, &rate, &channels);
+               PARA_INFO_LOG("decoder init: %d\n", ret);
                if (ret < 0) {
-                       PARA_INFO_LOG("%s\n", para_strerror(-ret));
-                       ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
-                               len, &rate, &channels);
-                       PARA_INFO_LOG("decoder init: %d\n", ret);
-                       if (ret < 0) {
-                               ret = -E_AACDEC_INIT;
-                               goto out;
-                       }
-                       consumed = ret;
-               } else {
-                       PARA_INFO_LOG("decoder len: %lu\n",
-                               padd->decoder_length);
-                       consumed += skip;
-                       p = inbuf + consumed;
                        ret = -E_AACDEC_INIT;
-                       if (NeAACDecInit2(padd->handle, (unsigned char *)p,
-                                       padd->decoder_length, &rate,
-                                       &channels) != 0)
-                               goto out;
+                       goto err;
                }
+               consumed = ret;
                padd->sample_rate = rate;
                padd->channels = channels;
                PARA_INFO_LOG("rate: %u, channels: %u\n",
                        padd->sample_rate, padd->channels);
-               padd->initialized = 1;
+               padd->initialized = true;
        }
-       if (padd->decoder_length > 0) {
-               consumed = 0;
-               if (!padd->entry) {
-                       ret = aac_find_entry_point(inbuf + consumed,
-                               len - consumed, &skip);
-                       if (ret < 0) {
-                               ret = len;
-                               goto out;
-                       }
-                       consumed += skip;
-                       padd->entry = ret;
-                       PARA_INFO_LOG("entry: %zu\n", padd->entry);
-               }
-               ret = len;
-               if (padd->consumed_total + len < padd->entry)
-                       goto out;
-               if (padd->consumed_total < padd->entry)
-                       consumed = padd->entry - padd->consumed_total;
-       }
-       for (; consumed < len; consumed++)
-               if ((inbuf[consumed] & 0xfe) == 0x20)
-                       break;
        if (consumed >= len)
                goto success;
-       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,
-               (unsigned char *)p, len - consumed);
-       if (padd->frame_info.error) {
-               int err = padd->frame_info.error;
+       outbuf = NeAACDecDecode(padd->handle, &frame_info,
+               (unsigned char *)inbuf + consumed, len - consumed);
+       if (frame_info.error) {
+               int err = frame_info.error;
                ret = -E_AAC_DECODE;
                if (padd->error_count++ > MAX_ERRORS)
                        goto err;
-               /* Suppress non-fatal bitstream error message at BOF/EOF */
-               if (len < fn->min_iqs || padd->consumed_total == 0) {
-                       consumed = len;
-                       goto success;
-               }
-               PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(err));
-               PARA_ERROR_LOG("consumed: %zu + %zu + %lu\n",
+               PARA_NOTICE_LOG("error #%u: (%s)\n", padd->error_count,
+                       NeAACDecGetErrorMessage(err));
+               PARA_NOTICE_LOG("consumed (total, buffer, frame): "
+                       "%zu, %zu, %lu\n",
                        padd->consumed_total, consumed,
-                       padd->frame_info.bytesconsumed);
-               if (consumed < len)
-                       consumed++; /* catch 21 */
+                       frame_info.bytesconsumed);
+               consumed++; /* just eat one byte and hope for the best */
                goto success;
        }
        padd->error_count = 0;
-       //PARA_CRIT_LOG("decoder ate %lu\n", padd->frame_info.bytesconsumed);
-       consumed += padd->frame_info.bytesconsumed;
-       ret = consumed;
-       if (!padd->frame_info.samples)
-               goto out;
-       btr_buf = para_malloc(2 * padd->frame_info.samples);
-       loaded = 0;
-       for (i = 0; i < padd->frame_info.samples; i++) {
-               short sh = ((short *)outbuffer)[i];
-               write_int16_host_endian(btr_buf + loaded, sh);
+       //PARA_CRIT_LOG("decoder ate %lu\n", frame_info.bytesconsumed);
+       consumed += frame_info.bytesconsumed;
+       if (!frame_info.samples)
+               goto success;
+       btrbuf = para_malloc(2 * frame_info.samples);
+       for (i = 0; i < frame_info.samples; i++) {
+               short sh = ((short *)outbuf)[i];
+               write_int16_host_endian(btrbuf + loaded, sh);
                loaded += 2;
        }
-       btr_add_output(btr_buf, loaded, btrn);
+       btr_add_output(btrbuf, loaded, btrn);
 success:
-       ret = consumed;
-out:
-       if (ret >= 0) {
-               padd->consumed_total += ret;
-               btr_consume(btrn, ret);
+       btr_consume(btrn, consumed);
+       padd->consumed_total += consumed;
+       if (loaded == 0)
                goto next_buffer;
-       }
+       return 1;
 err:
        assert(ret < 0);
        btr_remove_node(&fn->btrn);
        return ret;
 }
 
-/**
- * The init function of the aacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init
- */
-void aacdec_filter_init(struct filter *f)
-{
-       f->open = aacdec_open;
-       f->close = aacdec_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = aacdec_post_select;
-       f->execute = aacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_aacdec_user_data = {
+       .open = aacdec_open,
+       .close = aacdec_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = aacdec_post_select,
+       .execute = aacdec_execute
+};
diff --git a/acl.c b/acl.c
index 560ff9999d191f418c35695f4a6629b45436f49f..62677711e34f7c00e732bb049c705d08a9a3b8e1 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -81,7 +81,7 @@ no_match:
  * \param addr The address to add.
  * \param netmask The netmask to use for this entry.
  */
-static void acl_add_entry(struct list_head *acl, char *addr, int netmask)
+void acl_add_entry(struct list_head *acl, char *addr, int netmask)
 {
        struct access_info *ai = para_malloc(sizeof(struct access_info));
 
@@ -139,27 +139,6 @@ char *acl_get_contents(struct list_head *acl)
        return ret;
 }
 
-/**
- * Initialize an access control list.
- *
- * \param acl The list to initialize.
- * \param acl_info An array of strings of the form ip/netmask.
- * \param num The number of strings in \a acl_info.
- */
-void acl_init(struct list_head *acl, char * const *acl_info, int num)
-{
-       char    addr[16];
-       int     mask, i;
-
-       INIT_LIST_HEAD(acl);
-       for (i = 0; i < num; i++)
-               if (parse_cidr(acl_info[i], addr, sizeof(addr), &mask) == NULL)
-                       PARA_CRIT_LOG("ACL syntax error: %s, ignoring\n",
-                                     acl_info[i]);
-               else
-                       acl_add_entry(acl, addr, mask);
-}
-
 /**
  * Check whether the peer name of a given fd is allowed by an acl.
  *
diff --git a/acl.h b/acl.h
index 5bfa39f23b2c087456471e06ea3d55b816fffbaf..91efa600cdd32be1ab40cbfea3e68f55cf034498 100644 (file)
--- a/acl.h
+++ b/acl.h
@@ -6,7 +6,7 @@
 
 /** \file acl.h Exported functions of acl.c. */
 
-void acl_init(struct list_head *acl, char * const *acl_info, int num);
+void acl_add_entry(struct list_head *acl, char *addr, int netmask);
 char *acl_get_contents(struct list_head *acl);
 int acl_check_access(int fd, struct list_head *acl, int default_deny);
 void acl_allow(char *addr, int mask, struct list_head *acl, int default_deny);
diff --git a/afh.c b/afh.c
index 36c432e54546d7f6079f9eb0ea4b35b675ab806b..9d40caeb8b33fb7442bb1f822af7f6eba0a2c332 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -7,20 +7,26 @@
 /** \file afh.c Paraslash's standalone audio format handler tool. */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "afh.lsg.h"
 #include "para.h"
 #include "string.h"
-#include "afh.cmdline.h"
 #include "fd.h"
 #include "afh.h"
 #include "error.h"
 #include "version.h"
-#include "ggo.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
-static struct afh_args_info conf;
+static struct lls_parse_result *lpr;
+
+#define CMD_PTR (lls_cmd(0, afh_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AFH_PARA_AFH_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
 
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
@@ -37,34 +43,38 @@ static int rewrite_tags(const char *name, int input_fd, void *map,
        struct taginfo *tags = &afhi->tags;
        bool modified = false;
        char *tmp_name;
+       const char *arg;
        int output_fd = -1, ret;
        struct stat sb;
 
-       if (tag_needs_update(conf.year_given, tags->year, conf.year_arg)) {
+       arg = OPT_STRING_VAL(YEAR);
+       if (tag_needs_update(OPT_GIVEN(YEAR), tags->year, arg)) {
                free(tags->year);
-               tags->year = para_strdup(conf.year_arg);
+               tags->year = para_strdup(arg);
                modified = true;
        }
-       if (tag_needs_update(conf.title_given, tags->title, conf.title_arg)) {
+       arg = OPT_STRING_VAL(TITLE);
+       if (tag_needs_update(OPT_GIVEN(TITLE), tags->title, arg)) {
                free(tags->title);
-               tags->title = para_strdup(conf.title_arg);
+               tags->title = para_strdup(arg);
                modified = true;
        }
-       if (tag_needs_update(conf.artist_given, tags->artist,
-                       conf.artist_arg)) {
+       arg = OPT_STRING_VAL(ARTIST);
+       if (tag_needs_update(OPT_GIVEN(ARTIST), tags->artist, arg)) {
                free(tags->artist);
-               tags->artist = para_strdup(conf.artist_arg);
+               tags->artist = para_strdup(arg);
                modified = true;
        }
-       if (tag_needs_update(conf.album_given, tags->album, conf.album_arg)) {
+       arg = OPT_STRING_VAL(ALBUM);
+       if (tag_needs_update(OPT_GIVEN(ALBUM), tags->album, arg)) {
                free(tags->album);
-               tags->album = para_strdup(conf.album_arg);
+               tags->album = para_strdup(arg);
                modified = true;
        }
-       if (tag_needs_update(conf.comment_given, tags->comment,
-                       conf.comment_arg)) {
+       arg = OPT_STRING_VAL(COMMENT);
+       if (tag_needs_update(OPT_GIVEN(COMMENT), tags->comment, arg)) {
                free(tags->comment);
-               tags->comment = para_strdup(conf.comment_arg);
+               tags->comment = para_strdup(arg);
                modified = true;
        }
        if (!modified) {
@@ -99,7 +109,7 @@ static int rewrite_tags(const char *name, int input_fd, void *map,
                tmp_name);
        if (ret < 0)
                goto out;
-       if (conf.backup_given) {
+       if (OPT_GIVEN(BACKUP)) {
                char *backup_name = make_message("%s~", name);
                ret = xrename(name, backup_name);
                free(backup_name);
@@ -125,39 +135,53 @@ static void print_info(int audio_format_num, struct afh_info *afhi)
        free(msg);
 }
 
-static void print_chunk_table(struct afh_info *afhi)
+static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
+               const void *map, size_t mapsize)
 {
-       int i;
+       int i, ret;
+       void *ctx = NULL;
 
-       if (conf.parser_friendly_given) {
-               printf("chunk_table: ");
-               for (i = 0; i <= afhi->chunks_total; i++)
-                       printf("%u ", afhi->chunk_table[i]);
-               printf("\n");
-               return;
-       }
-       for (i = 1; i <= afhi->chunks_total; i++) {
+       for (i = 0; i < afhi->chunks_total; i++) {
                struct timeval tv;
                long unsigned from, to;
-               tv_scale(i - 1, &afhi->chunk_tv, &tv);
-               from = tv2ms(&tv);
+               const char *buf;
+               size_t len;
                tv_scale(i, &afhi->chunk_tv, &tv);
+               from = tv2ms(&tv);
+               tv_scale(i + 1, &afhi->chunk_tv, &tv);
                to = tv2ms(&tv);
-               printf("%d [%lu.%03lu - %lu.%03lu] %u - %u (%u)\n", i - 1,
-                       from / 1000, from % 1000, to / 1000, to % 1000,
-                       afhi->chunk_table[i - 1], afhi->chunk_table[i],
-                       afhi->chunk_table[i] - afhi->chunk_table[i - 1]);
+               ret = afh_get_chunk(i, afhi, audio_format_id, map, mapsize,
+                       &buf, &len, &ctx);
+               if (ret < 0) {
+                       PARA_ERROR_LOG("fatal: chunk %d: %s\n", i,
+                               para_strerror(-ret));
+                       return;
+               }
+               if (!OPT_GIVEN(PARSER_FRIENDLY))
+                       printf("%d [%lu.%03lu - %lu.%03lu] ", i, from / 1000,
+                               from % 1000, to / 1000, to % 1000);
+               printf("%td - %td", buf - (const char *)map,
+                       buf + len - (const char *)map);
+               if (!OPT_GIVEN(PARSER_FRIENDLY))
+                       printf(" (%zu)", len);
+               printf("\n");
        }
+       afh_close(ctx, audio_format_id);
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(afh);
-       int d = conf.detailed_help_given;
-       unsigned flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+       char *help;
 
-       ggo_print_help(&h, flags);
-       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP) || lls_num_inputs(lpr) == 0)
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s", help);
+       free(help);
+       printf("Supported audio formats\n  %s\n", AUDIO_FORMAT_HANDLERS);
        exit(EXIT_SUCCESS);
 }
 
@@ -175,34 +199,37 @@ int main(int argc, char **argv)
        void *audio_file_data;
        size_t audio_file_size;
        struct afh_info afhi;
+       char *errctx;
 
-       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 || conf.inputs_num == 0)
-               print_help_and_die();
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("afh", OPT_GIVEN(VERSION));
+       handle_help_flags();
        afh_init();
-       for (i = 0; i < conf.inputs_num; i++) {
+       for (i = 0; i < lls_num_inputs(lpr); i++) {
                int ret2;
-               ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
+               const char *path = lls_input(i, lpr);
+               ret = mmap_full_file(path, O_RDONLY, &audio_file_data,
                        &audio_file_size, &fd);
                if (ret < 0) {
-                       PARA_ERROR_LOG("failed to mmap \"%s\"\n", conf.inputs[i]);
+                       PARA_ERROR_LOG("failed to mmap \"%s\"\n", path);
                        goto out;
                }
-               ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
+               ret = compute_afhi(path, audio_file_data, audio_file_size,
                        fd, &afhi);
                if (ret >= 0) {
                        audio_format_num = ret;
-                       if (conf.modify_given) {
-                               ret = rewrite_tags(conf.inputs[i], fd, audio_file_data,
+                       if (OPT_GIVEN(MODIFY)) {
+                               ret = rewrite_tags(path, fd, audio_file_data,
                                        audio_file_size, audio_format_num, &afhi);
                        } else {
-                               printf("File %d: %s\n", i + 1, conf.inputs[i]);
+                               printf("File %d: %s\n", i + 1, path);
                                print_info(audio_format_num, &afhi);
-                               if (conf.chunk_table_given)
-                                       print_chunk_table(&afhi);
-                               printf("\n");
+                               if (OPT_GIVEN(CHUNK_TABLE))
+                                       print_chunk_table(&afhi, audio_format_num,
+                                               audio_file_data, audio_file_size);
                        }
                        clear_afhi(&afhi);
                }
@@ -214,7 +241,9 @@ int main(int argc, char **argv)
                        break;
        }
 out:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
        if (ret < 0)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/afh.h b/afh.h
index a6f9c50073bd39563d05a76f38c5377b10451b16..20c61dbac61ab689798c27aff92f479556a04c3f 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -40,6 +40,8 @@ struct afh_info {
         * the current audio file.
         */
        uint32_t *chunk_table;
+       /** Size of the largest chunk, introduced in v0.6.0. */
+       uint32_t max_chunk_size;
        /** Period of time between sending data chunks. */
        struct timeval chunk_tv;
        /**
@@ -64,7 +66,10 @@ struct audio_file_data {
        int fd;
        /** Vss needs this for streaming. */
        struct afh_info afhi;
-       /** Size of the largest chunk. */
+       /**
+        * Size of the largest chunk. Superseded by afhi->max_chunk_size. May
+        * be removed after v0.6.1.
+        */
        uint32_t max_chunk_size;
        /** Needed to get the audio file header. */
        uint8_t audio_format_id;
@@ -98,12 +103,32 @@ struct audio_format_handler {
         * success, the function must return a positive value and fill in the
         * given struct afh_info.
         *
-        * \sa struct afh_info
+        * \sa struct \ref afh_info.
         */
        int (*get_file_info)(char *map, size_t numbytes, int fd,
-               struct afh_info *afi);
+               struct afh_info *afhi);
        /** Optional, used for header-rewriting. See \ref afh_get_header(). */
        void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
+       /**
+        * An audio format handler may signify support for dynamic chunks by
+        * defining ->get_chunk below. In this case the vss calls ->open() at
+        * BOS, ->get_chunk() for each chunk while streaming, and ->close() at
+        * EOS. The chunk table is not accessed at all.
+        *
+        * The function may return its (opaque) context through the last
+        * argument. The returned pointer is passed to subsequent calls to
+        * ->get_chunk() and ->close().
+        */
+       int (*open)(const void *map, size_t mapsize, void **afh_context);
+       /**
+        * Return a reference to one chunk. The returned pointer points to a
+        * portion of the memory mapped audio file. The caller must not call
+        * free() on it.
+        */
+       int (*get_chunk)(long unsigned chunk_num, void *afh_context,
+               const char **buf, size_t *len);
+       /** Deallocate the resources occupied by ->open(). */
+       void (*close)(void *afh_context);
        /**
         * Write audio file with altered tags, optional.
         *
@@ -119,10 +144,12 @@ int guess_audio_format(const char *name);
 int compute_afhi(const char *path, char *data, size_t size,
        int fd, struct afh_info *afhi);
 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);
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+               uint8_t audio_format_id, const void *map, size_t mapsize,
+               const char **buf, size_t *len, void **afh_context);
+void afh_close(void *afh_context, uint8_t audio_format_id);
 int32_t afh_get_start_chunk(int32_t approx_chunk_num,
-               const struct afh_info *afhi);
+               const struct afh_info *afhi, uint8_t audio_format_id);
 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);
@@ -130,3 +157,5 @@ 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);
+void set_max_chunk_size(struct afh_info *afhi);
+bool afh_supports_dynamic_chunks(int audio_format_id);
index dfbf75132f4e4372eb0b4c95bff8ef25793dc545..1614c27c6c9dd6d7588399ca164a9fd754087e83 100644 (file)
 #include "afh.h"
 
 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,
+
+/*
+ * Declaration of the audio format handler init functions.
+ *
+ * These symbols are referenced in the afl array below.
+ *
+ * Most audio format handlers depend on an external library and are not
+ * compiled in if the library is not installed. Hence it is well possible that
+ * not all of these functions are defined. It does not hurt to declare them
+ * anyway, and this avoids another set of ifdefs.
+ */
+extern afh_init_func mp3_afh_init, ogg_afh_init, aac_afh_init, wma_afh_init,
        spx_afh_init, flac_afh_init, opus_afh_init;
 
 /** The list of all status items */
@@ -28,7 +38,7 @@ const char *status_item_list[] = {STATUS_ITEM_ARRAY};
  *
  * We always define the full array of audio formats even if some audio formats
  * were not compiled in. This is because for each audio file the number of its
- * audio format is stored in the database. We don't want that numbers to become
+ * audio format is stored in the database. We don't want these numbers to become
  * stale just because the user installed a new version of paraslash that
  * supports a different set of audio formats.
  *
@@ -38,17 +48,17 @@ const char *status_item_list[] = {STATUS_ITEM_ARRAY};
 static struct audio_format_handler afl[] = {
        {
                .name = "mp3",
-               .init = mp3_init,
+               .init = mp3_afh_init,
        },
        {
                .name = "ogg",
 #if defined(HAVE_OGG) && defined(HAVE_VORBIS)
-               .init = ogg_init,
+               .init = ogg_afh_init,
 #endif
        },
        {
                .name = "aac",
-#if defined(HAVE_MP4V2)
+#if defined(HAVE_FAAD)
                .init = aac_afh_init,
 #endif
        },
@@ -88,7 +98,6 @@ static inline int next_audio_format(int format)
                if (afl[format].init)
                        return format;
        }
-
 }
 
 /** Iterate over each supported audio format. */
@@ -109,6 +118,22 @@ void afh_init(void)
        }
 }
 
+/**
+ * Tell whether an audio format handler provides chunk tables.
+ *
+ * Each audio format handler either provides a chunk table or supports dynamic
+ * chunks.
+ *
+ * \param audio_format_id Offset in the afl array.
+ *
+ * \return True if dynamic chunks are supported, false if the audio format
+ * handler provides chunk tables.
+ */
+bool afh_supports_dynamic_chunks(int audio_format_id)
+{
+       return afl[audio_format_id].get_chunk;
+}
+
 /**
  * Guess the audio format judging from filename.
  *
@@ -261,21 +286,73 @@ static inline size_t get_chunk_len(long unsigned chunk_num,
 /**
  * Get one chunk of audio data.
  *
+ * This implicitly calls the ->open method of the audio format handler at the
+ * first call.
+ *
  * \param chunk_num The number of the chunk to get.
  * \param afhi Describes the audio file.
+ * \param audio_format_id Determines the afh.
  * \param map The memory mapped audio file.
+ * \param mapsize Passed to the afh's ->open() method.
  * \param buf Result pointer.
  * \param len The length of the chunk in bytes.
+ * \param afh_context Value/result, determines whether ->open() is called.
  *
  * Upon return, \a buf will point so memory inside \a map. The returned buffer
  * must therefore not be freed by the caller.
+ *
+ * \return Standard.
  */
-void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
-               void *map, const char **buf, size_t *len)
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+               uint8_t audio_format_id, const void *map, size_t mapsize,
+               const char **buf, size_t *len, void **afh_context)
 {
-       size_t pos = afhi->chunk_table[chunk_num];
-       *buf = map + pos;
-       *len = get_chunk_len(chunk_num, afhi);
+       struct audio_format_handler *afh = afl + audio_format_id;
+
+       if (afh_supports_dynamic_chunks(audio_format_id)) {
+               int ret;
+
+               if (!*afh_context) {
+                       ret = afh->open(map, mapsize, afh_context);
+                       if (ret < 0)
+                               return ret;
+               }
+               ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context,
+                       buf, len);
+               if (ret < 0) {
+                       afh->close(*afh_context);
+                       *afh_context = NULL;
+               }
+               return ret;
+       } else {
+               size_t pos = afhi->chunk_table[chunk_num];
+               *buf = map + pos;
+               *len = get_chunk_len(chunk_num, afhi);
+               return 0;
+       }
+}
+
+/**
+ * Deallocate resources allocated due to dynamic chunk handling.
+ *
+ * This function should be called if afh_get_chunk() was called at least once.
+ * It is OK to call it even for audio formats which do not support dynamic
+ * chunks, in which case the function does nothing.
+ *
+ * \param afh_context As returned from the ->open method of the afh.
+ * \param audio_format_id Determines the afh.
+ */
+void afh_close(void *afh_context, uint8_t audio_format_id)
+{
+       struct audio_format_handler *afh = afl + audio_format_id;
+
+       if (!afh_supports_dynamic_chunks(audio_format_id))
+               return;
+       if (!afh->close)
+               return;
+       if (!afh_context)
+               return;
+       afh->close(afh_context);
 }
 
 /**
@@ -283,16 +360,22 @@ void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
  *
  * \param approx_chunk_num Upper bound for the chunk number to return.
  * \param afhi Needed for the chunk table.
+ * \param audio_format_id Determines the afh.
  *
- * \return The first non-empty chunk <= \a approx_chunk_num.
+ * \return For audio format handlers which support dynamic chunks, the function
+ * returns the given chunk number. Otherwise it returns 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)
+               const struct afh_info *afhi, uint8_t audio_format_id)
 {
        int32_t k;
 
+       if (afh_supports_dynamic_chunks(audio_format_id))
+               return approx_chunk_num;
+
        for (k = PARA_MAX(0, approx_chunk_num); k >= 0; k--)
                if (get_chunk_len(k, afhi) > 0)
                        return k;
@@ -374,6 +457,7 @@ unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **re
                "%s: %" PRIu32 "\n" /* seconds total */
                "%s: %lu: %lu\n" /* chunk time */
                "%s: %" PRIu32 "\n" /* num chunks */
+               "%s: %" PRIu32 "\n" /* max chunk size */
                "%s: %s\n" /* techinfo */
                "%s: %s\n" /* artist */
                "%s: %s\n" /* title */
@@ -388,6 +472,7 @@ unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **re
                status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
                        (long unsigned)afhi->chunk_tv.tv_usec,
                status_item_list[SI_NUM_CHUNKS], afhi->chunks_total,
+               status_item_list[SI_MAX_CHUNK_SIZE], afhi->max_chunk_size,
                status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "",
                status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "",
                status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "",
@@ -397,6 +482,37 @@ unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **re
        );
 }
 
+/**
+ * Determine the maximal chunk size by investigating the chunk table.
+ *
+ * \param afhi Value/result.
+ *
+ * This function iterates over the chunk table and sets ->max_chunk_size
+ * accordingly. The function exists only for backward compatibility since as of
+ * version 0.6.0, para_server stores the maximal chunk size in its database.
+ * This function is only called if the database value is zero, indicating that
+ * the file was added by an older server version.
+ */
+void set_max_chunk_size(struct afh_info *afhi)
+{
+       uint32_t n, max = 0, old = 0;
+
+       for (n = 0; n <= afhi->chunks_total; n++) {
+               uint32_t val = afhi->chunk_table[n];
+               /*
+                * If the first chunk is the header, do not consider it for the
+                * calculation of the largest chunk size.
+                */
+               if (n == 0 || (n == 1 && afhi->header_len > 0)) {
+                       old = val;
+                       continue;
+               }
+               max = PARA_MAX(max, val - old);
+               old = val;
+       }
+       afhi->max_chunk_size = max;
+}
+
 /**
  * Create a copy of the given file with altered meta tags.
  *
index 28d8f3980f814763cfe249e5762203ff5038a9a5..d2d8b52bb86d18d70d356fdcb43044b064590de6 100644 (file)
@@ -8,15 +8,15 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "afh_recv.cmdline.h"
 #include "string.h"
 #include "fd.h"
 #include "afh.h"
@@ -31,6 +31,7 @@ struct private_afh_recv_data {
        long unsigned last_chunk;
        struct timeval stream_start;
        uint32_t current_chunk;
+       void *afh_context;
 };
 
 static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
@@ -58,47 +59,33 @@ 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 = afh_get_start_chunk(x, &pard->afhi);
+               pard->first_chunk = afh_get_start_chunk(x, &pard->afhi,
+                       pard->audio_format_num);
                pard->current_chunk = pard->first_chunk;
                return 1;
        }
        return -E_BTR_NAVAIL;
 }
 
-static void *afh_recv_parse_config(int argc, char **argv)
-{
-       struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
-       afh_recv_cmdline_parser(argc, argv, tmp);
-       return tmp;
-}
-
-static void afh_recv_free_config(void *conf)
-{
-       if (!conf)
-               return;
-       afh_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
 static int afh_recv_open(struct receiver_node *rn)
 {
-       struct afh_recv_args_info *conf = rn->conf;
+       struct lls_parse_result *lpr = rn->lpr;
        struct private_afh_recv_data *pard;
        struct afh_info *afhi;
-       char *filename = conf->filename_arg;
-
+       const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
+       int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
+       const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
        int ret;
 
-       if (!filename || *filename == '\0')
+       if (!fn || *fn == '\0')
                return -E_AFH_RECV_BAD_FILENAME;
        rn->private_data = pard = para_calloc(sizeof(*pard));
        afhi = &pard->afhi;
-       ret = mmap_full_file(filename, O_RDONLY, &pard->map,
+       ret = mmap_full_file(fn, O_RDONLY, &pard->map,
                &pard->map_size, &pard->fd);
        if (ret < 0)
                goto out;
-       ret = compute_afhi(filename, pard->map, pard->map_size,
+       ret = compute_afhi(fn, pard->map, pard->map_size,
                pard->fd, afhi);
        if (ret < 0)
                goto out_unmap;
@@ -106,23 +93,23 @@ static int afh_recv_open(struct receiver_node *rn)
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
        if (afhi->chunks_total == 0)
                goto out_clear_afhi;
-       if (PARA_ABS(conf->begin_chunk_arg) >= afhi->chunks_total)
+       if (PARA_ABS(bc) >= afhi->chunks_total)
                goto out_clear_afhi;
-       if (conf->begin_chunk_arg >= 0)
-               pard->first_chunk = afh_get_start_chunk(
-                       conf->begin_chunk_arg, &pard->afhi);
+       if (bc >= 0)
+               pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi,
+                       pard->audio_format_num);
        else
-               pard->first_chunk = afh_get_start_chunk(
-                       afhi->chunks_total + conf->begin_chunk_arg,
-                       &pard->afhi);
-       if (conf->end_chunk_given) {
+               pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
+                       &pard->afhi, pard->audio_format_num);
+       if (lls_opt_given(r_e)) {
+               int32_t ec = lls_int32_val(0, r_e);
                ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-               if (PARA_ABS(conf->end_chunk_arg) > afhi->chunks_total)
+               if (PARA_ABS(ec) > afhi->chunks_total)
                        goto out_clear_afhi;
-               if (conf->end_chunk_arg >= 0)
-                       pard->last_chunk = conf->end_chunk_arg;
+               if (ec >= 0)
+                       pard->last_chunk = ec;
                else
-                       pard->last_chunk = afhi->chunks_total + conf->end_chunk_arg;
+                       pard->last_chunk = afhi->chunks_total + ec;
        } else
                pard->last_chunk = afhi->chunks_total - 1;
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
@@ -150,6 +137,7 @@ static void afh_recv_close(struct receiver_node *rn)
        clear_afhi(&pard->afhi);
        para_munmap(pard->map, pard->map_size);
        close(pard->fd);
+       afh_close(pard->afh_context, pard->audio_format_num);
        freep(&rn->private_data);
 }
 
@@ -158,13 +146,14 @@ static void afh_recv_pre_select(struct sched *s, void *context)
        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 lls_parse_result *lpr = rn->lpr;
        struct timeval chunk_time;
        int state = generic_recv_pre_select(s, rn);
+       unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
 
        if (state <= 0)
                return;
-       if (!conf->just_in_time_given) {
+       if (!j_given) {
                sched_min_delay(s);
                return;
        }
@@ -176,20 +165,22 @@ static void afh_recv_pre_select(struct sched *s, void *context)
 static int afh_recv_post_select(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
-       struct afh_recv_args_info *conf = rn->conf;
+       struct lls_parse_result *lpr = rn->lpr;
        struct private_afh_recv_data *pard = rn->private_data;
        struct btr_node *btrn = rn->btrn;
        struct afh_info *afhi = &pard->afhi;
        int ret;
        char *buf;
-       const char *start, *end;
+       const char *start;
        size_t size;
        struct timeval chunk_time;
+       unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
+       unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
 
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
        if (ret <= 0)
                goto out;
-       if (pard->first_chunk > 0 && !conf->no_header_given) {
+       if (pard->first_chunk > 0 && !H_given) {
                char *header;
                afh_get_header(afhi, pard->audio_format_num, pard->map,
                        pard->map_size, &header, &size);
@@ -201,12 +192,17 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                        afh_free_header(header, pard->audio_format_num);
                }
        }
-       if (!conf->just_in_time_given) {
-               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 %td bytes\n", end - start);
-               btr_add_output_dont_free(start, end - start, btrn);
+       if (!j_given) {
+               long unsigned n;
+               for (n = pard->first_chunk; n < pard->last_chunk; n++) {
+                       ret = afh_get_chunk(n, afhi, pard->audio_format_num,
+                               pard->map, pard->map_size, &start, &size,
+                               &pard->afh_context);
+                       if (ret < 0)
+                               goto out;
+                       PARA_INFO_LOG("adding %zu bytes\n", size);
+                       btr_add_output_dont_free(start, size, btrn);
+               }
                ret = -E_RECV_EOF;
                goto out;
        }
@@ -219,7 +215,12 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                if (ret > 0)
                        goto out;
        }
-       afh_get_chunk(pard->current_chunk, afhi, pard->map, &start, &size);
+       ret = afh_get_chunk(pard->current_chunk, afhi,
+               pard->audio_format_num, pard->map,
+               pard->map_size, &start, &size,
+               &pard->afh_context);
+       if (ret < 0)
+               goto out;
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
        btr_add_output_dont_free(start, size, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
@@ -236,26 +237,12 @@ out:
        return ret;
 }
 
-/**
- * The init function of the afh receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void afh_recv_init(struct receiver *r)
-{
-       struct afh_recv_args_info dummy;
-
-       afh_init();
-       afh_recv_cmdline_parser_init(&dummy);
-       r->open = afh_recv_open;
-       r->close = afh_recv_close;
-       r->pre_select = afh_recv_pre_select;
-       r->post_select = afh_recv_post_select;
-       r->parse_config = afh_recv_parse_config;
-       r->free_config = afh_recv_free_config;
-       r->execute = afh_execute;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv);
-       afh_recv_cmdline_parser_free(&dummy);
-}
+/** See \ref recv_init(). */
+const struct receiver lsg_recv_cmd_com_afh_user_data = {
+       .init = afh_init,
+       .open = afh_recv_open,
+       .close = afh_recv_close,
+       .pre_select = afh_recv_pre_select,
+       .post_select = afh_recv_post_select,
+       .execute = afh_execute,
+};
diff --git a/afs.c b/afs.c
index 0946b6df3b0766f82f95f0cd100f039753543350..59595567fcd6c7b4f7c66c49bfb2e3183cd73283 100644 (file)
--- a/afs.c
+++ b/afs.c
 #include <signal.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <lopsub.h>
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
-#include "server.cmdline.h"
+#include "server.lsg.h"
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
 #include "sideband.h"
 #include "command.h"
 
-/** The osl tables used by afs. \sa blob.c. */
+/** The osl tables used by afs. \sa \ref blob.c. */
 enum afs_table_num {
-       /** Contains audio file information. See aft.c. */
+       /** Contains audio file information. See \ref aft.c. */
        TBLNUM_AUDIO_FILES,
-       /** The table for the paraslash attributes. See attribute.c. */
+       /** The table for the paraslash attributes. See \ref attribute.c. */
        TBLNUM_ATTRIBUTES,
        /**
         * Paraslash's scoring system is based on Gaussian normal
         * distributions, and the relevant data is stored in the rbtrees of an
-        * osl table containing only volatile columns.  See score.c for
+        * osl table containing only volatile columns. See \ref score.c for
         * details.
         */
        TBLNUM_SCORES,
        /**
         * A standard blob table containing the mood definitions. For details
-        * see mood.c.
+        * see \ref mood.c.
         */
        TBLNUM_MOODS,
        /** A blob table containing lyrics on a per-song basis. */
@@ -127,7 +130,7 @@ extern uint32_t afs_socket_cookie;
  * command socket, so that the handler process can read the id, attach the
  * shared memory area and use the result.
  *
- * \sa struct callback_result.
+ * \sa \ref struct callback_result.
  */
 struct callback_query {
        /** The function to be called. */
@@ -143,7 +146,7 @@ struct callback_query {
  * into the shared memory area holding the result, mainly to let the command
  * handler know the size of the result.
  *
- * \sa struct callback_query.
+ * \sa \ref struct callback_query.
  */
 struct callback_result {
        /** The number of bytes of the result. */
@@ -198,9 +201,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
  * shmid are passed to that function as an osl object. The private_result_data
  * pointer is passed as the second argument to \a result_handler.
  *
- * \return Number of shared memory areas dispatched on success, negative on errors.
- *
- * \sa send_option_arg_callback_request(), send_standard_callback_request().
+ * \return Number of shared memory areas dispatched on success, negative on
+ * errors.
  */
 int send_callback_request(afs_callback *f, struct osl_object *query,
                callback_result_handler *result_handler,
@@ -235,7 +237,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
        *(uint32_t *)buf = afs_socket_cookie;
        *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid;
 
-       ret = connect_local_socket(conf.afs_socket_arg);
+       ret = connect_local_socket(OPT_STRING_VAL(AFS_SOCKET));
        if (ret < 0)
                goto out;
        fd = ret;
@@ -280,73 +282,33 @@ out:
 }
 
 /**
- * Send a callback request passing an options structure and an argument vector.
+ * Wrapper for send_callback_request() which passes a lopsub parse result.
  *
- * \param options pointer to an arbitrary data structure.
- * \param argc Argument count.
- * \param argv Standard argument vector.
  * \param f The callback function.
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
- *
- * 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.
+ * \param cmd Needed for (de-)serialization.
+ * \param lpr Must match cmd.
+ * \param private_result_data Passed to send_callback_request().
  *
- * \return The return value of the underlying call to \ref
- * send_callback_request().
+ * This function serializes the parse result given by the lpr pointer into a
+ * buffer. The buffer is sent as the query to the afs process with the callback
+ * mechanism.
  *
- * \sa send_standard_callback_request(), send_callback_request().
+ * \return The return value of the underlying call to send_callback_request().
  */
-int send_option_arg_callback_request(struct osl_object *options,
-               int argc,  char * const * const argv, afs_callback *f,
-               callback_result_handler *result_handler,
-               void *private_result_data)
+int send_lls_callback_request(afs_callback *f,
+               const struct lls_command * const cmd,
+               struct lls_parse_result *lpr, void *private_result_data)
 {
-       char *p;
-       int i, ret;
-       struct osl_object query = {.size = options? options->size : 0};
-
-       for (i = 0; i < argc; i++)
-               query.size += strlen(argv[i]) + 1;
-       query.data = para_malloc(query.size);
-       p = query.data;
-       if (options) {
-               memcpy(query.data, options->data, options->size);
-               p += options->size;
-       }
-       for (i = 0; i < argc; i++) {
-               strcpy(p, argv[i]); /* OK */
-               p += strlen(argv[i]) + 1;
-       }
-       ret = send_callback_request(f, &query, result_handler,
-               private_result_data);
-       free(query.data);
-       return ret;
-}
+       struct osl_object query;
+       char *buf = NULL;
+       int ret = lls_serialize_parse_result(lpr, cmd, &buf, &query.size);
 
-/**
- * Send a callback request with an argument vector only.
- *
- * \param argc The same meaning as in send_option_arg_callback_request().
- * \param argv The same meaning as in send_option_arg_callback_request().
- * \param f The same meaning as in send_option_arg_callback_request().
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
- *
- * This is similar to send_option_arg_callback_request(), but no options buffer
- * is passed to the parent process.
- *
- * \return The return value of the underlying call to
- * send_option_arg_callback_request().
- */
-int send_standard_callback_request(int argc,  char * const * const argv,
-               afs_callback *f, callback_result_handler *result_handler,
-               void *private_result_data)
-{
-       return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
+       assert(ret >= 0);
+       query.data = buf;
+       ret = send_callback_request(f, &query, afs_cb_result_handler,
                private_result_data);
+       free(buf);
+       return ret;
 }
 
 static int action_if_pattern_matches(struct osl_row *row, void *data)
@@ -354,30 +316,37 @@ static int action_if_pattern_matches(struct osl_row *row, void *data)
        struct pattern_match_data *pmd = data;
        struct osl_object name_obj;
        const char *p, *name;
-       int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj));
-       const char *pattern_txt = (const char *)pmd->patterns.data;
+       int i, ret;
 
+       ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num,
+               &name_obj));
        if (ret < 0)
                return ret;
        name = (char *)name_obj.data;
        if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
                return 1;
-       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);
+       if (lls_num_inputs(pmd->lpr) == 0) {
+               if (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) {
+       i = pmd->input_skip;
+       for (;;) {
+               if (i >= lls_num_inputs(pmd->lpr))
+                       break;
+               p = lls_input(i, pmd->lpr);
                ret = fnmatch(p, name, pmd->fnmatch_flags);
-               if (ret == FNM_NOMATCH)
-                       continue;
-               if (ret)
-                       return -E_FNMATCH;
-               ret = pmd->action(pmd->table, row, name, pmd->data);
-               if (ret >= 0)
-                       pmd->num_matches++;
-               return ret;
+               if (ret != FNM_NOMATCH) {
+                       if (ret != 0)
+                               return -E_FNMATCH;
+                       ret = pmd->action(pmd->table, row, name, pmd->data);
+                       if (ret >= 0)
+                               pmd->num_matches++;
+                       return ret;
+
+               }
+               i++;
        }
        return 1;
 }
@@ -411,7 +380,7 @@ int for_each_matching_row(struct pattern_match_data *pmd)
  * \a obj1 is found, respectively, to be less than, to match, or be greater than
  * obj2.
  *
- * \sa strcmp(3), strncmp(3), osl_compare_func.
+ * \sa strcmp(3), strncmp(3).
  */
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 {
@@ -463,7 +432,7 @@ static int pass_afd(int fd, char *buf, size_t size)
  *
  * \return Standard.
  *
- * \sa open_and_update_audio_file().
+ * \sa \ref open_and_update_audio_file().
  */
 static int open_next_audio_file(void)
 {
@@ -590,14 +559,17 @@ static void flush_and_free_pb(struct para_buffer *pb)
 
 static int com_select_callback(struct afs_callback_arg *aca)
 {
-       const char *arg = aca->query.data;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       const char *arg;
        int num_admissible, ret;
 
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       arg = lls_input(0, aca->lpr);
        ret = clear_score_table();
        if (ret < 0) {
-               para_printf(&aca->pbout, "could not clear score table: %s\n",
-                       para_strerror(-ret));
-               return ret;
+               para_printf(&aca->pbout, "could not clear score table\n");
+               goto free_lpr;
        }
        if (current_play_mode == PLAY_MODE_MOOD)
                close_current_mood();
@@ -622,22 +594,26 @@ static int com_select_callback(struct afs_callback_arg *aca)
 out:
        para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
                current_mop? current_mop : "dummy mood", num_admissible);
+free_lpr:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_select(struct command_context *cc)
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct osl_object query;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
 
-       if (cc->argc != 2)
-               return -E_AFS_SYNTAX;
-       query.data = cc->argv[1];
-       query.size = strlen(cc->argv[1]) + 1;
-       return send_callback_request(com_select_callback, &query,
-               &afs_cb_result_handler, cc);
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(select);
 
-static void init_admissible_files(char *arg)
+static void init_admissible_files(const char *arg)
 {
        if (activate_mood_or_playlist(arg, NULL) < 0)
                activate_mood_or_playlist(NULL, NULL); /* always successful */
@@ -646,18 +622,13 @@ static void init_admissible_files(char *arg)
 static int setup_command_socket_or_die(void)
 {
        int ret, socket_fd;
-       char *socket_name = conf.afs_socket_arg;
+       const char *socket_name = OPT_STRING_VAL(AFS_SOCKET);
 
        unlink(socket_name);
-       ret = create_local_socket(socket_name, 0);
+       ret = create_local_socket(socket_name);
        if (ret < 0) {
-               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);
-               }
+               PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name);
+               exit(EXIT_FAILURE);
        }
        socket_fd = ret;
        PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name,
@@ -678,8 +649,8 @@ static char *database_dir;
 static void get_database_dir(void)
 {
        if (!database_dir) {
-               if (conf.afs_database_dir_given)
-                       database_dir = para_strdup(conf.afs_database_dir_arg);
+               if (OPT_GIVEN(AFS_DATABASE_DIR))
+                       database_dir = para_strdup(OPT_STRING_VAL(AFS_DATABASE_DIR));
                else {
                        char *home = para_homedir();
                        database_dir = make_message(
@@ -888,7 +859,7 @@ static int execute_server_command(fd_set *rfds)
                return ret;
        buf[n] = '\0';
        if (strcmp(buf, "new"))
-               return -E_BAD_CMD;
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        return open_next_audio_file();
 }
 
@@ -1020,7 +991,7 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
                goto out_close;
        PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
                server_socket, (unsigned) cookie);
-       init_admissible_files(conf.afs_initial_mode_arg);
+       init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
        register_command_task(cookie, &s);
        s.default_timeout.tv_sec = 0;
        s.default_timeout.tv_usec = 999 * 1000;
@@ -1070,23 +1041,24 @@ out:
        return ret;
 }
 
-int com_init(struct command_context *cc)
+static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, j, ret;
        uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1;
        struct osl_object query = {.data = &table_mask,
                .size = sizeof(table_mask)};
+       unsigned num_inputs = lls_num_inputs(lpr);
 
        ret = make_database_dir();
        if (ret < 0)
                return ret;
-       if (cc->argc != 1) {
+       if (num_inputs > 0) {
                table_mask = 0;
-               for (i = 1; i < cc->argc; i++) {
+               for (i = 0; i < num_inputs; i++) {
                        for (j = 0; j < NUM_AFS_TABLES; j++) {
                                struct afs_table *t = &afs_tables[j];
 
-                               if (strcmp(cc->argv[i], t->name))
+                               if (strcmp(lls_input(i, lpr), t->name))
                                        continue;
                                table_mask |= (1 << j);
                                break;
@@ -1098,77 +1070,37 @@ int com_init(struct command_context *cc)
        return send_callback_request(com_init_callback, &query,
                afs_cb_result_handler, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(init);
 
-/**
- * Flags for the check command.
- *
- * \sa com_check().
- */
-enum com_check_flags {
-       /** Check the audio file table. */
-       CHECK_AFT = 1,
-       /** Check the mood table. */
-       CHECK_MOODS = 2,
-       /** Check the playlist table. */
-       CHECK_PLAYLISTS = 4,
-       /** Check the attribute table against the audio file table. */
-       CHECK_ATTS = 8
-};
-
-int com_check(struct command_context *cc)
+static int com_check(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       unsigned flags = 0;
-       int i, ret;
+       const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(CHECK, AFT, lpr);
+       const struct lls_opt_result *r_A = SERVER_CMD_OPT_RESULT(CHECK, ATTRIBUTE, lpr);
+       const struct lls_opt_result *r_m = SERVER_CMD_OPT_RESULT(CHECK, MOOD, lpr);
+       const struct lls_opt_result *r_p = SERVER_CMD_OPT_RESULT(CHECK, PLAYLIST, lpr);
+       bool noopt = !lls_opt_given(r_a) && !lls_opt_given(r_A)
+               && !lls_opt_given(r_m) && !lls_opt_given(r_p);
+       int ret;
 
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-a")) {
-                       flags |= CHECK_AFT;
-                       continue;
-               }
-               if (!strcmp(arg, "-A")) {
-                       flags |= CHECK_ATTS;
-                       continue;
-               }
-               if (!strcmp(arg, "-p")) {
-                       flags |= CHECK_PLAYLISTS;
-                       continue;
-               }
-               if (!strcmp(arg, "-m")) {
-                       flags |= CHECK_MOODS;
-                       continue;
-               }
-               return -E_AFS_SYNTAX;
-       }
-       if (i < cc->argc)
-               return -E_AFS_SYNTAX;
-       if (!flags)
-               flags = ~0U;
-       if (flags & CHECK_AFT) {
+       if (noopt || lls_opt_given(r_a)) {
                ret = send_callback_request(aft_check_callback, NULL,
                        afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
-       if (flags & CHECK_ATTS) {
+       if (noopt || lls_opt_given(r_A)) {
                ret = send_callback_request(attribute_check_callback, NULL,
                        afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
-       if (flags & CHECK_PLAYLISTS) {
+       if (noopt || lls_opt_given(r_p)) {
                ret = send_callback_request(playlist_check_callback,
                        NULL, afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
-       if (flags & CHECK_MOODS) {
+       if (noopt || lls_opt_given(r_m)) {
                ret = send_callback_request(mood_check_callback, NULL,
                        afs_cb_result_handler, cc);
                if (ret < 0)
@@ -1176,6 +1108,7 @@ int com_check(struct command_context *cc)
        }
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(check);
 
 /**
  * The afs event dispatcher.
diff --git a/afs.cmd b/afs.cmd
deleted file mode 100644 (file)
index 76b5f4d..0000000
--- a/afs.cmd
+++ /dev/null
@@ -1,304 +0,0 @@
-BN: afs
-SF: afs.c aft.c attribute.c
-SN: list of afs commands
-TM: mood lyr img pl
----
-N: add
-P: AFS_READ | AFS_WRITE
-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:
-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. 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:     even if a file with the same path and the same hash value exists.
-H:
-H: -v  Verbose mode. Print what is being done.
----
-N: init
-P: AFS_READ | AFS_WRITE
-D: Initialize the osl tables for the audio file selector.
-U: init [table_name ...]
-H: When invoked without arguments, this command creates all tables. Otherwise
-H: only the tables given by table_name... are created.
----
-N: ls
-P: AFS_READ
-D: List audio files.
-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=mode     Change listing mode. Defaults to short listing if not given.
-H:
-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: -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.
-H:
-H: -r  Reverse sort order.
-H:
-H: -d  Print dates as seconds after the epoch.
-H:
-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 given
-H: pattern. If no pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i  Sort attributes by id. The default is to sort alphabetically by name.
-H:
-H: -l  Print a long listing containing both identifier and attribute name. The
-H:     default is to print only the name.
-H:
-H: -r  Reverse sort order.
----
-N: setatt
-P: AFS_READ | AFS_WRITE
-D: Set attribute(s) for all files matching a pattern.
-U: setatt attribute{+|-}... pattern
-H: Set ('+') or unset ('-') the given attributes for all audio files matching
-H: pattern.  Example:
-H:
-H:         setatt rock+ punk+ pop- '*foo.mp3'
-H:
-H: sets the 'rock' and the 'punk' attribute and unsets the 'pop'
-H: attribute of all files ending with 'foo.mp3'.
----
-N: addatt
-P: AFS_READ | AFS_WRITE
-D: Add new attribute(s).
-U: addatt attribute1...
-H: This adds new attributes to the attribute table. At most 64
-H: attributes may be defined.
----
-N: mvatt
-P: AFS_READ | AFS_WRITE
-D: Rename an attribute.
-U: mvatt old new
-H: Rename attribute old to new.
----
-N: check
-P: AFS_READ
-D: Run integrity checks against osl tables.
-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:
-H: -a  Run audio file table checks. Checks for entries in the audio file
-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 checks are run.
----
-N: rmatt
-P: AFS_READ | AFS_WRITE
-D: Remove attribute(s).
-U: rmatt pattern...
-H: Remove all attributes matching any given pattern. All information
-H: about this attribute in the audio file table is lost.
----
-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.  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.
-H:
-H: -f  Force mode. Ignore nonexistent files. Don't complain if nothing
-H:     was removed.
-H:
-H: -p  Pathname match. Match a slash in the path only with a slash
-H:     in pattern and not by an asterisk (*) or a question mark
-H:     (?) metacharacter, nor by a bracket expression ([]) containing
-H:     a slash (see fnmatch(3)).
----
-N: touch
-P: AFS_READ | AFS_WRITE
-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, 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 the numplayed count, i.e. the number of times this audio
-H:     file was selected for streaming so far.
-H:
-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 which specifies the lyrics data file associated
-H:     with the audio file.
-H:
-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
-H:     normalize the volume of the audio file.  A value of zero means
-H:     no amplification, 64 means the amplitude should be multiplied
-H:     by a factor of two, 128 by three and so on.
-H:
-H:     This value is used by the amp filter.
-H:
-H: -v  Verbose mode. Explain what is being done.
-H:
-H: -p  Pathname match. Match a slash in the path only with a slash
-H:     in pattern and not by an asterisk (*) or a question mark
-H:     (?) metacharacter, nor by a bracket expression ([]) containing
-H:     a slash (see fnmatch(3)).
----
-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 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:
-H: -a  Copy attributes.
-H:
-H: -y  Copy the lyrics id.
-H:
-H: -i  Copy the image id.
-H:
-H: -l  Copy the lastplayed time.
-H:
-H: -n  Copy the numplayed count.
-H:
-H: -v  Verbose mode.
----
-N: select
-P: AFS_READ | AFS_WRITE
-D: Activate a mood or a playlist.
-U: select specifier/name
-H: The specifier is either 'm' or 'p' to indicate whether a playlist or
-H: a mood should be activated. Example:
-H:
-H:     select m/foo
-H:
-H: loads the mood named 'foo'.
----
-T: add
-N: add@member@
-O: int com_add@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Add stdin as a blob to the @member@ table.
-U: add@member@ @member@_name
-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@
-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: Retrieve the named blob and write it to stdout.
----
-T: ls
-N: ls@member@
-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 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:
-H: -i  Sort by identifier. The default is to sort alphabetically by name.
-H:
-H: -l  Print identifier and name. The default is to print only the name.
-H:
-H: -r  Reverse sort order.
----
-T: rm
-N: rm@member@
-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 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@ 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 2f9d7e5fc40bdc9c1661336ffce20d7a7df76148..ea4b497e4b3b1bc2e54ab202962638ab25598d4d 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -141,12 +141,14 @@ struct pattern_match_data {
        unsigned loop_col_num;
        /** Data from this column is matched against the given patterns. */
        unsigned match_col_num;
-       /** \see pattern_match_flags. */
+       /** \see \ref pattern_match_flags. */
        unsigned pm_flags;
        /** This value is passed verbatim to fnmatch(). */
        int fnmatch_flags;
-       /** Null-terminated array of patterns. */
-       struct osl_object patterns;
+       /** Obtained by deserializing the query buffer in the callback. */
+       struct lls_parse_result *lpr;
+       /** Do not try to match the first inputs of lpr */
+       unsigned input_skip;
        /** Data pointer passed to the action function. */
        void *data;
        /** Gets increased by one for each match. */
@@ -163,6 +165,7 @@ struct afs_callback_arg {
        struct osl_object query;
        /** Will be written on band SBD_OUTPUT, fully buffered. */
        struct para_buffer pbout;
+       struct lls_parse_result *lpr;
 };
 
 /**
@@ -171,16 +174,14 @@ struct afs_callback_arg {
  * Therefore afs commands typically consist of two functions: The command
  * handler and the corresponding callback function that runs in afs context.
  *
- * \sa send_callback_request().
+ * \sa \ref send_callback_request().
  */
 typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
- * Callbacks send chunks to data back to the command handler. Pointers to
- * this type of function are used by \ref send_callback_request and friends
- * to deal with the data in the command handler process.
- *
- * \sa \ref send_callback_request().
+ * Some AFS callbacks need to send data back to the command handler. Pointers
+ * to this type of function are passed to \ref send_callback_request() and
+ * related functions to dispatch the data in the command handler process.
  */
 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);
@@ -221,13 +222,9 @@ __must_check int afs_event(enum afs_events event, struct para_buffer *pb,
 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, afs_callback *f,
-               callback_result_handler *result_handler,
-               void *private_result_data);
-int send_standard_callback_request(int argc,  char * const * const argv,
-               afs_callback *f, callback_result_handler *result_handler,
-               void *private_result_data);
+int send_lls_callback_request(afs_callback *f,
+               const struct lls_command * const cmd,
+               struct lls_parse_result *lpr, 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);
 
diff --git a/aft.c b/aft.c
index 1afc16bc542b3812687af3a58df97b941586d885..63a40d790f602c81d6f28e71e02b6d46d907cd5d 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -11,7 +11,9 @@
 #include <fnmatch.h>
 #include <sys/shm.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -86,18 +88,6 @@ struct ls_data {
        unsigned char *hash;
 };
 
-/** The flags accepted by the ls command. */
-enum ls_flags {
-       /** -p */
-       LS_FLAG_FULL_PATH = 1,
-       /** -a */
-       LS_FLAG_ADMISSIBLE_ONLY = 2,
-       /** -r */
-       LS_FLAG_REVERSE = 4,
-       /** -d */
-       LS_FLAG_UNIXDATE = 8,
-};
-
 /**
  * The size of the individual output fields of the ls command.
  *
@@ -128,16 +118,11 @@ struct ls_widths {
 
 /** Data passed from the ls command handler to its callback function. */
 struct ls_options {
-       /** The given command line flags. */
-       unsigned flags;
-       /** The sorting method given at the command line. */
+       struct lls_parse_result *lpr;
+       /* Derived from lpr */
        enum ls_sorting_method sorting;
-       /** The given listing mode (short, long, verbose, mbox). */
+       /* Derived from lpr */
        enum ls_listing_mode mode;
-       /** The arguments passed to the ls command. */
-       char **patterns;
-       /** Number of non-option arguments. */
-       int num_patterns;
        /** Used for long listing mode to align the output fields. */
        struct ls_widths widths;
        /** Size of the \a data array. */
@@ -153,7 +138,7 @@ struct ls_options {
 /**
  * Describes the layout of the mmapped-afs info struct.
  *
- * \sa struct afs_info.
+ * \sa struct \ref afs_info.
  */
 enum afsi_offsets {
        /** Where .last_played is stored. */
@@ -182,7 +167,7 @@ enum afsi_offsets {
  * \param afsi Pointer to the audio file info to be converted.
  * \param obj Result pointer.
  *
- * \sa load_afsi().
+ * \sa \ref load_afsi().
  */
 static void save_afsi(struct afs_info *afsi, struct osl_object *obj)
 {
@@ -207,7 +192,7 @@ static void save_afsi(struct afs_info *afsi, struct osl_object *obj)
  *
  * \return Standard.
  *
- * \sa save_afsi().
+ * \sa \ref save_afsi().
  */
 static int load_afsi(struct afs_info *afsi, struct osl_object *obj)
 {
@@ -335,8 +320,8 @@ enum afhi_offsets {
        CHUNKS_TOTAL_OFFSET = 20,
        /** The length of the audio file header (4 bytes). */
        HEADER_LEN_OFFSET = 24,
-       /** Was: The start of the audio file header (4 bytes). */
-       AFHI_UNUSED2_OFFSET = 28,
+       /** Size of the largest chunk in bytes. (4 bytes). */
+       AFHI_MAX_CHUNK_SIZE_OFFSET = 28,
        /** The seconds part of the chunk time (4 bytes). */
        CHUNK_TV_TV_SEC_OFFSET = 32,
        /** The microseconds part of the chunk time (4 bytes). */
@@ -376,11 +361,14 @@ static void save_afhi(struct afh_info *afhi, char *buf)
        write_u8(buf + AFHI_CHANNELS_OFFSET, afhi->channels);
        write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total);
        write_u32(buf + HEADER_LEN_OFFSET, afhi->header_len);
-       write_u32(buf + AFHI_UNUSED2_OFFSET, 0);
+       write_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET, afhi->max_chunk_size);
        write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec);
        write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec);
        p = buf + AFHI_INFO_STRING_OFFSET;
-       /* The sprintf's below are OK as our caller made sure that buf is large enough */
+       /*
+        * The below sprintf(3) calls are OK because our caller already made
+        * sure that buf is large enough.
+        */
        p += sprintf(p, "%s", afhi->techinfo) + 1;
        p += sprintf(p, "%s", afhi->tags.artist) + 1;
        p += sprintf(p, "%s", afhi->tags.title) + 1;
@@ -389,6 +377,7 @@ static void save_afhi(struct afh_info *afhi, char *buf)
        sprintf(p, "%s", afhi->tags.comment);
 }
 
+/* does not load the chunk table */
 static void load_afhi(const char *buf, struct afh_info *afhi)
 {
        afhi->seconds_total = read_u32(buf + AFHI_SECONDS_TOTAL_OFFSET);
@@ -398,6 +387,7 @@ static void load_afhi(const char *buf, struct afh_info *afhi)
        afhi->channels = read_u8(buf + AFHI_CHANNELS_OFFSET);
        afhi->chunks_total = read_u32(buf + CHUNKS_TOTAL_OFFSET);
        afhi->header_len = read_u32(buf + HEADER_LEN_OFFSET);
+       afhi->max_chunk_size = read_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET);
        afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
        afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
        afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET;
@@ -408,42 +398,37 @@ static void load_afhi(const char *buf, struct afh_info *afhi)
        afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1;
 }
 
+/* Only used for saving the chunk table, but not for loading. */
 static unsigned sizeof_chunk_table(struct afh_info *afhi)
 {
-       if (!afhi)
+       if (!afhi || !afhi->chunk_table)
                return 0;
        return 4 * (afhi->chunks_total + 1);
 }
 
-static uint32_t save_chunk_table(struct afh_info *afhi, char *buf)
+static void save_chunk_table(struct afh_info *afhi, char *buf)
 {
-       int i;
-       uint32_t max = 0, old = 0;
-
-       for (i = 0; i <= afhi->chunks_total; i++) {
-               uint32_t val = afhi->chunk_table[i];
-               write_u32(buf + 4 * i, val);
-               /*
-                * If the first chunk is the header, do not consider it for the
-                * calculation of the largest chunk size.
-                */
-               if (i == 0 || (i == 1 && afhi->header_len > 0)) {
-                       old = val;
-                       continue;
-               }
-               max = PARA_MAX(max, val - old);
-               old = val;
-       }
-       return max;
+       uint32_t n;
+
+       if (!afhi->chunk_table)
+               return;
+       for (n = 0; n <= afhi->chunks_total; n++)
+               write_u32(buf + 4 * n, afhi->chunk_table[n]);
 }
 
-static void load_chunk_table(struct afh_info *afhi, char *buf)
+static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
 {
        int i;
+       size_t sz;
 
-       afhi->chunk_table = para_malloc(sizeof_chunk_table(afhi));
-       for (i = 0; i <= afhi->chunks_total; i++)
-               afhi->chunk_table[i] = read_u32(buf + 4 * i);
+       if (!ct->data || ct->size < 4) {
+               afhi->chunk_table = NULL;
+               return;
+       }
+       sz  = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
+       afhi->chunk_table = para_malloc(sz);
+       for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
+               afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
 }
 
 /**
@@ -547,7 +532,7 @@ static int get_afsi_of_path(const char *path, struct afs_info *afsi)
  * \param row Pointer to a row in the audio file table.
  * \param path Result pointer.
  *
- * The result is a pointer to mmapped data. The caller must not attempt
+ * The result is a pointer to memory-mapped data. The caller must not attempt
  * to free it.
  *
  * \return Standard.
@@ -571,7 +556,7 @@ int get_audio_file_path_of_row(const struct osl_row *row, char **path)
  *
  * \return The return value of the underlying call to osl_get_object().
  *
- * \sa get_hash_of_row().
+ * \sa \ref get_hash_of_row().
  */
 static int get_hash_object_of_aft_row(const struct osl_row *row,
                struct osl_object *obj)
@@ -609,7 +594,9 @@ static int get_hash_of_row(const struct osl_row *row, unsigned char **hash)
  *
  * \return The return value of the underlying call to osl_get_object().
  *
- * \sa get_chunk_table_of_row().
+ * After the call the members of the afhi structure point to mapped memory
+ * which is owned by the osl table, Hence the caller must not attempt to free
+ * this memory by calling \ref clear_afhi().
  */
 int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi)
 {
@@ -638,7 +625,13 @@ static int save_afd(struct audio_file_data *afd)
                goto err;
        buf = shm_afd;
        buf += sizeof(*afd);
-       afd->max_chunk_size = save_chunk_table(&afd->afhi, buf);
+       save_chunk_table(&afd->afhi, buf);
+       if (afd->afhi.max_chunk_size == 0) { /* v0.5.x on-disk afhi */
+               set_max_chunk_size(&afd->afhi);
+               PARA_NOTICE_LOG("max chunk size unset, re-add required\n");
+       } else
+               PARA_INFO_LOG("using max chunk size from afhi\n");
+       afd->max_chunk_size = afd->afhi.max_chunk_size;
        *(struct audio_file_data *)shm_afd = *afd;
        shm_detach(shm_afd);
        return shmid;
@@ -663,14 +656,22 @@ int load_afd(int shmid, struct audio_file_data *afd)
 {
        void *shm_afd;
        int ret;
+       struct osl_object obj;
 
        ret = shm_attach(shmid, ATTACH_RO, &shm_afd);
        if (ret < 0)
                return ret;
+       ret = shm_size(shmid, &obj.size);
+       if (ret < 0)
+               goto detach;
        *afd = *(struct audio_file_data *)shm_afd;
-       load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd));
+       obj.data = shm_afd + sizeof(*afd);
+       obj.size -= sizeof(*afd);
+       load_chunk_table(&afd->afhi, &obj);
+       ret = 1;
+detach:
        shm_detach(shm_afd);
-       return 1;
+       return ret;
 }
 
 static int get_local_time(uint64_t *seconds, char *buf, size_t size,
@@ -699,7 +700,7 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size,
        /*
         * 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
+        * string below makes the formatted date align nicely with dates that
         * contain the time (those written by the above strftime() statement).
         */
        if (!strftime(buf, size, "%b %e  %Y", tm))
@@ -783,11 +784,11 @@ static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
 }
 
 static void write_filename_items(struct para_buffer *b, const char *path,
-               unsigned flags)
+               bool basename)
 {
        char *val;
 
-       if (!(flags & LS_FLAG_FULL_PATH)) {
+       if (basename) {
                WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
                return;
        }
@@ -820,7 +821,11 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                (long unsigned) d->afhi.chunk_tv.tv_usec
        );
        buf = chunk_table_obj.data;
-       for (i = 0; i <= d->afhi.chunks_total; i++)
+       for (
+               i = 0;
+               i <= d->afhi.chunks_total && 4 * i + 3 < chunk_table_obj.size;
+               i++
+       )
                para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
        para_printf(b, "\n");
        ret = 1;
@@ -828,17 +833,12 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
        return ret;
 }
 
-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;
-       WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
-}
-
 static int print_list_item(struct ls_data *d, struct ls_options *opts,
        struct para_buffer *b, time_t current_time)
 {
+       const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
+       const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME, opts->lpr);
+       const struct lls_opt_result *r_d = SERVER_CMD_OPT_RESULT(LS, UNIX_DATE, opts->lpr);
        int ret;
        char att_buf[65];
        char last_played_time[30];
@@ -857,7 +857,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                goto out;
        }
        get_attribute_bitmap(&afsi->attributes, att_buf);
-       if (opts->flags & LS_FLAG_UNIXDATE)
+       if (lls_opt_given(r_d))
                sprintf(last_played_time, "%llu",
                        (long long unsigned)afsi->last_played);
        else {
@@ -869,10 +869,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        get_duration_buf(afhi->seconds_total, duration_buf, opts);
        if (opts->mode == LS_MODE_LONG) {
                struct ls_widths *w = &opts->widths;
-               if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+               if (lls_opt_given(r_a))
                        para_printf(b, "%*li ", opts->widths.score_width,
                                d->score);
-               }
                para_printf(b,
                        "%s "   /* attributes */
                        "%*u "  /* amp */
@@ -912,8 +911,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        last_played_time,
                        bn? bn : "?");
        }
-       write_filename_items(b, d->path, opts->flags);
-       write_score(b, d, opts);
+       write_filename_items(b, d->path, lls_opt_given(r_b));
+       if (lls_opt_given(r_a))
+               WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
        ret = write_attribute_items(b, att_buf, afsi);
        if (ret < 0)
                goto out;
@@ -935,6 +935,8 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        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);
+       WRITE_STATUS_ITEM(b, SI_MAX_CHUNK_SIZE, "%" PRIu32 "\n",
+               afhi->max_chunk_size);
        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);
@@ -980,21 +982,23 @@ out:
 
 static int make_status_items(void)
 {
-       struct ls_options opts = {
-               .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
-               .mode = LS_MODE_VERBOSE,
-       };
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+       char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
+       struct ls_options opts = {.mode = LS_MODE_VERBOSE};
        struct para_buffer pb = {.max_size = shm_get_shmmax() - 1};
        time_t current_time;
        int ret;
 
+       ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
+       assert(ret >= 0);
        time(&current_time);
        ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
        if (ret < 0)
-               return ret;
+               goto out;
        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;
@@ -1007,7 +1011,10 @@ static int make_status_items(void)
        make_inode_status_items(&pb);
        free(parser_friendly_status_items);
        parser_friendly_status_items = pb.buf;
-       return 1;
+       ret = 1;
+out:
+       lls_free_parse_result(opts.lpr, cmd);
+       return ret;
 }
 
 /**
@@ -1054,8 +1061,15 @@ again:
        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;
+       if (ret < 0) {
+               if (!afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+                       return ret;
+               PARA_INFO_LOG("no chunk table for %s\n", d->path);
+               chunk_table_obj.data = NULL;
+               chunk_table_obj.size = 0;
+       } else {
+               PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
+       }
        ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
        if (ret < 0)
                goto out;
@@ -1072,7 +1086,7 @@ again:
        save_afsi(&new_afsi, &afsi_obj); /* in-place update */
 
        afd->audio_format_id = d->afsi.audio_format_id;
-       load_chunk_table(&afd->afhi, chunk_table_obj.data);
+       load_chunk_table(&afd->afhi, &chunk_table_obj);
        aced.aft_row = current_aft_row;
        aced.old_afsi = &d->afsi;
        /*
@@ -1085,7 +1099,8 @@ again:
        ret = save_afd(afd);
 out:
        free(afd->afhi.chunk_table);
-       osl_close_disk_object(&chunk_table_obj);
+       if (chunk_table_obj.data)
+               osl_close_disk_object(&chunk_table_obj);
        if (ret < 0) {
                PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
                ret = score_delete(current_aft_row);
@@ -1095,6 +1110,12 @@ out:
        return ret;
 }
 
+static int ls_hash_compare(const void *a, const void *b)
+{
+       struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b;
+       return memcmp(d1->hash, d2->hash, HASH_SIZE);
+}
+
 static int ls_audio_format_compare(const void *a, const void *b)
 {
        struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b;
@@ -1161,8 +1182,16 @@ static int ls_path_compare(const void *a, const void *b)
        return strcmp(d1->path, d2->path);
 }
 
+static inline bool admissible_only(struct ls_options *opts)
+{
+       return SERVER_CMD_OPT_GIVEN(LS, ADMISSIBLE, opts->lpr)
+               || opts->sorting == LS_SORT_BY_SCORE;
+}
+
 static int sort_matching_paths(struct ls_options *options)
 {
+       const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME,
+               options->lpr);
        size_t nmemb = options->num_matching_paths;
        size_t size = sizeof(*options->data_ptr);
        int (*compar)(const void *, const void *);
@@ -1173,13 +1202,13 @@ static int sort_matching_paths(struct ls_options *options)
                options->data_ptr[i] = options->data + i;
 
        /* In these cases the array is already sorted */
-       if (options->sorting == LS_SORT_BY_PATH
-               && !(options->flags & LS_FLAG_ADMISSIBLE_ONLY)
-               && (options->flags & LS_FLAG_FULL_PATH))
-               return 1;
-       if (options->sorting == LS_SORT_BY_SCORE &&
-                       options->flags & LS_FLAG_ADMISSIBLE_ONLY)
-               return 1;
+       if (admissible_only(options)) {
+               if (options->sorting == LS_SORT_BY_SCORE)
+                       return 1;
+       } else {
+               if (options->sorting == LS_SORT_BY_PATH && !lls_opt_given(r_b))
+                       return 1;
+       }
 
        switch (options->sorting) {
        case LS_SORT_BY_PATH:
@@ -1204,6 +1233,8 @@ static int sort_matching_paths(struct ls_options *options)
                compar = ls_duration_compare; break;
        case LS_SORT_BY_AUDIO_FORMAT:
                compar = ls_audio_format_compare; break;
+       case LS_SORT_BY_HASH:
+               compar = ls_hash_compare; break;
        default:
                return -E_BAD_SORT;
        }
@@ -1217,15 +1248,16 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
 {
        int ret, i;
        struct ls_options *options = ls_opts;
+       bool basename_given = SERVER_CMD_OPT_GIVEN(LS, BASENAME, options->lpr);
        struct ls_data *d;
        struct ls_widths *w;
        unsigned short num_digits;
-       unsigned tmp;
+       unsigned tmp, num_inputs;
        struct osl_row *aft_row;
        long score;
        char *path;
 
-       if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+       if (admissible_only(options)) {
                ret = get_score_and_aft_row(row, &score, &aft_row);
                if (ret < 0)
                        return ret;
@@ -1236,21 +1268,22 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        ret = get_audio_file_path_of_row(aft_row, &path);
        if (ret < 0)
                return ret;
-       if (!(options->flags & LS_FLAG_FULL_PATH)) {
+       if (basename_given) {
                char *p = strrchr(path, '/');
                if (p)
                        path = p + 1;
        }
-       if (options->num_patterns) {
-               for (i = 0; i < options->num_patterns; i++) {
-                       ret = fnmatch(options->patterns[i], path, 0);
+       num_inputs = lls_num_inputs(options->lpr);
+       if (num_inputs > 0) {
+               for (i = 0; i < num_inputs; i++) {
+                       ret = fnmatch(lls_input(i, options->lpr), path, 0);
                        if (!ret)
                                break;
                        if (ret == FNM_NOMATCH)
                                continue;
                        return -E_FNMATCH;
                }
-               if (i >= options->num_patterns) /* no match */
+               if (i >= num_inputs) /* no match */
                        return 1;
        }
        tmp = options->num_matching_paths++;
@@ -1289,7 +1322,7 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        w->amp_width = PARA_MAX(w->amp_width, num_digits);
        num_digits = strlen(audio_format_name(d->afsi.audio_format_id));
        w->audio_format_width = PARA_MAX(w->audio_format_width, num_digits);
-       if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+       if (admissible_only(options)) {
                GET_NUM_DIGITS(score, &num_digits);
                num_digits++; /* add one for the sign (space or "-") */
                w->score_width = PARA_MAX(w->score_width, num_digits);
@@ -1302,21 +1335,19 @@ err:
 
 static int com_ls_callback(struct afs_callback_arg *aca)
 {
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
        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;
+       const struct lls_opt_result *r_r;
+
+       ret = lls_deserialize_parse_result(
+               (char *)aca->query.data + sizeof(*opts), cmd, &opts->lpr);
+       assert(ret >= 0);
+       r_r = SERVER_CMD_OPT_RESULT(LS, REVERSE, opts->lpr);
 
        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++) {
-                       opts->patterns[i] = p;
-                       p += strlen(p) + 1;
-               }
-       } else
-               opts->patterns = NULL;
-       if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY)
+       if (admissible_only(opts))
                ret = admissible_file_loop(opts, prepare_ls_row);
        else
                ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
@@ -1324,14 +1355,14 @@ static int com_ls_callback(struct afs_callback_arg *aca)
        if (ret < 0)
                goto out;
        if (opts->num_matching_paths == 0) {
-               ret = opts->num_patterns > 0? -E_NO_MATCH : 0;
+               ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
                goto out;
        }
        ret = sort_matching_paths(opts);
        if (ret < 0)
                goto out;
        time(&current_time);
-       if (opts->flags & LS_FLAG_REVERSE)
+       if (lls_opt_given(r_r))
                for (i = opts->num_matching_paths - 1; i >= 0; i--) {
                        ret = print_list_item(opts->data_ptr[i], opts,
                                &aca->pbout, current_time);
@@ -1346,143 +1377,91 @@ static int com_ls_callback(struct afs_callback_arg *aca)
                                goto out;
                }
 out:
+       lls_free_parse_result(opts->lpr, cmd);
        free(opts->data);
        free(opts->data_ptr);
-       free(opts->patterns);
        return ret;
 }
 
-/*
- * TODO: flags -h (sort by hash)
- */
-int com_ls(struct command_context *cc)
+static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       int i;
-       unsigned flags = 0;
-       enum ls_sorting_method sort = LS_SORT_BY_PATH;
-       enum ls_listing_mode mode = LS_MODE_SHORT;
-       struct ls_options opts = {.patterns = NULL};
-       struct osl_object query = {.data = &opts, .size = sizeof(opts)};
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       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)) {
-                       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':
-                               mode = LS_MODE_VERBOSE;
-                               continue;
-                       case 'm':
-                               mode = LS_MODE_MBOX;
-                               continue;
-                       case 'c':
-                               mode = LS_MODE_CHUNKS;
-                               continue;
-                       case 'p':
-                               mode = LS_MODE_PARSER;
-                               continue;
-                       default:
-                               return -E_AFT_SYNTAX;
-                       }
-               }
-               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;
-               }
-               if (!strcmp(arg, "-r")) {
-                       flags |= LS_FLAG_REVERSE;
-                       continue;
-               }
-               if (!strcmp(arg, "-d")) {
-                       flags |= LS_FLAG_UNIXDATE;
-                       continue;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+       struct ls_options *opts;
+       struct osl_object query;
+       const struct lls_opt_result *r_l = SERVER_CMD_OPT_RESULT(LS, LISTING_MODE,
+               lpr);
+       const struct lls_opt_result *r_s = SERVER_CMD_OPT_RESULT(LS, SORT, lpr);
+       int ret;
+       char *slpr;
+
+       ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
+       assert(ret >= 0);
+       query.size += sizeof(*opts);
+       query.data = para_malloc(query.size);
+       opts = query.data;
+       memset(opts, 0, sizeof(*opts));
+       slpr = query.data + sizeof(*opts);
+       ret = lls_serialize_parse_result(lpr, cmd, &slpr, NULL);
+       assert(ret >= 0);
+       opts->mode = LS_MODE_SHORT;
+       opts->sorting = LS_SORT_BY_PATH;
+       if (lls_opt_given(r_l)) {
+               const char *val = lls_string_val(0, r_l);
+               if (!strcmp(val, "l") || !strcmp(val, "long"))
+                       opts->mode = LS_MODE_LONG;
+               else if (!strcmp(val, "s") || !strcmp(val, "short"))
+                       opts->mode = LS_MODE_SHORT;
+               else if (!strcmp(val, "v") || !strcmp(val, "verbose"))
+                       opts->mode = LS_MODE_VERBOSE;
+               else if (!strcmp(val, "m") || !strcmp(val, "mbox"))
+                       opts->mode = LS_MODE_MBOX;
+               else if (!strcmp(val, "c") || !strcmp(val, "chunk-table"))
+                       opts->mode = LS_MODE_MBOX;
+               else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
+                       opts->mode = LS_MODE_PARSER;
+               else {
+                       ret = -E_AFT_SYNTAX;
+                       goto out;
                }
-               /* The compatibility remark above applies also to -s. */
-               if (!strncmp(arg, "-s", 2)) {
-                       arg += 2;
-                       if (*arg == '=')
-                               arg++;
-                       switch (*arg) {
-                       case 'p':
-                               sort = LS_SORT_BY_PATH;
-                               continue;
-                       case 's': /* -ss implies -a */
-                               sort = LS_SORT_BY_SCORE;
-                               flags |= LS_FLAG_ADMISSIBLE_ONLY;
-                               continue;
-                       case 'l':
-                               sort = LS_SORT_BY_LAST_PLAYED;
-                               continue;
-                       case 'n':
-                               sort = LS_SORT_BY_NUM_PLAYED;
-                               continue;
-                       case 'f':
-                               sort = LS_SORT_BY_FREQUENCY;
-                               continue;
-                       case 'c':
-                               sort = LS_SORT_BY_CHANNELS;
-                               continue;
-                       case 'i':
-                               sort = LS_SORT_BY_IMAGE_ID;
-                               continue;
-                       case 'y':
-                               sort = LS_SORT_BY_LYRICS_ID;
-                               continue;
-                       case 'b':
-                               sort = LS_SORT_BY_BITRATE;
-                               continue;
-                       case 'd':
-                               sort = LS_SORT_BY_DURATION;
-                               continue;
-                       case 'a':
-                               sort = LS_SORT_BY_AUDIO_FORMAT;
-                               continue;
-                       default:
-                               return -E_AFT_SYNTAX;
-                       }
+       }
+       if (lls_opt_given(r_s)) {
+               const char *val = lls_string_val(0, r_s);
+               if (!strcmp(val, "p") || !strcmp(val, "path"))
+                       opts->sorting = LS_SORT_BY_PATH;
+               else if (!strcmp(val, "s") || !strcmp(val, "score"))
+                       opts->sorting = LS_SORT_BY_SCORE;
+               else if (!strcmp(val, "l") || !strcmp(val, "lastplayed"))
+                       opts->sorting = LS_SORT_BY_LAST_PLAYED;
+               else if (!strcmp(val, "n") || !strcmp(val, "numplayed"))
+                       opts->sorting = LS_SORT_BY_NUM_PLAYED;
+               else if (!strcmp(val, "f") || !strcmp(val, "frquency"))
+                       opts->sorting = LS_SORT_BY_FREQUENCY;
+               else if (!strcmp(val, "c") || !strcmp(val, "channels"))
+                       opts->sorting = LS_SORT_BY_CHANNELS;
+               else if (!strcmp(val, "i") || !strcmp(val, "image-id"))
+                       opts->sorting = LS_SORT_BY_IMAGE_ID;
+               else if (!strcmp(val, "y") || !strcmp(val, "lyrics-id"))
+                       opts->sorting = LS_SORT_BY_LYRICS_ID;
+               else if (!strcmp(val, "b") || !strcmp(val, "bitrate"))
+                       opts->sorting = LS_SORT_BY_BITRATE;
+               else if (!strcmp(val, "d") || !strcmp(val, "duration"))
+                       opts->sorting = LS_SORT_BY_DURATION;
+               else if (!strcmp(val, "a") || !strcmp(val, "audio-format"))
+                       opts->sorting = LS_SORT_BY_AUDIO_FORMAT;
+               else if (!strcmp(val, "h") || !strcmp(val, "hash"))
+                       opts->sorting = LS_SORT_BY_HASH;
+               else {
+                       ret = -E_AFT_SYNTAX;
+                       goto out;
                }
-               return -E_AFT_SYNTAX;
        }
-       opts.flags = flags;
-       opts.sorting = sort;
-       opts.mode = mode;
-       opts.num_patterns = cc->argc - i;
-       return send_option_arg_callback_request(&query, opts.num_patterns,
-               cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
+       ret = send_callback_request(com_ls_callback, &query,
+               afs_cb_result_handler, cc);
+out:
+       free(query.data);
+       return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(ls);
 
 /**
  * Call the given function for each file in the audio file table.
@@ -1522,8 +1501,8 @@ enum com_add_buffer_offsets {
        CAB_AFHI_OFFSET_POS = 0,
        /** Start of the chunk table (if present). */
        CAB_CHUNKS_OFFSET_POS = 4,
-       /** Flags given to the add command. */
-       CAB_FLAGS_OFFSET = 8,
+       /** Start of the (serialized) lopsub parse result. */
+       CAB_LPR_OFFSET = 8,
        /** Audio format id. */
        CAB_AUDIO_FORMAT_ID_OFFSET = 12,
        /** The hash of the audio file being added. */
@@ -1540,31 +1519,32 @@ enum com_add_buffer_offsets {
  * handler info won't be stored in the buffer.
  */
 static void save_add_callback_buffer(unsigned char *hash, const char *path,
-               struct afh_info *afhi, uint32_t flags,
+               struct afh_info *afhi, const char *slpr, size_t slpr_size,
                uint8_t audio_format_num, struct osl_object *obj)
 {
        size_t path_len = strlen(path) + 1;
        size_t afhi_size = sizeof_afhi_buf(afhi);
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
-               + sizeof_chunk_table(afhi);
+               + sizeof_chunk_table(afhi) + slpr_size;
        char *buf = para_malloc(size);
        uint32_t pos;
 
+       assert(size <= ~(uint32_t)0);
+       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);
        pos = CAB_PATH_OFFSET + path_len;
        write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
        save_afhi(afhi, buf + pos);
        pos += afhi_size;
-
        write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
-       if (afhi)
+       if (afhi) {
                save_chunk_table(afhi, buf + pos);
-
-       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);
-
+               pos += sizeof_chunk_table(afhi);
+       }
+       write_u32(buf + CAB_LPR_OFFSET, pos);
+       memcpy(buf + pos, slpr, slpr_size);
+       assert(pos + slpr_size == size);
        obj->data = buf;
        obj->size = size;
 }
@@ -1620,18 +1600,6 @@ Notes:
 
 */
 
-/** Flags passed to the add command. */
-enum com_add_flags {
-       /** Skip paths that exist already. */
-       ADD_FLAG_LAZY = 1,
-       /** Force adding. */
-       ADD_FLAG_FORCE = 2,
-       /** Print what is being done. */
-       ADD_FLAG_VERBOSE = 4,
-       /** Try to add files with unknown suffixes. */
-       ADD_FLAG_ALL = 8,
-};
-
 static int com_add_callback(struct afs_callback_arg *aca)
 {
        char *buf = aca->query.data, *path;
@@ -1642,9 +1610,16 @@ static int com_add_callback(struct afs_callback_arg *aca)
        char asc[2 * HASH_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
-       uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
+       char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
        uint16_t afhi_offset, chunks_offset;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+       const struct lls_opt_result *r_f, *r_v;
+
+       ret = lls_deserialize_parse_result(slpr, cmd, &aca->lpr);
+       assert(ret >= 0);
+       r_f = SERVER_CMD_OPT_RESULT(ADD, FORCE, aca->lpr);
+       r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);
@@ -1662,8 +1637,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
        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)
+       if (hs && pb && hs == pb && !lls_opt_given(r_f)) {
+               if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "ignoring duplicate\n");
                ret = 1;
                goto out;
@@ -1671,7 +1646,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        if (hs && hs != pb) {
                struct osl_object obj;
                if (pb) { /* hs trumps pb, remove pb */
-                       if (flags & ADD_FLAG_VERBOSE)
+                       if (lls_opt_given(r_v))
                                para_printf(&aca->pbout, "removing %s\n", path);
                        ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, pb);
                        if (ret < 0)
@@ -1682,7 +1657,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                        pb = NULL;
                }
                /* file rename, update hs' path */
-               if (flags & ADD_FLAG_VERBOSE) {
+               if (lls_opt_given(r_v)) {
                        ret = osl(osl_get_object(audio_file_table, hs,
                                AFTCOL_PATH, &obj));
                        if (ret < 0)
@@ -1697,7 +1672,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
                if (ret < 0)
                        goto out;
-               if (!(flags & ADD_FLAG_FORCE))
+               if (!lls_opt_given(r_f))
                        goto out;
        }
        /* no hs or force mode, child must have sent afhi */
@@ -1718,35 +1693,34 @@ static int com_add_callback(struct afs_callback_arg *aca)
                if (ret < 0)
                        goto out;
                hash_to_asc(old_hash, old_asc);
-               if (flags & ADD_FLAG_VERBOSE)
+               if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
-               ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
-                       &objs[AFTCOL_HASH]);
+               ret = osl(osl_update_object(audio_file_table, pb, AFTCOL_HASH,
+                       &objs[AFTCOL_HASH]));
                if (ret < 0)
                        goto out;
        }
        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)
+               if (lls_opt_given(r_v))
                        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)
                        goto out;
+               /* truncate the file to size zero if there is no chunk table */
                ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
                        &objs[AFTCOL_CHUNKS]));
                if (ret < 0)
                        goto out;
                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)
+       if (lls_opt_given(r_v))
                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_ID_OFFSET);
@@ -1761,15 +1735,20 @@ static int com_add_callback(struct afs_callback_arg *aca)
 out:
        if (ret < 0)
                para_printf(&aca->pbout, "could not add %s\n", path);
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-/** Used by com_add(). */
+/* Used by com_add(). */
 struct private_add_data {
-       /** The pointer passed to the original command handler. */
+       /* The pointer passed to the original command handler. */
        struct command_context *cc;
-       /** The given add flags. */
-       uint32_t flags;
+       /* Contains the flags given at the command line. */
+       struct lls_parse_result *lpr;
+       /* Serialized lopsub parse result. */
+       char *slpr;
+       /* Number of bytes. */
+       size_t slpr_size;
 };
 
 static int path_brother_callback(struct afs_callback_arg *aca)
@@ -1814,9 +1793,13 @@ static int add_one_audio_file(const char *path, void *private_data)
        struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
        struct osl_object map, obj = {.data = NULL}, query;
        unsigned char hash[HASH_SIZE];
+       bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
+       bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
+       bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
+       bool v_given = SERVER_CMD_OPT_GIVEN(ADD, VERBOSE, pad->lpr);
 
        ret = guess_audio_format(path);
-       if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) {
+       if (ret < 0 && !a_given) {
                ret = 0;
                goto out_free;
        }
@@ -1827,8 +1810,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                goto out_free;
        ret = 1;
-       if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
-               if (pad->flags & ADD_FLAG_VERBOSE)
+       if (pb && l_given) { /* lazy is really cheap */
+               if (v_given)
                        send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                                "lazy-ignore: %s\n", path);
                goto out_free;
@@ -1848,8 +1831,8 @@ static int add_one_audio_file(const char *path, void *private_data)
                goto out_unmap;
        /* Return success if we already know this file. */
        ret = 1;
-       if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
-               if (pad->flags & ADD_FLAG_VERBOSE)
+       if (pb && hs && hs == pb && !f_given) {
+               if (v_given)
                        send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                                "%s exists, not forcing update\n", path);
                goto out_unmap;
@@ -1858,7 +1841,7 @@ static int add_one_audio_file(const char *path, void *private_data)
         * We won't recalculate the audio format info and the chunk table if
         * there is a hash sister and FORCE was not given.
         */
-       if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
+       if (!hs || f_given) {
                ret = compute_afhi(path, map.data, map.size, fd, &afhi);
                if (ret < 0)
                        goto out_unmap;
@@ -1867,13 +1850,14 @@ 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) {
+       if (v_given) {
                send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                        "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
-       save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
+       save_add_callback_buffer(hash, path, afhi_ptr, pad->slpr,
+               pad->slpr_size, format_num, &obj);
        /* Ask afs to consider this entry for adding. */
        ret = send_callback_request(com_add_callback, &obj,
                afs_cb_result_handler, pad->cc);
@@ -1892,46 +1876,30 @@ out_free:
        return send_ret;
 }
 
-int com_add(struct command_context *cc)
+static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret;
-       struct private_add_data pad = {.cc = cc, .flags = 0};
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-a")) {
-                       pad.flags |= ADD_FLAG_ALL;
-                       continue;
-               }
-               if (!strcmp(arg, "-l")) {
-                       pad.flags |= ADD_FLAG_LAZY;
-                       continue;
-               }
-               if (!strcmp(arg, "-f")) {
-                       pad.flags |= ADD_FLAG_FORCE;
-                       continue;
-               }
-               if (!strcmp(arg, "-v")) {
-                       pad.flags |= ADD_FLAG_VERBOSE;
-                       continue;
-               }
+       struct private_add_data pad = {.cc = cc, .lpr = lpr};
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+       unsigned num_inputs;
+       char *errctx;
+
+       ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
-       if (cc->argc <= i)
-               return -E_AFT_SYNTAX;
-       for (; i < cc->argc; i++) {
+       ret = lls_serialize_parse_result(lpr, cmd, &pad.slpr, &pad.slpr_size);
+       assert(ret >= 0);
+       num_inputs = lls_num_inputs(lpr);
+       for (i = 0; i < num_inputs; i++) {
                char *path;
-               ret = verify_path(cc->argv[i], &path);
+               ret = verify_path(lls_input(i, lpr), &path);
                if (ret < 0) {
                        ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
-                               cc->argv[i], para_strerror(-ret));
+                               lls_input(i, lpr), para_strerror(-ret));
                        if (ret < 0)
-                               return ret;
+                               goto out;
                        continue;
                }
                if (ret == 1) /* directory */
@@ -1947,14 +1915,14 @@ int com_add(struct command_context *cc)
                }
                free(path);
        }
-       return 1;
+       ret = 1;
+out:
+       free(pad.slpr);
+       return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(add);
 
-/**
- * Flags used by the touch command.
- *
- * \sa com_touch().
- */
+/** Flags used by the touch command. */
 enum touch_flags {
        /** Whether the \p FNM_PATHNAME flag should be passed to fnmatch(). */
        TOUCH_FLAG_FNM_PATHNAME = 1,
@@ -1962,33 +1930,26 @@ enum touch_flags {
        TOUCH_FLAG_VERBOSE = 2
 };
 
-/** Options used by com_touch(). */
-struct com_touch_options {
-       /** New num_played value. */
-       int32_t num_played;
-       /** New last played count. */
-       int64_t last_played;
-       /** New lyrics id. */
-       int32_t lyrics_id;
-       /** New image id. */
-       int32_t image_id;
-       /** New amplification value. */
-       int32_t amp;
-       /** Command line flags (see \ref touch_flags). */
-       unsigned flags;
-};
-
 static int touch_audio_file(__a_unused struct osl_table *table,
                struct osl_row *row, const char *name, void *data)
 {
        struct afs_callback_arg *aca = data;
-       struct com_touch_options *cto = aca->query.data;
+       bool v_given = SERVER_CMD_OPT_GIVEN(TOUCH, VERBOSE, aca->lpr);
+       const struct lls_opt_result *r_n, *r_l, *r_i, *r_y, *r_a;
+       int ret;
        struct osl_object obj;
        struct afs_info old_afsi, new_afsi;
-       int ret, no_options = cto->num_played < 0 && cto->last_played < 0 &&
-               cto->lyrics_id < 0 && cto->image_id < 0 && cto->amp < 0;
+       bool no_options;
        struct afsi_change_event_data aced;
 
+       r_n = SERVER_CMD_OPT_RESULT(TOUCH, NUMPLAYED, aca->lpr);
+       r_l = SERVER_CMD_OPT_RESULT(TOUCH, LASTPLAYED, aca->lpr);
+       r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+       r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+       r_a = SERVER_CMD_OPT_RESULT(TOUCH, AMP, aca->lpr);
+       no_options = !lls_opt_given(r_n) && !lls_opt_given(r_l) && !lls_opt_given(r_i)
+               && !lls_opt_given(r_y) && !lls_opt_given(r_a);
+
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot touch %s\n", name);
@@ -2003,23 +1964,23 @@ static int touch_audio_file(__a_unused struct osl_table *table,
        if (no_options) {
                new_afsi.num_played++;
                new_afsi.last_played = time(NULL);
-               if (cto->flags & TOUCH_FLAG_VERBOSE)
+               if (v_given)
                        para_printf(&aca->pbout, "%s: num_played = %u, "
                                "last_played = now()\n", name,
                                new_afsi.num_played);
        } else {
-               if (cto->flags & TOUCH_FLAG_VERBOSE)
+               if (lls_opt_given(r_l))
+                       new_afsi.last_played = lls_uint64_val(0, r_l);
+               if (lls_opt_given(r_n))
+                       new_afsi.num_played = lls_uint32_val(0, r_n);
+               if (lls_opt_given(r_i))
+                       new_afsi.image_id = lls_uint32_val(0, r_i);
+               if (lls_opt_given(r_y))
+                       new_afsi.lyrics_id = lls_uint32_val(0, r_y);
+               if (lls_opt_given(r_a))
+                       new_afsi.amp = lls_uint32_val(0, r_a);
+               if (v_given)
                        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;
@@ -2029,134 +1990,73 @@ static int touch_audio_file(__a_unused struct osl_table *table,
 
 static int com_touch_callback(struct afs_callback_arg *aca)
 {
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+       bool p_given;
+       const struct lls_opt_result *r_i, *r_y;
        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 *)aca->query.data
-                               + sizeof(struct com_touch_options),
-                       .size = aca->query.size
-                               - sizeof(struct com_touch_options)
-               },
                .data = aca,
                .action = touch_audio_file
        };
-       if (cto->image_id >= 0) {
-               ret = img_get_name_by_id(cto->image_id, NULL);
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+
+       r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+       if (lls_opt_given(r_i)) {
+               uint32_t id = lls_uint32_val(0, r_i);
+               ret = img_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid image ID: %d\n",
-                               cto->image_id);
+                       para_printf(&aca->pbout, "invalid image ID: %u\n", id);
                        return ret;
                }
        }
-       if (cto->lyrics_id >= 0) {
-               ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+       r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+       if (lls_opt_given(r_y)) {
+               uint32_t id = lls_uint32_val(0, r_y);
+               ret = lyr_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid lyrics ID: %d\n",
-                               cto->lyrics_id);
+                       para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
                        return ret;
                }
        }
-       if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
+       p_given = SERVER_CMD_OPT_GIVEN(TOUCH, PATHNAME_MATCH, aca->lpr);
+       if (p_given)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
        if (ret >= 0 && pmd.num_matches == 0)
                ret = -E_NO_MATCH;
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_touch(struct command_context *cc)
+static int com_touch(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct com_touch_options cto = {
-               .num_played = -1,
-               .last_played = -1,
-               .lyrics_id = -1,
-               .image_id = -1,
-               .amp = -1,
-       };
-       struct osl_object query = {.data = &cto, .size = sizeof(cto)};
-       int i, ret;
-
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+       int ret;
+       char *errctx;
 
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-n=", 3)) {
-                       ret = para_atoi32(arg + 3, &cto.num_played);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strncmp(arg, "-l=", 3)) {
-                       ret = para_atoi64(arg + 3, &cto.last_played);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strncmp(arg, "-y=", 3)) {
-                       ret = para_atoi32(arg + 3, &cto.lyrics_id);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strncmp(arg, "-i=", 3)) {
-                       ret = para_atoi32(arg + 3, &cto.image_id);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strncmp(arg, "-a=", 3)) {
-                       int32_t val;
-                       ret = para_atoi32(arg + 3, &val);
-                       if (ret < 0)
-                               return ret;
-                       if (val < 0 || val > 255)
-                               return -ERRNO_TO_PARA_ERROR(EINVAL);
-                       cto.amp = val;
-                       continue;
-               }
-               if (!strcmp(arg, "-p")) {
-                       cto.flags |= TOUCH_FLAG_FNM_PATHNAME;
-                       continue;
-               }
-               if (!strcmp(arg, "-v")) {
-                       cto.flags |= TOUCH_FLAG_VERBOSE;
-                       continue;
-               }
-               break; /* non-option starting with dash */
+       ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
-       if (i >= cc->argc)
-               return -E_AFT_SYNTAX;
-       return send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
-}
-
-/** Flags for com_rm(). */
-enum rm_flags {
-       /** -v */
-       RM_FLAG_VERBOSE = 1,
-       /** -f */
-       RM_FLAG_FORCE = 2,
-       /** -p */
-       RM_FLAG_FNM_PATHNAME = 4
-};
+       return send_lls_callback_request(com_touch_callback, cmd, lpr, cc);
+}
+EXPORT_SERVER_CMD_HANDLER(touch);
 
 static int remove_audio_file(__a_unused struct osl_table *table,
                struct osl_row *row, const char *name, void *data)
 {
        struct afs_callback_arg *aca = data;
-       uint32_t flags =*(uint32_t *)aca->query.data;
+       bool v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
        int ret;
 
-       if (flags & RM_FLAG_VERBOSE)
+       if (v_given)
                para_printf(&aca->pbout, "removing %s\n", name);
        ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, row);
        if (ret < 0)
@@ -2169,95 +2069,63 @@ static int remove_audio_file(__a_unused struct osl_table *table,
 
 static int com_rm_callback(struct afs_callback_arg *aca)
 {
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
        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 *)aca->query.data + sizeof(uint32_t),
-                       .size = aca->query.size - sizeof(uint32_t)},
                .data = aca,
                .action = remove_audio_file
        };
-       if (flags & RM_FLAG_FNM_PATHNAME)
+       bool v_given, p_given, f_given;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+       v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
+       p_given = SERVER_CMD_OPT_GIVEN(RM, PATHNAME_MATCH, aca->lpr);
+       f_given = SERVER_CMD_OPT_GIVEN(RM, FORCE, aca->lpr);
+
+       if (p_given)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
        if (pmd.num_matches == 0) {
-               if (!(flags & RM_FLAG_FORCE))
+               if (!f_given)
                        ret = -E_NO_MATCH;
-       } else if (flags & RM_FLAG_VERBOSE)
+       } else if (v_given)
                para_printf(&aca->pbout, "removed %u file(s)\n",
                        pmd.num_matches);
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
 /* TODO options: -r (recursive) */
-int com_rm(struct command_context *cc)
+static int com_rm(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       uint32_t flags = 0;
-       struct osl_object query = {.data = &flags, .size = sizeof(flags)};
-       int i;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
+       char *errctx;
+       int ret;
 
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-f")) {
-                       flags |= RM_FLAG_FORCE;
-                       continue;
-               }
-               if (!strcmp(arg, "-p")) {
-                       flags |= RM_FLAG_FNM_PATHNAME;
-                       continue;
-               }
-               if (!strcmp(arg, "-v")) {
-                       flags |= RM_FLAG_VERBOSE;
-                       continue;
-               }
-               break;
+       ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
-       if (i >= cc->argc)
-               return -E_AFT_SYNTAX;
-       return send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
+       return send_lls_callback_request(com_rm_callback, cmd, lpr, cc);
 }
-
-/**
- * Flags used by the cpsi command.
- *
- * \sa com_cpsi().
- */
-enum cpsi_flags {
-       /** Whether the lyrics id should be copied. */
-       CPSI_FLAG_COPY_LYRICS_ID = 1,
-       /** Whether the image id should be copied. */
-       CPSI_FLAG_COPY_IMAGE_ID = 2,
-       /** Whether the lastplayed time should be copied. */
-       CPSI_FLAG_COPY_LASTPLAYED = 4,
-       /** Whether the numplayed count should be copied. */
-       CPSI_FLAG_COPY_NUMPLAYED = 8,
-       /** Whether the attributes should be copied. */
-       CPSI_FLAG_COPY_ATTRIBUTES = 16,
-       /** Activates verbose mode. */
-       CPSI_FLAG_VERBOSE = 32,
-};
+EXPORT_SERVER_CMD_HANDLER(rm);
 
 /** Data passed to the action handler of com_cpsi(). */
 struct cpsi_action_data {
-       /** command line flags (see \ref cpsi_flags). */
-       unsigned flags;
        /** Values are copied from here. */
        struct afs_info source_afsi;
        /** What was passed to com_cpsi_callback(). */
        struct afs_callback_arg *aca;
+       bool copy_all;
 };
 
 static int copy_selector_info(__a_unused struct osl_table *table,
@@ -2268,6 +2136,14 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        int ret;
        struct afs_info old_afsi, target_afsi;
        struct afsi_change_event_data aced;
+       bool a_given, y_given, i_given, l_given, n_given, v_given;
+
+       a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, cad->aca->lpr);
+       y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, cad->aca->lpr);
+       i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, cad->aca->lpr);
+       l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, cad->aca->lpr);
+       n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, cad->aca->lpr);
+       v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, cad->aca->lpr);
 
        ret = get_afsi_object_of_row(row, &target_afsi_obj);
        if (ret < 0)
@@ -2276,18 +2152,18 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        if (ret < 0)
                return ret;
        old_afsi = target_afsi;
-       if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID)
+       if (cad->copy_all || y_given)
                target_afsi.lyrics_id = cad->source_afsi.lyrics_id;
-       if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID)
+       if (cad->copy_all || i_given)
                target_afsi.image_id = cad->source_afsi.image_id;
-       if (cad->flags & CPSI_FLAG_COPY_LASTPLAYED)
+       if (cad->copy_all || l_given)
                target_afsi.last_played = cad->source_afsi.last_played;
-       if (cad->flags & CPSI_FLAG_COPY_NUMPLAYED)
+       if (cad->copy_all || n_given)
                target_afsi.num_played = cad->source_afsi.num_played;
-       if (cad->flags & CPSI_FLAG_COPY_ATTRIBUTES)
+       if (cad->copy_all || a_given)
                target_afsi.attributes = cad->source_afsi.attributes;
        save_afsi(&target_afsi, &target_afsi_obj); /* in-place update */
-       if (cad->flags & CPSI_FLAG_VERBOSE)
+       if (v_given)
                para_printf(&cad->aca->pbout, "copied afsi to %s\n", name);
        aced.aft_row = row;
        aced.old_afsi = &old_afsi;
@@ -2296,86 +2172,60 @@ static int copy_selector_info(__a_unused struct osl_table *table,
 
 static int com_cpsi_callback(struct afs_callback_arg *aca)
 {
-       struct cpsi_action_data cad = {
-               .flags = *(unsigned *)aca->query.data,
-               .aca = aca
-       };
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+       bool a_given, y_given, i_given, l_given, n_given, v_given;
+       struct cpsi_action_data cad = {.aca = aca};
        int ret;
-       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 = aca->query.size - sizeof(cad.flags)
-                               - strlen(source_path) - 1},
+               .input_skip = 1, /* skip first argument (source file) */
                .data = &cad,
                .action = copy_selector_info
        };
 
-       ret = get_afsi_of_path(source_path, &cad.source_afsi);
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+
+       a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, aca->lpr);
+       y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, aca->lpr);
+       i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, aca->lpr);
+       l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, aca->lpr);
+       n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, aca->lpr);
+       v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, aca->lpr);
+       cad.copy_all = !a_given && !y_given && !i_given && !l_given && !n_given;
+
+       ret = get_afsi_of_path(lls_input(0, aca->lpr), &cad.source_afsi);
        if (ret < 0)
                goto out;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
        if (pmd.num_matches > 0) {
-               if (cad.flags & CPSI_FLAG_VERBOSE)
+               if (v_given)
                        para_printf(&aca->pbout, "updated afsi of %u file(s)\n",
                                pmd.num_matches);
        } else
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_cpsi(struct command_context *cc)
+static int com_cpsi(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       unsigned flags = 0;
-       int i;
-       struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-y")) {
-                       flags |= CPSI_FLAG_COPY_LYRICS_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-i")) {
-                       flags |= CPSI_FLAG_COPY_IMAGE_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-l")) {
-                       flags |= CPSI_FLAG_COPY_LASTPLAYED;
-                       continue;
-               }
-               if (!strcmp(arg, "-n")) {
-                       flags |= CPSI_FLAG_COPY_NUMPLAYED;
-                       continue;
-               }
-               if (!strcmp(arg, "-a")) {
-                       flags |= CPSI_FLAG_COPY_ATTRIBUTES;
-                       continue;
-               }
-               if (!strcmp(arg, "-v")) {
-                       flags |= CPSI_FLAG_VERBOSE;
-                       continue;
-               }
-               break;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
-       if (i + 1 >= cc->argc) /* need at least source file and pattern */
-               return -E_AFT_SYNTAX;
-       if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
-               flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
-       return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
+       return send_lls_callback_request(com_cpsi_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(cpsi);
 
 struct change_atts_data {
        uint64_t add_mask, del_mask;
@@ -2409,9 +2259,8 @@ static int change_atts(__a_unused struct osl_table *table,
 
 static int com_setatt_callback(struct afs_callback_arg *aca)
 {
-       char *p;
-       int ret;
-       size_t len;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+       int i, ret;
        struct change_atts_data cad = {.aca = aca};
        struct pattern_match_data pmd = {
                .table = audio_file_table,
@@ -2421,27 +2270,36 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                .data = &cad,
                .action = change_atts
        };
+       unsigned num_inputs;
 
-       for (
-               p = aca->query.data;
-               p < (char *)aca->query.data + aca->query.size;
-               p += len + 1
-       ) {
-               char c;
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+
+       num_inputs = lls_num_inputs(aca->lpr);
+       for (i = 0; i < num_inputs; i++) {
                unsigned char bitnum;
                uint64_t one = 1;
+               const char *arg = lls_input(i, aca->lpr);
+               char c, *p;
+               size_t len = strlen(arg);
 
-               len = strlen(p);
                ret = -E_ATTR_SYNTAX;
                if (len == 0)
                        goto out;
-               c = p[len - 1];
-               if (c != '+' && c != '-')
-                       break;
+               c = arg[len - 1];
+               if (c != '+' && c != '-') {
+                       if (cad.add_mask == 0 && cad.del_mask == 0)
+                               goto out; /* no attribute modifier given */
+                       goto set_atts;
+               }
+               p = para_malloc(len);
+               memcpy(p, arg, len - 1);
                p[len - 1] = '\0';
                ret = get_attribute_bitnum_by_name(p, &bitnum);
+               free(p);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "attribute not found: %s\n", p);
+                       para_printf(&aca->pbout, "invalid argument: %s\n", arg);
                        goto out;
                }
                if (c == '+')
@@ -2449,29 +2307,32 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                else
                        cad.del_mask |= (one << bitnum);
        }
+       /* no pattern given */
        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;
+       goto out;
+set_atts:
+       pmd.input_skip = i;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               goto out;
-       if (pmd.num_matches == 0)
+       if (ret >= 0 && pmd.num_matches == 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_setatt(struct command_context *cc)
+static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       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);
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_setatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(setatt);
 
 static int afs_stat_callback(struct afs_callback_arg *aca)
 {
@@ -2547,8 +2408,6 @@ static int check_audio_file(struct osl_row *row, void *data)
  * \param aca Only ->pbout is used for diagnostics.
  *
  * \return Standard. Inconsistencies are reported but not regarded as an error.
- *
- * \sa com_check().
  */
 int aft_check_callback(struct afs_callback_arg *aca)
 {
@@ -2617,7 +2476,7 @@ int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb)
  *
  * \param flags Usual flags that are passed to osl_close_table().
  *
- * \sa osl_close_table().
+ * \sa \ref osl_close_table().
  */
 static void aft_close(void)
 {
@@ -2632,7 +2491,7 @@ static void aft_close(void)
  *
  * \return Standard.
  *
- * \sa osl_open_table().
+ * \sa \ref osl_open_table().
  */
 static int aft_open(const char *dir)
 {
index 3adee929a804434fcea1999a439819dc85c66936..6520416fb83931ede15a299118a972f5893b2f34 100644 (file)
@@ -216,19 +216,13 @@ static int alsa_mix_set(struct mixer_handle *h, int val)
        return 1;
 }
 
-/**
- * The init function of the ALSA mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref oss_mix_init().
- */
-void alsa_mix_init(struct mixer *self)
-{
-       self->open = alsa_mix_open;
-       self->get_channels = alsa_mix_get_channels;
-       self->set_channel = alsa_mix_set_channel;
-       self->close = alsa_mix_close;
-       self->get = alsa_mix_get;
-       self->set = alsa_mix_set;
-}
+/** The mixer operations for the ALSA mixer. */
+const struct mixer alsa_mixer = {
+       .name = "alsa",
+       .open = alsa_mix_open,
+       .get_channels = alsa_mix_get_channels,
+       .set_channel = alsa_mix_set_channel,
+       .close = alsa_mix_close,
+       .get = alsa_mix_get,
+       .set = alsa_mix_set
+};
index fd3b404c0359c1a571255b1ac7265b0ee0faef41..3935c8c687be53e25791abbb93697a8d61186527 100644 (file)
 #include <regex.h>
 #include <sys/types.h>
 #include <alsa/asoundlib.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "fd.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
-#include "alsa_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the alsa writer. */
@@ -71,22 +70,22 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
 }
 
 /* Install PCM software and hardware configuration. */
-static int alsa_init(struct private_alsa_write_data *pad,
-               struct alsa_write_args_info *conf)
+static int alsa_init(struct writer_node *wn)
 {
+       struct private_alsa_write_data *pad = wn->private_data;
        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;
        int ret;
-       const char *msg;
+       const char *msg, *dev = WRITE_CMD_OPT_STRING_VAL(ALSA, DEVICE, wn->lpr);
        unsigned period_time;
 
-       PARA_INFO_LOG("opening %s\n", conf->device_arg);
+       PARA_INFO_LOG("opening %s\n", dev);
        msg = "unable to open pcm";
-       ret = snd_pcm_open(&pad->handle, conf->device_arg,
-               SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+       ret = snd_pcm_open(&pad->handle, dev, SND_PCM_STREAM_PLAYBACK,
+               SND_PCM_NONBLOCK);
        if (ret < 0)
                goto fail;
        ret = snd_pcm_hw_params_malloc(&hwparams);
@@ -116,7 +115,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
        if (ret < 0)
                goto fail;
        /* alsa wants microseconds */
-       pad->buffer_time = conf->buffer_time_arg * 1000;
+       pad->buffer_time = 1000U * WRITE_CMD_OPT_UINT32_VAL(ALSA, BUFFER_TIME,
+               wn->lpr);
        msg = "could not set buffer time";
        ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
                &pad->buffer_time, NULL);
@@ -295,7 +295,7 @@ again:
 
                if (bytes == 0) /* no data available */
                        return 0;
-               pad = para_calloc(sizeof(*pad));
+               pad = wn->private_data = para_calloc(sizeof(*pad));
                get_btr_sample_rate(btrn, &val);
                pad->sample_rate = val;
                get_btr_channels(btrn, &val);
@@ -305,12 +305,12 @@ again:
 
                PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
-               ret = alsa_init(pad, wn->conf);
+               ret = alsa_init(wn);
                if (ret < 0) {
-                       free(pad);
+                       free(wn->private_data);
+                       wn->private_data = NULL;
                        goto err;
                }
-               wn->private_data = pad;
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
@@ -340,37 +340,9 @@ err:
        return ret;
 }
 
-__malloc static void *alsa_parse_config_or_die(int argc, char **argv)
-{
-       struct alsa_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       alsa_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void alsa_free_config(void *conf)
-{
-       alsa_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the alsa writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct \ref writer.
- */
-void alsa_write_init(struct writer *w)
-{
-       struct alsa_write_args_info dummy;
+struct writer lsg_write_cmd_com_alsa_user_data = {
 
-       alsa_write_cmdline_parser_init(&dummy);
-       w->close = alsa_close;
-       w->pre_select = alsa_write_pre_select;
-       w->post_select = alsa_write_post_select;
-       w->parse_config_or_die = alsa_parse_config_or_die;
-       w->free_config = alsa_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write);
-       alsa_write_cmdline_parser_free(&dummy);
-}
+       .pre_select = alsa_write_pre_select,
+       .post_select = alsa_write_post_select,
+       .close = alsa_close,
+};
index 5193d7c166d9345ced27899e38714ddd5d393fcf..f3d0d87d45f077d901d5f4cb98c20dc9a3099547 100644 (file)
@@ -7,12 +7,12 @@
 /** \file amp_filter.c Paraslash's amplify filter. */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "amp_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -31,33 +31,18 @@ static void amp_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
-static int amp_parse_config(int argc, char **argv, void **config)
-{
-       struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
-       int ret;
-
-       amp_filter_cmdline_parser(argc, argv, conf);
-       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-       if (conf->amp_arg < 0)
-               goto err;
-       *config = conf;
-       return 1;
-err:
-       free(conf);
-       return ret;
-}
-
 static void amp_open(struct filter_node *fn)
 {
        struct private_amp_data *pad = para_calloc(sizeof(*pad));
-       struct amp_filter_args_info *conf = fn->conf;
+       unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
+       uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
 
        fn->private_data = pad;
        fn->min_iqs = 2;
-       if (!conf->amp_given && stat_item_values[SI_AMPLIFICATION])
+       if (!given && stat_item_values[SI_AMPLIFICATION])
                sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp);
        else
-               pad->amp = conf->amp_arg;
+               pad->amp = amp_arg;
        PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n",
                pad->amp, pad->amp / 64.0 + 1.0);
 }
@@ -116,26 +101,9 @@ err:
        return ret;
 }
 
-static void amp_free_config(void *conf)
-{
-       amp_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the amplify filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void amp_filter_init(struct filter *f)
-{
-       struct amp_filter_args_info dummy;
-
-       amp_filter_cmdline_parser_init(&dummy);
-       f->open = amp_open;
-       f->close = amp_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = amp_post_select;
-       f->parse_config = amp_parse_config;
-       f->free_config = amp_free_config;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter);
-}
+const struct filter lsg_filter_cmd_com_amp_user_data = {
+       .open = amp_open,
+       .close = amp_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = amp_post_select,
+};
index 82d98f3ee8cf6c0491e1820ee32b63c8216abac2..e469a394fa6ee8df698a633274cf16394891e102 100644 (file)
@@ -9,17 +9,16 @@
 #include <pthread.h>
 #include <ao/ao.h>
 #include <regex.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "fd.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
-#include "ao_write.cmdline.h"
 #include "error.h"
 
 struct private_aow_data {
@@ -44,6 +43,7 @@ static void aow_close(struct writer_node *wn)
        ao_close(pawd->dev);
        free(pawd);
        wn->private_data = NULL;
+       ao_shutdown();
 }
 
 static void aow_pre_select(struct sched *s, void *context)
@@ -147,6 +147,38 @@ static int aow_open_device(int id, ao_sample_format *asf, ao_option *options,
        return -E_AO_OPEN_LIVE;
 }
 
+static void aow_show_drivers(void)
+{
+       int i, j, num_drivers;
+       ao_info **driver_list;
+
+       PARA_DEBUG_LOG("libao drivers available on this host:\n");
+       PARA_DEBUG_LOG("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+       driver_list = ao_driver_info_list(&num_drivers);
+
+       for (i = 0; i < num_drivers; i++) {
+               ao_info *info = driver_list[i];
+               char *keys = NULL, *tmp = NULL;
+
+               if (info->type == AO_TYPE_FILE)
+                       continue;
+               PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
+               PARA_DEBUG_LOG("priority: %d", info->priority);
+               for (j = 0; j < info->option_count; j++) {
+                       tmp = make_message("%s%s%s", keys? keys : "",
+                               keys? ", " : "",
+                               info->options[j]);
+                       free(keys);
+                       keys = tmp;
+               }
+               PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+               free(keys);
+               PARA_DEBUG_LOG("comment: %s", info->comment?
+                       info->comment : "[none]");
+       }
+}
+
 static int aow_init(struct writer_node *wn, unsigned sample_rate,
                unsigned channels, int sample_format)
 {
@@ -154,12 +186,15 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        ao_option *aoo = NULL;
        ao_sample_format asf;
        ao_info *info;
+       const struct lls_opt_result *r;
+       unsigned n;
        struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
-       struct ao_write_args_info *conf = wn->conf;
 
-       if (conf->driver_given) {
+       ao_initialize();
+       aow_show_drivers();
+       if (WRITE_CMD_OPT_GIVEN(AO, DRIVER, wn->lpr)) {
                ret = -E_AO_BAD_DRIVER;
-               id = ao_driver_id(conf->driver_arg);
+               id = ao_driver_id(WRITE_CMD_OPT_STRING_VAL(AO, DRIVER, wn->lpr));
        } else {
                ret = -E_AO_DEFAULT_DRIVER;
                id = ao_default_driver_id();
@@ -173,8 +208,11 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
                goto fail;
        }
        PARA_INFO_LOG("using %s driver\n", info->short_name);
-       for (i = 0; i < conf->ao_option_given; i++) {
-               char *o = para_strdup(conf->ao_option_arg[i]), *value;
+       r = WRITE_CMD_OPT_RESULT(AO, AO_OPTION, wn->lpr);
+       n = lls_opt_given(r);
+       for (i = 0; i < n; i++) {
+               char *o = para_strdup(lls_string_val(i, r));
+               char *value;
 
                ret = -E_AO_BAD_OPTION;
                value = strchr(o, ':');
@@ -379,78 +417,9 @@ out:
        return ret;
 }
 
-__malloc static void *aow_parse_config_or_die(int argc, char **argv)
-{
-       struct ao_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       ao_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void aow_free_config(void *conf)
-{
-       ao_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the ao writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void ao_write_init(struct writer *w)
-{
-       struct ao_write_args_info dummy;
-       int i, j, num_drivers, num_lines;
-       ao_info **driver_list;
-       char **dh; /* detailed help */
-
-       ao_write_cmdline_parser_init(&dummy);
-       w->close = aow_close;
-       w->pre_select = aow_pre_select;
-       w->post_select = aow_post_select;
-       w->parse_config_or_die = aow_parse_config_or_die;
-       w->free_config = aow_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write);
-       /* create detailed help containing all supported drivers/options */
-       for (i = 0; ao_write_args_info_detailed_help[i]; i++)
-               ; /* nothing */
-       num_lines = i;
-       dh = para_malloc((num_lines + 3) * sizeof(char *));
-       for (i = 0; i < num_lines; i++)
-               dh[i] = para_strdup(ao_write_args_info_detailed_help[i]);
-       dh[num_lines++] = para_strdup("libao drivers available on this host:");
-       dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
-
-       ao_initialize();
-       driver_list = ao_driver_info_list(&num_drivers);
-
-       for (i = 0; i < num_drivers; i++) {
-               ao_info *info = driver_list[i];
-               char *keys = NULL, *tmp = NULL;
-
-               if (info->type == AO_TYPE_FILE)
-                       continue;
-               for (j = 0; j < info->option_count; j++) {
-                       tmp = make_message("%s%s%s", keys? keys : "",
-                               keys? ", " : "",
-                               info->options[j]);
-                       free(keys);
-                       keys = tmp;
-               }
-               dh = para_realloc(dh, (num_lines + 6) * sizeof(char *));
-               dh[num_lines++] = make_message("%s: %s", info->short_name, info->name);
-               dh[num_lines++] = make_message("priority: %d", info->priority);
-               dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]");
-               dh[num_lines++] = make_message("comment: %s", info->comment?
-                       info->comment : "[none]");
-               dh[num_lines++] = para_strdup(NULL);
-               free(keys);
-       }
-       dh[num_lines] = NULL;
-       w->help.detailed_help = (const char **)dh;
-       ao_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_ao_user_data = {
+       .close = aow_close,
+       .pre_select = aow_pre_select,
+       .post_select = aow_post_select,
+};
 
index 4cb2982860de78dd26b9fc55ad552c73a7ca1b9f..2ea73a6ab41fed2dcd18c8ca077b661e9809a3b7 100644 (file)
@@ -8,7 +8,9 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -106,30 +108,16 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum)
        return 1;
 }
 
-/**
- * Flags used by the lsatt command.
- *
- * \param \sa com_lsatt().
- */
-enum lsatt_flags {
-       /** Whether "-a" was given for the lsatt command. */
-       LSATT_FLAG_SORT_BY_ID = 1,
-       /** Whether "-l" was given for the lsatt command. */
-       LSATT_FLAG_LONG = 2,
-       /** Reverse sort order. */
-       LSATT_FLAG_REVERSE = 4
-};
-
 /** Data passed to the action function of lsatt */
 static int print_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
        struct afs_callback_arg *aca = data;
-       unsigned flags = *(unsigned *)aca->query.data;
+       bool l_given = SERVER_CMD_OPT_GIVEN(LSATT, LONG, aca->lpr);
        struct osl_object bitnum_obj;
        int ret;
 
-       if (!(flags & LSATT_FLAG_LONG)) {
+       if (!l_given) {
                para_printf(&aca->pbout, "%s\n", name);
                return 1;
        }
@@ -145,21 +133,27 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
 
 static int com_lsatt_callback(struct afs_callback_arg *aca)
 {
-       unsigned flags = *(unsigned *)aca->query.data;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+       bool i_given, r_given;
        int ret;
        struct pattern_match_data pmd = {
                .table = attribute_table,
                .loop_col_num = ATTCOL_NAME,
                .match_col_num = ATTCOL_NAME,
-               .patterns = {.data = (char *)aca->query.data + sizeof(flags),
-                       .size = aca->query.size - sizeof(flags)},
                .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING,
                .data = aca,
                .action = print_attribute
        };
-       if (flags & LSATT_FLAG_SORT_BY_ID)
+
+       ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+       i_given = SERVER_CMD_OPT_GIVEN(LSATT, ID_SORT, aca->lpr);
+       r_given = SERVER_CMD_OPT_GIVEN(LSATT, REVERSE, aca->lpr);
+
+       if (i_given)
                pmd.loop_col_num = ATTCOL_BITNUM;
-       if (flags & LSATT_FLAG_REVERSE)
+       if (r_given)
                pmd.pm_flags |= PM_REVERSE_LOOP;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
@@ -167,70 +161,49 @@ static int com_lsatt_callback(struct afs_callback_arg *aca)
        if (pmd.num_matches == 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_lsatt(struct command_context *cc)
+static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       unsigned flags = 0;
-       struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-       int i;
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-i")) {
-                       flags |= LSATT_FLAG_SORT_BY_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-l")) {
-                       flags |= LSATT_FLAG_LONG;
-                       continue;
-               }
-               if (!strcmp(arg, "-r")) {
-                       flags |= LSATT_FLAG_REVERSE;
-                       continue;
-               }
-       }
-       return send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
-               com_lsatt_callback, afs_cb_result_handler, cc);
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+       return send_lls_callback_request(com_lsatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(lsatt);
 
 struct addatt_event_data {
        const char *name;
        unsigned char bitnum;
 };
 
-
 static int com_addatt_callback(struct afs_callback_arg *aca)
 {
-       char *p;
-       int ret = 1;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+       int i, ret = 1;
        size_t len;
+       unsigned num_inputs;
 
-       for (
-               p = aca->query.data;
-               p < (char *)aca->query.data + aca->query.size;
-               p += len + 1
-       ) {
+       ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+       assert(ret >= 0);
+       num_inputs = lls_num_inputs(aca->lpr);
+       for (i = 0; i < num_inputs; i++) {
+               const char *name = lls_input(i, aca->lpr);
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
                struct addatt_event_data aed;
 
-               len = strlen(p);
-               if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
-                       para_printf(&aca->pbout, "invalid attribute name: %s\n", p);
+               len = strlen(name);
+               if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
+                       para_printf(&aca->pbout,
+                               "invalid attribute name: %s\n", name);
                        continue;
                }
-               ret = get_attribute_bitnum_by_name(p, &bitnum);
+               ret = get_attribute_bitnum_by_name(name, &bitnum);
                if (ret >= 0) {
-                       para_printf(&aca->pbout, "attribute \"%s\" already exists\n", p);
+                       para_printf(&aca->pbout,
+                               "attribute \"%s\" already exists\n", name);
                        continue;
                }
                if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
@@ -251,12 +224,12 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                        ret = -E_ATT_TABLE_FULL;
                        goto out;
                }
-               objs[ATTCOL_NAME].data = p;
+               objs[ATTCOL_NAME].data = (char *)name;
                objs[ATTCOL_NAME].size = len + 1;
                ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
-               aed.name = p;
+               aed.name = name;
                aed.bitnum = bitnum;
                ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
                if (ret < 0)
@@ -265,53 +238,67 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
        }
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "%s: %s\n", p, para_strerror(-ret));
+               para_printf(&aca->pbout, "error while adding %s\n",
+                       lls_input(i, aca->lpr));
        return ret;
 }
 
-int com_addatt(struct command_context *cc)
+static int com_addatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       int ret;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 64, &errctx));
 
-       if (cc->argc < 2)
-               return -E_ATTR_SYNTAX;
-       ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_addatt_callback, afs_cb_result_handler, cc);
-       if (ret < 0)
-               send_strerror(cc, -ret);
-       return ret;
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_addatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(addatt);
 
 static int com_mvatt_callback(struct afs_callback_arg *aca)
 {
-       char *old = aca->query.data;
-       size_t size = strlen(old) + 1;
-       char *new = old + size;
-       struct osl_object obj = {.data = old, .size = size};
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+       const char *old, *new;
+       struct osl_object obj;
        struct osl_row *row;
        int ret;
 
+       ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+       assert(ret >= 0);
+       old = lls_input(0, aca->lpr);
+       new = lls_input(1, aca->lpr);
+       obj.data = (char *)old;
+       obj.size = strlen(old) + 1;
        ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
        if (ret < 0)
                goto out;
-       obj.data = new;
+       obj.data = (char *)new;
        obj.size = strlen(new) + 1;
+       /* The update fails if the destination attribute exists. */
        ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
 out:
        if (ret < 0)
                para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
        else
                ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_mvatt(struct command_context *cc)
+static int com_mvatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc != 3)
-               return -E_ATTR_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_mvatt_callback, afs_cb_result_handler, cc);
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_mvatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(mvatt);
 
 static int remove_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
@@ -336,31 +323,41 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
 
 static int com_rmatt_callback(struct afs_callback_arg *aca)
 {
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
        int ret;
        struct pattern_match_data pmd = {
                .table = attribute_table,
-               .patterns = aca->query,
                .loop_col_num = ATTCOL_BITNUM,
                .match_col_num = ATTCOL_NAME,
                .data = aca,
                .action = remove_attribute
        };
+       ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
        if (pmd.num_matches == 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_rmatt(struct command_context *cc)
+static int com_rmatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_ATTR_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_rmatt_callback, afs_cb_result_handler, cc);
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_rmatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(rmatt);
 
 /**
  * Return a binary representation of the given attribute value.
@@ -479,7 +476,7 @@ int attribute_check_callback(struct afs_callback_arg *aca)
 /**
  * Close the attribute table.
  *
- * \sa osl_close_table().
+ * \sa \ref osl_close_table().
  */
 static void attribute_close(void)
 {
@@ -494,7 +491,7 @@ static void attribute_close(void)
  *
  * \return Positive on success, negative on errors.
  *
- * \sa osl_open_table().
+ * \sa \ref osl_open_table().
  */
 static int attribute_open(const char *dir)
 {
index f8fd80faa77bdea4a4f1139bb8168268ce59ddc3..1d69b2c15f7e6f3fb2683d107f04e738ecfc83cf 100644 (file)
--- a/audioc.c
+++ b/audioc.c
 #include <sys/un.h>
 #include <netdb.h>
 #include <signal.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
+#include "audioc.lsg.h"
 
-#include "audioc.cmdline.h"
 #include "para.h"
 #include "error.h"
 #include "net.h"
 #include "string.h"
 #include "fd.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
-/** The gengetopt structure containing command line args. */
-static struct audioc_args_info conf;
 static char *socket_name;
+static struct lls_parse_result *lpr;
 
+#define CMD_PTR (lls_cmd(0, audioc_suite))
+#define OPT_RESULT(_name) \
+       (lls_opt_result(LSG_AUDIOC_PARA_AUDIOC_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
 
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel);
@@ -41,7 +48,8 @@ static char *concat_args(unsigned argc, char * const *argv)
        char *buf = NULL;
 
        for (i = 0; i < argc; i++) {
-               buf = para_strcat(buf, argv[i]);
+               const char *arg = argv? argv[i] : lls_input(i, lpr);
+               buf = para_strcat(buf, arg);
                if (i != argc - 1)
                        buf = para_strcat(buf, "\n");
        }
@@ -72,7 +80,6 @@ fail:
 #include "sched.h"
 #include "buffer_tree.h"
 #include "interactive.h"
-#include "audiod.completion.h"
 
 static struct sched sched;
 
@@ -100,7 +107,7 @@ static void help_completer(struct i9e_completion_info *ci,
 static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_VERSION_OPTS, NULL};
 
        if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -110,7 +117,7 @@ static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
        char *sia[] = {STATUS_ITEM_ARRAY NULL};
-       char *opts[] = {"-p", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_STAT_OPTS, NULL};
 
        if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -121,12 +128,15 @@ static void stat_completer(struct i9e_completion_info *ci,
 static void grab_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-ms", "-ms", "-ma", "-p=", "-n=", "-o", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_GRAB_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
 static struct i9e_completer audiod_completers[] = {
-       AUDIOD_COMPLETERS
+#define LSG_AUDIOD_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_AUDIOD_CMD_SUBCOMMANDS
+#undef LSG_AUDIOD_CMD_CMD
        {.name = NULL}
 };
 
@@ -145,13 +155,15 @@ static int audioc_post_select(struct sched *s, void *context)
        char *buf = NULL;
        struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
+       size_t bufsize;
 
        if (ret < 0)
                goto out;
        if (!FD_ISSET(at->fd, &s->rfds))
                return 0;
-       buf = para_malloc(conf.bufsize_arg);
-       ret = recv_bin_buffer(at->fd, buf, conf.bufsize_arg);
+       bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
+       buf = para_malloc(bufsize);
+       ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
                ret = -E_AUDIOC_EOF;
@@ -172,28 +184,26 @@ static struct audioc_task audioc_task, *at = &audioc_task;
 
 static int audioc_i9e_line_handler(char *line)
 {
-       char *args = NULL;
-       int ret;
+       int argc, ret;
+       char *args, **argv;
 
        PARA_DEBUG_LOG("line: %s\n", line);
-       ret = create_argv(line, " ", &conf.inputs);
+       ret = create_argv(line, " ", &argv);
        if (ret < 0)
                return ret;
-       conf.inputs_num = ret;
-       args = concat_args(conf.inputs_num, conf.inputs);
-       free_argv(conf.inputs);
+       argc = ret;
+       args = concat_args(argc, argv);
+       free_argv(argv);
        if (!args)
                return 0;
-       conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */
        ret = connect_audiod(socket_name, args);
+       free(args);
        if (ret < 0)
-               goto out;
+               return ret;
        at->fd = ret;
        ret = mark_fd_nonblocking(at->fd);
        if (ret < 0)
                goto close;
-       free(args);
-       args = NULL;
        at->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "audioc line handler"));
        at->task = task_register(&(struct task_info) {
@@ -206,8 +216,6 @@ static int audioc_i9e_line_handler(char *line)
        return 1;
 close:
        close(at->fd);
-out:
-       free(args);
        return ret;
 }
 
@@ -223,9 +231,10 @@ __noreturn static void interactive_session(void)
                .loglevel = loglevel,
                .completers = audiod_completers,
        };
+
        PARA_NOTICE_LOG("\n%s\n", version_text("audioc"));
-       if (conf.history_file_given)
-               history_file = para_strdup(conf.history_file_arg);
+       if (OPT_GIVEN(HISTORY_FILE))
+               history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
        else {
                char *home = para_homedir();
                history_file = make_message("%s/.paraslash/audioc.history",
@@ -251,7 +260,7 @@ __noreturn static void interactive_session(void)
        para_log = stderr_log;
 out:
        free(history_file);
-       audioc_cmdline_parser_free(&conf);
+       free(socket_name);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
@@ -293,13 +302,19 @@ static char *configfile_exists(void)
        return NULL;
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(audioc);
-       bool d = conf.detailed_help_given;
+       char *help;
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 /**
@@ -316,58 +331,83 @@ __noreturn static void print_help_and_die(void)
  *
  * \return EXIT_SUCCESS or EXIT_FAILURE.
  *
- * \sa send_cred_buffer(), para_audioc(1), para_audiod(1).
+ * \sa \ref send_cred_buffer(), para_audioc(1), para_audiod(1).
  */
 int main(int argc, char *argv[])
 {
+       const struct lls_command *cmd = CMD_PTR;
        int ret, fd;
-       char *cf, *buf = NULL, *args = NULL;
+       char *cf = NULL, *buf, *args, *errctx = NULL;
        size_t bufsize;
+       struct lls_parse_result *lpr1, *lpr2, *lpr3;
+       unsigned num_inputs;
 
-       audioc_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("audioc", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
+       ret = lls(lls_parse(argc, argv, cmd, &lpr1, &errctx));
+       if (ret < 0)
+               goto fail;
+       lpr = lpr1;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("audioc", OPT_GIVEN(VERSION));
+       handle_help_flag();
        cf = configfile_exists();
        if (cf) {
-               struct audioc_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1,
-
-               };
-               audioc_cmdline_parser_config_file(cf, &conf, &params);
-               free(cf);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
+               void *map;
+               size_t sz;
+               int cf_argc;
+               char **cf_argv;
+               ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+               if (ret != -E_EMPTY) {
+                       if (ret < 0)
+                               goto out;
+                       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
+                               &errctx));
+                       para_munmap(map, sz);
+                       if (ret < 0) {
+                               PARA_ERROR_LOG("syntax error in %s\n", cf);
+                               goto out;
+                       }
+                       cf_argc = ret;
+                       ret = lls(lls_parse(cf_argc, cf_argv, cmd, &lpr2,
+                               &errctx));
+                       lls_free_argv(cf_argv);
+                       if (ret < 0) {
+                               PARA_ERROR_LOG("parse error in %s\n", cf);
+                               goto out;
+                       }
+                       ret = lls(lls_merge(lpr1, lpr2, cmd, &lpr3, &errctx));
+                       lls_free_parse_result(lpr2, cmd);
+                       if (ret < 0)
+                               goto out;
+                       lls_free_parse_result(lpr1, cmd);
+                       lpr = lpr3;
+                       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+               }
        }
-       if (conf.socket_given)
-               socket_name = para_strdup(conf.socket_arg);
+       if (OPT_GIVEN(COMPLETE))
+               print_completions();
+       if (OPT_GIVEN(SOCKET))
+               socket_name = para_strdup(OPT_STRING_VAL(SOCKET));
        else {
                char *hn = para_hostname();
                socket_name = make_message("/var/paraslash/audiod_socket.%s",
                        hn);
                free(hn);
        }
-
-       if (conf.complete_given)
-               print_completions();
-
-       if (conf.inputs_num == 0)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                interactive_session();
-       args = concat_args(conf.inputs_num, conf.inputs);
 
+       args = concat_args(num_inputs, NULL);
        ret = connect_audiod(socket_name, args);
        free(socket_name);
+       free(args);
        if (ret < 0)
                goto out;
        fd = ret;
        ret = mark_fd_blocking(STDOUT_FILENO);
        if (ret < 0)
                goto out;
-       bufsize = conf.bufsize_arg;
+       bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
        buf = para_malloc(bufsize);
        do {
                size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
@@ -375,9 +415,14 @@ int main(int argc, char *argv[])
                        break;
                ret = write_all(STDOUT_FILENO, buf, n);
        } while (ret >= 0);
-out:
        free(buf);
-       free(args);
+out:
+       lls_free_parse_result(lpr, cmd);
+       free(cf);
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
index 32b6895dcf0da9393538e649f7241878b89e96c0..defd673d20ba0029aaa228e7d23545fd11a5ce54 100644 (file)
--- a/audiod.c
+++ b/audiod.c
 #include <netdb.h>
 #include <signal.h>
 #include <pwd.h>
+#include <lopsub.h>
 
+#include "audiod.lsg.h"
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
-#include "audiod.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
 #include "filter.h"
 #include "grab_client.h"
-#include "client.cmdline.h"
 #include "client.h"
 #include "audiod.h"
 #include "net.h"
 #include "string.h"
 #include "fd.h"
 #include "write.h"
-#include "write_common.h"
 #include "signal.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *lpr;
+#define CMD_PTR (lls_cmd(0, audiod_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+       lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
+
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 /** define the array containing all supported audio formats */
 const char *audio_formats[] = {AUDIOD_AUDIO_FORMAT_ARRAY NULL};
 
-DEFINE_RECEIVER_ARRAY;
-
 /** Defines how audiod handles one supported audio format. */
 struct audio_format_info {
-       /** pointer to the receiver for this audio format */
-       struct receiver *receiver;
-       /** the receiver configuration */
-       void *receiver_conf;
+       /** the receiver for this audio format */
+       int receiver_num;
+       /** Parsed receiver command line. */
+       struct lls_parse_result *receiver_lpr;
        /** the number of filters that should be activated for this audio format */
        unsigned int num_filters;
        /** Array of filter numbers to be activated. */
        unsigned *filter_nums;
        /** Pointer to the array of filter configurations. */
        void **filter_conf;
+       /** Parsed filter command line, one parse result per filter. */
+       struct lls_parse_result **filter_lpr;
        /** the number of filters that should be activated for this audio format */
        unsigned int num_writers;
-       /** Array of writer numbers to be activated. */
-       int *writer_nums;
-       /** pointer to the array of writer configurations */
-       void **writer_conf;
+       /** Array of writer IDs to be activated. */
+       int *wids;
+       /** Parsed writer command line(s) */
+       struct lls_parse_result **writer_lpr;
        /** do not start receiver/filters/writer before this time */
        struct timeval restart_barrier;
 };
@@ -88,6 +96,9 @@ struct slot_info {
        struct writer_node *wns;
 };
 
+#define RECEIVER_CMD(_a) lls_cmd((_a)->receiver_num, recv_cmd_suite)
+#define RECEIVER(_a) ((const struct receiver *)lls_user_data(RECEIVER_CMD(_a)))
+
 /** Maximal number of simultaneous instances. */
 #define MAX_STREAM_SLOTS 5
 
@@ -98,9 +109,7 @@ struct slot_info {
  * 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
  * information on the status of these slots.
- *
- * \sa struct slot_info
- * */
+ */
 struct slot_info slot[MAX_STREAM_SLOTS];
 
 /** The vss status flags audiod is interested in. */
@@ -156,54 +165,32 @@ struct status_task {
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
 /**
- * the current mode of operation of which can be changed by the on/off/cycle
- * commands. It is either, AUDIOD_OFF, AUDIOD_ON or AUDIOD_STANDBY.
+ * The current mode of operation (AUDIOD_OFF, AUDIOD_ON or AUDIOD_STANDBY).
+ * Set by the on/off/cycle commands.
  */
 int audiod_status = AUDIOD_ON;
 
-/**
- * the gengetopt args_info struct that holds information on all command line
- * arguments
- */
-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;
-
 static struct status_task status_task_struct;
-
 static uid_t *uid_whitelist;
 
 /**
- * the task that calls the status command of para_server
+ * The task that calls the status command of para_server.
  *
- * \sa struct status_task
+ * \sa \ref struct status_task.
  */
 static struct status_task *stat_task = &status_task_struct;
 
-/*
- * 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.
- *
- * 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 sockets. */
-       int fd[2];
-       /** the associated task structure */
+       /** The local listening socket. */
+       int fd;
+       /** The associated task structure. */
        struct task *task;
 };
 
-/** iterate over all supported audio formats */
+/** Iterate over all supported audio formats. */
 #define FOR_EACH_AUDIO_FORMAT(af) for (af = 0; af < NUM_AUDIO_FORMATS; af++)
 
 /**
@@ -395,58 +382,82 @@ empty:
 
 static void parse_config_or_die(void)
 {
-       int ret, i;
-       char *config_file;
-       struct audiod_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 0,
-               .check_required = 1,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
-
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       int ret;
+       char *cf, *errctx = NULL;
+       void *map;
+       size_t sz;
+
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/audiod.conf", home);
+               cf = make_message("%s/.paraslash/audiod.conf", home);
                free(home);
        }
-       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) {
-               audiod_cmdline_parser_config_file(config_file, &conf, &params);
-               daemon_set_loglevel(conf.loglevel_arg);
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+                       goto free_cf;
+       } else {
+               int cf_argc;
+               char **cf_argv;
+               struct lls_parse_result *cf_lpr, *merged_lpr;
+               ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+               para_munmap(map, sz);
+               if (ret < 0)
+                       goto free_cf;
+               cf_argc = ret;
+               ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+               lls_free_argv(cf_argv);
+               if (ret < 0)
+                       goto free_cf;
+               ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+               lls_free_parse_result(cf_lpr, CMD_PTR);
+               if (ret < 0)
+                       goto free_cf;
+               lls_free_parse_result(lpr, CMD_PTR);
+               lpr = merged_lpr;
        }
-       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++) {
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       if (OPT_GIVEN(USER_ALLOW)) {
+               uint32_t n = OPT_GIVEN(USER_ALLOW);
+               int i;
+
+               uid_whitelist = para_malloc(n * sizeof(uid_t));
+               for (i = 0; i < n; i++) {
+                       const char *arg = lls_string_val(i,
+                               OPT_RESULT(USER_ALLOW));
                        int32_t val;
                        struct passwd *pw;
-                       ret = para_atoi32(conf.user_allow_arg[i], &val);
+                       ret = para_atoi32(arg, &val);
                        if (ret >= 0) {
                                uid_whitelist[i] = val;
                                continue;
                        }
                        errno = 0; /* see getpwnam(3) */
-                       pw = getpwnam(conf.user_allow_arg[i]);
+                       pw = getpwnam(arg);
                        if (!pw) {
-                               PARA_EMERG_LOG("invalid username: %s\n",
-                                       conf.user_allow_arg[i]);
-                               goto err;
+                               PARA_EMERG_LOG("invalid username: %s\n", arg);
+                               free(uid_whitelist);
+                               goto free_cf;
                        }
                        uid_whitelist[i] = pw->pw_uid;
                }
        }
-       return;
-err:
-       exit(EXIT_FAILURE);
+       ret = 0;
+free_cf:
+       free(cf);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               lls_free_parse_result(lpr, CMD_PTR);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
 }
 
 static void setup_signal_handling(void)
@@ -477,7 +488,7 @@ static void close_receiver(int slot_num)
        a = &afi[s->format];
        PARA_NOTICE_LOG("closing %s receiver in slot %d\n",
                audio_formats[s->format], slot_num);
-       a->receiver->close(s->receiver_node);
+       RECEIVER(a)->close(s->receiver_node);
        btr_remove_node(&s->receiver_node->btrn);
        task_reap(&s->receiver_node->task);
        free(s->receiver_node);
@@ -489,13 +500,10 @@ static void close_receiver(int slot_num)
 
 static void writer_cleanup(struct writer_node *wn)
 {
-       struct writer *w;
-
        if (!wn)
                return;
-       w = writers + wn->writer_num;
-       PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]);
-       w->close(wn);
+       PARA_INFO_LOG("closing %s\n", writer_name(wn->wid));
+       writer_get(wn->wid)->close(wn);
        btr_remove_node(&wn->btrn);
        task_reap(&wn->task);
 }
@@ -590,17 +598,20 @@ static void open_filters(struct slot_info *s)
        parent = s->receiver_node->btrn;
        for (i = 0; i < nf; i++) {
                char buf[20];
+               const char *name;
                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->lpr = a->filter_lpr[i];
+               name = filter_name(fn->filter_num);
                fn->btrn = btr_new_node(&(struct btr_node_description)
-                       EMBRACE(.name = f->name, .parent = parent,
+                       EMBRACE(.name = name, .parent = parent,
                                .handler = f->execute, .context = fn));
 
                if (f->open)
                        f->open(fn);
-               sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot));
+               sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
                fn->task = task_register(&(struct task_info) {
                        .name = buf,
                        .pre_select = f->pre_select,
@@ -609,7 +620,7 @@ static void open_filters(struct slot_info *s)
                }, &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));
+                       audio_formats[s->format], i,  nf, name, (int)(s - slot));
        }
 }
 
@@ -625,11 +636,11 @@ static void open_writers(struct slot_info *s)
                * sizeof(struct writer_node));
        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];
+               wn->wid = a->wids[i];
+               wn->lpr = a->writer_lpr[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));
+                       writer_name(a->wids[i]), (int)(s - slot));
        }
 }
 
@@ -639,7 +650,8 @@ static int open_receiver(int format)
        struct audio_format_info *a = &afi[format];
        struct slot_info *s;
        int ret, slot_num;
-       struct receiver *r = a->receiver;
+       const struct receiver *r = RECEIVER(a);
+       const char *name = lls_command_name(RECEIVER_CMD(a));
        struct receiver_node *rn;
 
        tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier);
@@ -649,9 +661,9 @@ static int open_receiver(int format)
        slot_num = ret;
        rn = para_calloc(sizeof(*rn));
        rn->receiver = r;
-       rn->conf = a->receiver_conf;
+       rn->lpr = a->receiver_lpr;
        rn->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = r->name, .context = rn));
+               EMBRACE(.name = name, .context = rn));
        ret = r->open(rn);
        if (ret < 0) {
                btr_remove_node(&rn->btrn);
@@ -662,9 +674,9 @@ static int open_receiver(int format)
        s->format = format;
        s->receiver_node = rn;
        PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n",
-               audio_formats[format], r->name, slot_num);
+               audio_formats[format], name, slot_num);
        rn->task = task_register(&(struct task_info) {
-               .name = r->name,
+               .name = name,
                .pre_select = r->pre_select,
                .post_select = r->post_select,
                .context = rn,
@@ -827,7 +839,7 @@ static int update_item(int itemnum, char *buf)
        return 1;
 }
 
-static int parse_stream_command(const char *txt, char **cmd)
+static int parse_stream_command(const char *txt, const char **cmd)
 {
        int ret, len;
        char *re, *p = strchr(txt, ':');
@@ -844,38 +856,41 @@ static int parse_stream_command(const char *txt, char **cmd)
        return ret;
 }
 
-static int add_filter(int format, char *cmdline)
+static int add_filter(int format, const char *cmdline)
 {
        struct audio_format_info *a = &afi[format];
        int filter_num, nf = a->num_filters;
        void *cfg;
+       struct lls_parse_result *flpr;
 
-       filter_num = check_filter_arg(cmdline, &cfg);
-       if (filter_num < 0)
-               return filter_num;
+       filter_num = filter_setup(cmdline, &cfg, &flpr);
+       a->filter_lpr = para_realloc(a->filter_lpr,
+               (nf + 1) * sizeof(flpr));
        a->filter_conf = para_realloc(a->filter_conf,
                (nf + 1) * sizeof(void *));
        a->filter_nums = para_realloc(a->filter_nums,
                (nf + 1) * sizeof(unsigned));
+
        a->filter_nums[nf] = filter_num;
        a->filter_conf[nf] = cfg;
+       a->filter_lpr[nf] = flpr;
        a->num_filters++;
        PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf,
-               filter_get(filter_num)->name);
+               filter_name(filter_num));
        return filter_num;
 }
 
 static int parse_writer_args(void)
 {
        int i, ret;
-       char *cmd;
+       const char *cmd;
        struct audio_format_info *a;
 
-       for (i = 0; i < conf.writer_given; i++) {
-               void *wconf;
-               int j, nw, writer_num, af_mask;
+       for (i = 0; i < OPT_GIVEN(WRITER); i++) {
+               int j, nw, af_mask;
 
-               ret = parse_stream_command(conf.writer_arg[i], &cmd);
+               ret = parse_stream_command(lls_string_val(i,
+                       OPT_RESULT(WRITER)), &cmd);
                if (ret < 0)
                        return ret;
                af_mask = ret;
@@ -883,47 +898,45 @@ static int parse_writer_args(void)
                        a = afi + j;
                        if ((af_mask & (1 << j)) == 0) /* no match */
                                continue;
-                       wconf = check_writer_arg_or_die(cmd, &writer_num);
                        nw = a->num_writers;
-                       a->writer_nums = para_realloc(a->writer_nums, (nw + 1) * sizeof(int));
-                       a->writer_conf = para_realloc(a->writer_conf, (nw + 1) * sizeof(void *));
-                       a->writer_nums[nw] = writer_num;
-                       a->writer_conf[nw] = wconf;
+                       a->wids = para_realloc(a->wids, (nw + 1) * sizeof(int));
+                       a->writer_lpr = para_realloc(a->writer_lpr,
+                               (nw + 1) * sizeof(struct lls_parse_result *));
+                       a->wids[nw] = check_writer_arg_or_die(cmd,
+                               a->writer_lpr + nw);
                        PARA_INFO_LOG("%s writer #%d: %s\n", audio_formats[j],
-                               nw, writer_names[writer_num]);
+                               nw, writer_name(a->wids[nw]));
                        a->num_writers++;
                }
        }
        /* Use default writer for audio formats which are not yet set up. */
        FOR_EACH_AUDIO_FORMAT(i) {
-               void *writer_conf;
-               int writer_num;
                a = afi + i;
                if (a->num_writers > 0)
                        continue; /* already set up */
-               writer_conf = check_writer_arg_or_die(NULL, &writer_num);
-               a->writer_nums = para_malloc(sizeof(int));
-               a->writer_nums[0] = writer_num;
-               a->writer_conf = para_malloc(sizeof(void *));
-               a->writer_conf[0] = writer_conf;
                a->num_writers = 1;
+               a->wids = para_malloc(sizeof(int));
+               a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+               a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
                PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
-                       writer_names[writer_num]);
+                       writer_name(a->wids[0]));
        }
        return 1;
 }
 
 static int parse_receiver_args(void)
 {
-       int i, ret, receiver_num;
-       char *cmd = NULL;
+       int i, ret;
+       const char *arg;
        struct audio_format_info *a;
 
-       for (i = conf.receiver_given - 1; i >= 0; i--) {
-               char *arg;
+       FOR_EACH_AUDIO_FORMAT(i)
+               afi[i].receiver_num = -1;
+       for (i = OPT_GIVEN(RECEIVER) - 1; i >= 0; i--) {
                int j, af_mask;
 
-               ret = parse_stream_command(conf.receiver_arg[i], &arg);
+               ret = parse_stream_command(lls_string_val(i,
+                       OPT_RESULT(RECEIVER)), &arg);
                if (ret < 0)
                        goto out;
                af_mask = ret;
@@ -937,37 +950,27 @@ static int parse_receiver_args(void)
                         * config here. Since we are iterating backwards, the winning
                         * receiver arg is in fact the first one given.
                         */
-                       if (a->receiver_conf)
-                               a->receiver->free_config(a->receiver_conf);
-                       a->receiver_conf = check_receiver_arg(arg, &receiver_num);
-                       ret = -E_RECV_SYNTAX;
-                       if (!a->receiver_conf)
-                               goto out;
-                       a->receiver = receivers + receiver_num;
+                       lls_free_parse_result(a->receiver_lpr, RECEIVER_CMD(a));
+                       a->receiver_num = check_receiver_arg(arg, &a->receiver_lpr);
                }
        }
        /*
-        * Use the first available receiver with no arguments for those audio
-        * formats for which no receiver was specified.
+        * Use the default receiver for those audio formats for which no
+        * receiver was specified.
         */
-       cmd = para_strdup(receivers[0].name);
        FOR_EACH_AUDIO_FORMAT(i) {
-               a = &afi[i];
-               if (a->receiver_conf)
+               a = afi + i;
+               if (a->receiver_num >= 0)
                        continue;
-               a->receiver_conf = check_receiver_arg(cmd, &receiver_num);
-               if (!a->receiver_conf)
-                       return -E_RECV_SYNTAX;
-               a->receiver = &receivers[receiver_num];
+               a->receiver_num = check_receiver_arg(NULL, &a->receiver_lpr);
        }
        FOR_EACH_AUDIO_FORMAT(i) {
                a = afi + i;
                PARA_INFO_LOG("receiving %s streams via %s receiver\n",
-                       audio_formats[i], a->receiver->name);
+                       audio_formats[i], lls_command_name(RECEIVER_CMD(a)));
        }
        ret = 1;
 out:
-       free(cmd);
        return ret;
 }
 
@@ -977,6 +980,7 @@ static int init_default_filters(void)
 
        FOR_EACH_AUDIO_FORMAT(i) {
                struct audio_format_info *a = &afi[i];
+               const char *name = lls_command_name(RECEIVER_CMD(a));
                char *tmp;
                int j;
 
@@ -986,8 +990,7 @@ static int init_default_filters(void)
                 * udp and dccp streams are fec-encoded, so add fecdec as the
                 * first filter.
                 */
-               if (strcmp(afi[i].receiver->name, "udp") == 0 ||
-                               strcmp(afi[i].receiver->name, "dccp") == 0) {
+               if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
                        tmp = para_strdup("fecdec");
                        add_filter(i, tmp);
                        free(tmp);
@@ -996,20 +999,20 @@ static int init_default_filters(void)
                }
                /* add "dec" to audio format name */
                tmp = make_message("%sdec", audio_formats[i]);
-               for (j = 0; filter_get(j); j++)
-                       if (!strcmp(tmp, filter_get(j)->name))
+               for (j = 1; filter_get(j); j++)
+                       if (!strcmp(tmp, filter_name(j)))
                                break;
                free(tmp);
                ret = -E_UNSUPPORTED_FILTER;
                if (!filter_get(j))
                        goto out;
-               tmp = para_strdup(filter_get(j)->name);
+               tmp = para_strdup(filter_name(j));
                ret = add_filter(i, tmp);
                free(tmp);
                if (ret < 0)
                        goto out;
                PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i],
-                       filter_get(j)->name);
+                       filter_name(j));
        }
 out:
        return ret;
@@ -1019,9 +1022,10 @@ static int parse_filter_args(void)
 {
        int i, j, ret, af_mask, num_matches;
 
-       for (i = 0; i < conf.filter_given; i++) {
-               char *arg;
-               ret = parse_stream_command(conf.filter_arg[i], &arg);
+       for (i = 0; i < OPT_GIVEN(FILTER); i++) {
+               const char *arg;
+               ret = parse_stream_command(lls_string_val(i,
+                       OPT_RESULT(FILTER)), &arg);
                if (ret < 0)
                        goto out;
                af_mask = ret;
@@ -1036,7 +1040,7 @@ static int parse_filter_args(void)
                }
                if (num_matches == 0)
                        PARA_WARNING_LOG("ignoring filter spec: %s\n",
-                               conf.filter_arg[i]);
+                               lls_string_val(i, OPT_RESULT(FILTER)));
        }
        ret = init_default_filters(); /* use default values for the rest */
 out:
@@ -1060,10 +1064,10 @@ static int parse_stream_args(void)
 }
 
 /* does not unlink socket on errors */
-static void init_local_sockets(struct command_task *ct)
+static void init_local_socket(struct command_task *ct)
 {
-       if (conf.socket_given)
-               socket_name = para_strdup(conf.socket_arg);
+       if (OPT_GIVEN(SOCKET))
+               socket_name = para_strdup(OPT_STRING_VAL(SOCKET));
        else {
                char *hn = para_hostname();
                socket_name = make_message("/var/paraslash/audiod_socket.%s",
@@ -1071,14 +1075,12 @@ static void init_local_sockets(struct command_task *ct)
                free(hn);
        }
        PARA_NOTICE_LOG("local socket: %s\n", socket_name);
-       if (conf.force_given)
+       if (OPT_GIVEN(FORCE))
                unlink(socket_name);
-       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)
+       ct->fd = create_local_socket(socket_name);
+       if (ct->fd >= 0)
                return;
-       PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd[1]));
+       PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd));
        exit(EXIT_FAILURE);
 }
 
@@ -1105,16 +1107,12 @@ static int signal_post_select(struct sched *s, void *context)
 static void command_pre_select(struct sched *s, void *context)
 {
        struct command_task *ct = context;
-       int i;
-
-       for (i = 0; i < 2; i++)
-               if (ct->fd[i] >= 0)
-                       para_fd_set(ct->fd[i], &s->rfds, &s->max_fileno);
+       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
 }
 
 static int command_post_select(struct sched *s, void *context)
 {
-       int ret, i;
+       int ret;
        struct command_task *ct = context;
        static struct timeval last_status_dump;
        struct timeval tmp, delay;
@@ -1123,19 +1121,15 @@ static int command_post_select(struct sched *s, void *context)
        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;
-       }
+       ret = handle_connect(ct->fd, &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;
 
@@ -1163,7 +1157,7 @@ dump:
 
 static void init_command_task(struct command_task *ct)
 {
-       init_local_sockets(ct); /* doesn't return on errors */
+       init_local_socket(ct); /* doesn't return on errors */
 
        ct->task = task_register(&(struct task_info) {
                .name = "command",
@@ -1256,7 +1250,6 @@ static void audiod_cleanup(void)
                unlink(socket_name);
        close_stat_pipe();
        close_unused_slots();
-       audiod_cmdline_parser_free(&conf);
        close_stat_clients();
        free(uid_whitelist);
 }
@@ -1337,7 +1330,7 @@ static int status_post_select(struct sched *s, void *context)
                        goto out;
                }
                close_stat_pipe();
-               st->clock_diff_count = conf.clock_diff_count_arg;
+               st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
                goto out;
        }
        if (st->ct) {
@@ -1405,7 +1398,7 @@ static void init_status_task(struct status_task *st)
 {
        memset(st, 0, sizeof(struct status_task));
        st->sa_time_diff_sign = 1;
-       st->clock_diff_count = conf.clock_diff_count_arg;
+       st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
        st->current_audio_format_num = -1;
        st->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stat"));
@@ -1421,36 +1414,20 @@ static void init_status_task(struct status_task *st)
 static void set_initial_status(void)
 {
        audiod_status = AUDIOD_ON;
-       if (!conf.mode_given)
+       if (!OPT_GIVEN(MODE))
                return;
-       if (!strcmp(conf.mode_arg, "sb")) {
+       if (!strcmp(OPT_STRING_VAL(MODE), "sb")) {
                audiod_status = AUDIOD_STANDBY;
                return;
        }
-       if (!strcmp(conf.mode_arg, "off")) {
+       if (!strcmp(OPT_STRING_VAL(MODE), "off")) {
                audiod_status = AUDIOD_OFF;
                return;
        }
-       if (strcmp(conf.mode_arg, "on"))
+       if (strcmp(OPT_STRING_VAL(MODE), "on"))
                PARA_WARNING_LOG("invalid mode\n");
 }
 
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(audiod);
-       bool d = conf.detailed_help_given;
-       unsigned flags;
-
-       flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
-       ggo_print_help(&h, flags);
-
-       flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
-       print_receiver_helps(flags);
-       print_filter_helps(flags);
-       print_writer_helps(flags);
-       exit(0);
-}
-
 /**
  * Lookup the given UID in the whitelist.
  *
@@ -1466,14 +1443,33 @@ bool uid_is_whitelisted(uid_t uid)
 {
        int i;
 
-       if (!conf.user_allow_given)
+       if (!OPT_GIVEN(USER_ALLOW))
                return true;
-       for (i = 0; i < conf.user_allow_given; i++)
+       for (i = 0; i < OPT_GIVEN(USER_ALLOW); i++)
                if (uid == uid_whitelist[i])
                        return true;
        return false;
 }
 
+static void handle_help_flags(void)
+{
+       char *help;
+       bool d = OPT_GIVEN(DETAILED_HELP);
+
+       if (d)
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_receiver_helps(d);
+       print_filter_helps(d);
+       print_writer_helps(d);
+       exit(EXIT_SUCCESS);
+}
+
 /**
  * the main function of para_audiod
  *
@@ -1488,40 +1484,34 @@ int main(int argc, char *argv[])
 {
        int ret, i;
        struct command_task command_task_struct, *cmd_task = &command_task_struct;
-       struct audiod_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
+       char *errctx;
 
        valid_fd_012();
-       audiod_cmdline_parser_ext(argc, argv, &conf, &params);
-       daemon_set_loglevel(conf.loglevel_arg);
-       version_handle_flag("audiod", conf.version_given);
-       /* init receivers/filters/writers early to make help work */
-       recv_init();
-       filter_init();
-       writer_init();
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       daemon_set_priority(conf.priority_arg);
-       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+               OPT_STRING_VAL(GROUP));
+       version_handle_flag("audiod", OPT_GIVEN(VERSION));
+       handle_help_flags();
        parse_config_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_priority(OPT_UINT32_VAL(PRIORITY));
+       recv_init();
+       if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+                       COLOR_NO, OPT_GIVEN(LOGFILE))) {
+               for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+                       daemon_set_log_color_or_die(lls_string_val(i,
+                               OPT_RESULT(LOG_COLOR)));
+       }
        daemon_set_flag(DF_LOG_TIME);
        daemon_set_flag(DF_LOG_HOSTNAME);
        daemon_set_flag(DF_LOG_LL);
-       if (conf.log_timing_given)
+       if (OPT_GIVEN(LOG_TIMING))
                daemon_set_flag(DF_LOG_TIMING);
-       if (conf.logfile_given) {
-               daemon_set_logfile(conf.logfile_arg);
+       if (OPT_GIVEN(LOGFILE)) {
+               daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        }
        ret = parse_stream_args();
@@ -1539,7 +1529,7 @@ int main(int argc, char *argv[])
        init_status_task(stat_task);
        init_command_task(cmd_task);
 
-       if (conf.daemon_given)
+       if (OPT_GIVEN(DAEMON))
                daemonize(false /* parent exits immediately */);
 
        signal_task->task = task_register(&(struct task_info) {
@@ -1556,6 +1546,10 @@ int main(int argc, char *argv[])
        sched_shutdown(&sched);
        signal_shutdown(signal_task);
 
+out:
+       lls_free_parse_result(lpr, CMD_PTR);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
        if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/audiod.cmd b/audiod.cmd
deleted file mode 100644 (file)
index 18c802d..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-BN: audiod
-SF: audiod_command.c
-SN: list of audiod commands
----
-N: cycle
-D: switch to next mode
-U: cycle
-H: on -> standby -> off -> on
----
-N: grab
-D: grab the audio stream
-L:
-U: -- grab [-m[{s|p|a}]] [-p=PARENT] [-n=NAME] [-o]
-H:
-H: grab ('splice') the audio stream at any position in the buffer
-H: tree and send that data back to the client.
-H:
-H: Options:
-H:
-H: -m  Change grab mode. Defaults to sloppy grab if not given.
-H:
-H:    -ms: sloppy grab
-H:    -mp: pedantic grab
-H:    -ma: aggressive grab
-H:
-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:
-H: -n  Name of the new buffer tree node. Defaults to 'grab'.
-H:
-H: -o  One-shot mode: Stop grabbing if audio file changes.
----
-N: help
-D: display command list or help for given command
-U: help [command]
-H: When I was younger, so much younger than today, I never needed anybody's help
-H: in any way. But now these days are gone, I'm not so self assured. Now I find
-H: I've changed my mind and opened up the doors.
-H:                                                              -- Beatles: Help
----
-N: off
-D: deactivate para_audiod
-U: off
-H: Close connection to para_server and stop all decoders.
----
-N: on
-D: activate para_audiod
-U: on
-H: Establish connection to para_server, retrieve para_server's current status. If
-H: playing, start corresponding decoder. Otherwise stop all decoders.
----
-N: sb
-D: enter standby mode
-U: sb
-H: Stop all decoders but leave connection to para_server open.
----
-N: stat
-D: print status information
-U: stat [-p] [item1 ...]
-H: Dump given status items (all if none given) to stdout. If -p is given, use
-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.
----
-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 7073c6dd2108fb31b915724fdbed431d76d51e63..295a02bc44ddb339932539d276f570f3ad785b10 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -13,15 +13,6 @@ enum {AUDIOD_AUDIO_FORMATS_ENUM};
 /** array of audio format names supported by para_audiod */
 extern const char *audio_formats[];
 
-/**
- * the possible modes of operation
- *
- * - off: disconnect from para_server
- * - on: receive status information from para_server and play the audio stream
- * - sb: only receive status information but not the audio stream
- */
-enum audiod_status_info {AUDIOD_OFF, AUDIOD_ON, AUDIOD_STANDBY};
-
 extern int audiod_status;
 
 /* defined in audiod.c */
index 3a39027523a72c686d997d1b358912dc5dfd2363..cccc2a8908d933d0d5ef2f41bb6b79dc11602baf 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "audiod.lsg.h"
 #include "para.h"
-#include "audiod.cmdline.h"
-#include "audiod.command_list.h"
+#include "audiod_cmd.lsg.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.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;
-};
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
 
-static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
 
-/** Iterate over the array of all audiod commands. */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+       audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
 
-/** The maximal number of simultaneous connections. */
-#define MAX_STAT_CLIENTS 50
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+       /** Implementation of _cmd. */ \
+       const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 /** Flags used for the stat command of para_audiod. */
 enum stat_client_flags {
@@ -245,10 +233,11 @@ static int dump_commands(int fd)
        char *buf = para_strdup(""), *tmp = NULL;
        int i;
        ssize_t ret;
+       const struct lls_command *cmd;
 
-       FOR_EACH_COMMAND(i) {
-               tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
-                       audiod_cmds[i].description);
+       for (i = 1; (cmd = lls_cmd(i, audiod_cmd_suite)); i++) {
+               tmp = make_message("%s%s\t%s\n", buf, lls_command_name(cmd),
+                       lls_purpose(cmd));
                free(buf);
                buf = tmp;
        }
@@ -257,76 +246,80 @@ static int dump_commands(int fd)
        return ret;
 }
 
-static int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret;
-       char *buf;
-
-       if (argc < 2) {
-               ret = dump_commands(fd);
-               goto out;
+       int ret;
+       char *buf, *errctx;
+       const struct lls_command *cmd;
+
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx) {
+                       buf = make_message("%s\n", errctx);
+                       client_write(fd, buf);
+                       free(buf);
+                       free(errctx);
+               }
+               return ret;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[1]))
-                       continue;
-               buf = make_message(
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\tpara_audioc %s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       audiod_cmds[i].description,
-                       audiod_cmds[i].usage,
-                       audiod_cmds[i].help
-               );
-               ret = client_write(fd, buf);
+       if (lls_num_inputs(lpr) == 0)
+               return dump_commands(fd);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), audiod_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               buf = make_message("%s: %s\nAvailable commands:\n", errctx?
+                       errctx : lls_input(0, lpr), para_strerror(-ret));
+               if (client_write(fd, buf) >= 0)
+                       dump_commands(fd);
+               free(errctx);
                free(buf);
                goto out;
        }
-       ret = client_write(fd, "No such command. Available commands:\n");
-       if (ret > 0)
-               ret = dump_commands(fd);
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       ret = client_write(fd, buf);
+       free(buf);
 out:
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(help)
 
-static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
+       int ret;
        char *tl = get_task_list(&sched);
-       int ret = 1;
 
-       if (tl)
-               ret = client_write(fd, tl);
+       if (!tl) /* no tasks registered yet */
+               return 0;
+       ret = client_write(fd, tl);
        free(tl);
-       return ret < 0? ret : 0;
+       return ret;
 }
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
-static int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
+       const struct lls_opt_result *r;
+       unsigned num_inputs;
 
        ret = mark_fd_nonblocking(fd);
        if (ret < 0)
                return ret;
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-p", 2)) {
-                       parser_friendly = 1;
-                       b.flags = PBF_SIZE_PREFIX;
-               }
+       r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+       if (lls_opt_given(r) > 0) {
+               parser_friendly = 1;
+               b.flags = PBF_SIZE_PREFIX;
        }
-       if (i >= argc)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                mask--; /* set all bits */
-       for (; i < argc; i++) {
-               ret = stat_item_valid(argv[i]);
+       for (i = 0; i < num_inputs; i++) {
+               ret = stat_item_valid(lls_input(i, lpr));
                if (ret < 0)
                        return ret;
                mask |= (one << ret);
@@ -344,58 +337,61 @@ static int com_stat(int fd, int argc, char **argv)
        free(b.buf);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(stat)
 
-static int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
 {
-       int ret = grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, lpr, &sched);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(grab)
 
-static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        return -E_AUDIOD_TERM;
 }
+EXPORT_AUDIOD_CMD_HANDLER(term)
 
-static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_ON;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(on)
 
-static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_OFF;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(off)
 
-static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_STANDBY;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(sb)
 
-static int com_cycle(__a_unused int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        switch (audiod_status) {
-               case  AUDIOD_ON:
-                       return com_sb(fd, argc, argv);
-                       break;
-               case  AUDIOD_OFF:
-                       return com_on(fd, argc, argv);
-                       break;
-               case  AUDIOD_STANDBY:
-                       return com_off(fd, argc, argv);
-                       break;
+               case  AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+               case  AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+               case  AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
        }
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
 
-static int com_version(int fd, int argc, char **argv)
+static int com_version(int fd, struct lls_parse_result *lpr)
 {
        int ret;
        char *msg;
+       const struct lls_opt_result *r_v;
 
-       if (argc > 1 && strcmp(argv[1], "-v") == 0)
+       r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+       if (lls_opt_given(r_v))
                msg = make_message("%s", version_text("audiod"));
        else
                msg = make_message("%s\n", version_single_line("audiod"));
@@ -403,6 +399,7 @@ static int com_version(int fd, int argc, char **argv)
        free(msg);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(version)
 
 /**
  * Handle arriving connections on the local socket.
@@ -419,14 +416,18 @@ static int com_version(int fd, int argc, char **argv)
  * \return Positive on success, negative on errors, zero if there was no
  * connection to accept.
  *
- * \sa para_accept(), recv_cred_buffer()
+ * \sa \ref para_accept(), \ref recv_cred_buffer().
  * */
 int handle_connect(int accept_fd, fd_set *rfds)
 {
-       int i, argc, ret, clifd;
+       int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr;
+       char *errctx = NULL;
+       const struct audiod_command_info *aci;
 
        ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
@@ -443,18 +444,27 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret <= 0)
                goto out;
        argc = ret;
-       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[0]))
-                       continue;
-               ret = audiod_cmds[i].handler(clifd, argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       ret = -E_INVALID_AUDIOD_CMD;
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       aci = lls_user_data(cmd);
+       ret = aci->handler(clifd, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
        free_argv(argv);
        if (ret < 0 && ret != -E_CLIENT_WRITE) {
-               char *tmp = make_message("%s\n", para_strerror(-ret));
+               char *tmp;
+               if (errctx) {
+                       tmp = make_message("%s\n", errctx);
+                       free(errctx);
+                       client_write(clifd, tmp);
+                       free(tmp);
+               }
+               tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
        }
index d432adbb64aa08a74e4dab7bd21360ebb2ef4227..708602e7e04c073ff510be8584235fe198ac128e 100755 (executable)
@@ -24,5 +24,5 @@ autoheader
 echo configuring...
 ./configure $@ > /dev/null
 echo compiling...
-make clean2 > /dev/null 2>&1
+make clean > /dev/null 2>&1
 make -j $n > /dev/null
index 638d19a3579b02053bd281e29e7f39d2bf54495c..9cd1273cfd578cf89492012b41cf236ecd32a38d 100644 (file)
@@ -19,6 +19,7 @@
 #include "error.h"
 #include "string.h"
 #include "wma.h"
+#include "portable_io.h"
 #include "bitstream.h"
 
 static inline uint32_t get_data(const void *table, int i, int size)
index 5875b0d090e6e8007d34766ee090950c819188dd..a6349861a55092fcdc1f957d7570621fd51e6834 100644 (file)
@@ -36,8 +36,8 @@ struct vlc {
 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];
+       const char *p = (const char *)gbc->buffer + (idx >> 3);
+       uint32_t x = read_u32_be(p);
        return (x << (idx & 7)) >> (32 - num);
 }
 
diff --git a/blob.c b/blob.c
index ed684428aba55ae848c58fe8c96af2c900f4d6b9..2d7c6050cfb18c198675c1ce4a534dd824983310 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -9,7 +9,9 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -28,8 +30,6 @@
  * \param obj2 Pointer to the second integer.
  *
  * \return The values required for an osl compare function.
- *
- * \sa osl_compare_func, osl_hash_compare().
  */
 static int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 {
@@ -90,26 +90,16 @@ INIT_BLOB_TABLE(moods);
 INIT_BLOB_TABLE(playlists);
 /** \endcond blob_table */
 
-/** Flags that may be passed to the \p ls functions of each blob  type. */
-enum blob_ls_flags {
-       /** List both id and name. */
-       BLOB_LS_FLAG_LONG = 1,
-       /** Reverse sort order. */
-       BLOB_LS_FLAG_REVERSE = 2,
-       /** Sort by id instead of name. */
-       BLOB_LS_FLAG_SORT_BY_ID = 4,
-};
-
 static int print_blob(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
        struct afs_callback_arg *aca = data;
-       uint32_t flags = *(uint32_t *)aca->query.data;
+       bool l_given = SERVER_CMD_OPT_GIVEN(LSMOOD, LONG, aca->lpr);
        struct osl_object obj;
        uint32_t id;
        int ret;
 
-       if (!(flags & BLOB_LS_FLAG_LONG)) {
+       if (!l_given) {
                para_printf(&aca->pbout, "%s\n", name);
                return 0;
        }
@@ -123,68 +113,44 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_lsblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_lsblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       uint32_t flags = *(uint32_t *)aca->query.data;
+       bool i_given, r_given;
        struct pattern_match_data pmd = {
                .table = table,
-               .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 = aca,
                .action = print_blob,
        };
        int ret;
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       pmd.lpr = aca->lpr;
+       assert(ret >= 0);
+       i_given = SERVER_CMD_OPT_GIVEN(LSMOOD, ID_SORT, aca->lpr);
+       r_given = SERVER_CMD_OPT_GIVEN(LSMOOD, REVERSE, aca->lpr);
 
-       if (flags & BLOB_LS_FLAG_REVERSE)
+       if (r_given)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       if (!(flags & BLOB_LS_FLAG_SORT_BY_ID))
-               pmd.loop_col_num = BLOBCOL_NAME;
-       else
+       if (i_given)
                pmd.loop_col_num = BLOBCOL_ID;
+       else
+               pmd.loop_col_num = BLOBCOL_NAME;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
-       if (pmd.num_matches == 0 && pmd.patterns.size > 0)
+       if (pmd.num_matches == 0 && lls_num_inputs(aca->lpr) > 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_lsblob(afs_callback *f, struct command_context *cc)
+static int com_lsblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       uint32_t flags = 0;
-       struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-       int i;
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-l")) {
-                       flags |= BLOB_LS_FLAG_LONG;
-                       continue;
-               }
-               if (!strcmp(arg, "-i")) {
-                       flags |= BLOB_LS_FLAG_SORT_BY_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-r")) {
-                       flags |= BLOB_LS_FLAG_REVERSE;
-                       continue;
-               }
-               break;
-       }
-//     if (argc > i)
-//             return -E_BLOB_SYNTAX;
-       return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, f, afs_cb_result_handler, cc);
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
 static int cat_blob(struct osl_table *table, struct osl_row *row,
@@ -202,33 +168,42 @@ static int cat_blob(struct osl_table *table, struct osl_row *row,
        return (ret < 0)? ret : ret2;
 }
 
-static int com_catblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_catblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = &aca->fd,
                .action = cat_blob
        };
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               return ret;
+               goto out;
        if (pmd.num_matches == 0)
                ret = -E_NO_MATCH;
+out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_catblob(afs_callback *f, struct command_context *cc)
+static int com_catblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_BLOB_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
 static int remove_blob(struct osl_table *table, struct osl_row *row,
@@ -243,19 +218,21 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_rmblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_rmblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = aca,
                .action = remove_blob
        };
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
@@ -267,19 +244,25 @@ static int com_rmblob_callback(struct osl_table *table,
                ret = afs_event(BLOB_REMOVE, NULL, table);
        }
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_rmblob(afs_callback *f, struct command_context *cc)
+static int com_rmblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
-static int com_addblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = aca->query.data;
@@ -399,11 +382,12 @@ again:
  * 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,
-               afs_callback *f)
+static int stdin_command(struct command_context *cc,
+               struct lls_parse_result *lpr, afs_callback *f)
 {
        struct osl_object query, stdin_obj;
        int ret;
+       size_t len = strlen(lls_input(0, lpr));
 
        ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
        if (ret < 0)
@@ -411,11 +395,11 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        ret = fd2buf(&cc->scc, &stdin_obj);
        if (ret < 0)
                return ret;
-       query.size = arg_obj->size + stdin_obj.size;
+       query.size = len + 1 + stdin_obj.size;
        query.data = para_malloc(query.size);
-       memcpy(query.data, arg_obj->data, arg_obj->size);
+       memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
-               memcpy((char *)query.data + arg_obj->size, stdin_obj.data,
+               memcpy((char *)query.data + len + 1, stdin_obj.data,
                        stdin_obj.size);
        free(stdin_obj.data);
        ret = send_callback_request(f, &query, afs_cb_result_handler, cc);
@@ -423,33 +407,42 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        return ret;
 }
 
-static int com_addblob(afs_callback *f, struct command_context *cc)
+static int com_addblob(afs_callback *f, __a_unused const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct osl_object arg_obj;
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
 
-       if (cc->argc != 2)
-               return -E_BLOB_SYNTAX;
-       if (!*cc->argv[1]) /* empty name is reserved for the dummy row */
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (!lls_input(0, lpr)[0]) /* empty name is reserved for the dummy row */
                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);
+       return stdin_command(cc, lpr, f);
 }
 
-static int com_mvblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_mvblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       char *src = (char *)aca->query.data;
-       struct osl_object obj = {.data = src, .size = strlen(src) + 1};
-       char *dest = src + obj.size;
+       const char *src, *dest;
+       struct osl_object obj;
        struct osl_row *row;
-       int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
+       int ret;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       src = lls_input(0, aca->lpr);
+       dest = lls_input(1, aca->lpr);
+       obj.data = (char *)src;
+       obj.size = strlen(src) + 1;
+       ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot find source blob %s\n", src);
                goto out;
        }
-       obj.data = dest;
+       obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
@@ -459,26 +452,35 @@ static int com_mvblob_callback(struct osl_table *table,
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_mvblob(afs_callback *f, struct command_context *cc)
+static int com_mvblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc != 3)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1,
-               cc->argv + 1, f, afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
-#define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
-       static int com_ ## cmd_name ## cmd_prefix ## _callback(struct afs_callback_arg *aca) \
+#define DEFINE_BLOB_COMMAND(cmd_name, c_cmd_name, table_name, short_name, c_short_name) \
+       static int com_ ## cmd_name ## short_name ## _callback(struct afs_callback_arg *aca) \
        { \
-               return com_ ## cmd_name ## blob_callback(table_name ## _table, aca); \
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob_callback(cmd, table_name ## _table, aca); \
        } \
-       int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
+       static int com_ ## cmd_name ## short_name(struct command_context *cc, struct lls_parse_result *lpr) \
        { \
-               return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, cc); \
-       }
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob(com_ ## cmd_name ## short_name ## _callback, cmd, cc, lpr); \
+       } \
+       EXPORT_SERVER_CMD_HANDLER(cmd_name ## short_name);
 
 static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                char **name)
@@ -629,25 +631,25 @@ static int blob_open(struct osl_table **table,
 
 
 /** Define all functions for this blob type. */
-#define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
+#define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
        DEFINE_BLOB_INIT(table_name) \
-       DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
-       DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
-       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
+       DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(rm, RM, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(mv, MV, table_name, short_name, c_short_name) \
+       DEFINE_GET_NAME_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_NAME(table_name, short_name); \
+       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, short_name); \
 
 /* doxygen isn't smart enough to recognize these */
 /** \cond blob_function */
-DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
-DEFINE_BLOB_FUNCTIONS(images, img);
-DEFINE_BLOB_FUNCTIONS(moods, mood);
-DEFINE_BLOB_FUNCTIONS(playlists, pl);
+DEFINE_BLOB_FUNCTIONS(lyrics, lyr, LYR);
+DEFINE_BLOB_FUNCTIONS(images, img, IMG);
+DEFINE_BLOB_FUNCTIONS(moods, mood, MOOD);
+DEFINE_BLOB_FUNCTIONS(playlists, pl, PL);
 /** \endcond blob_function */
index b0cd6665416627806d922cda50fac2e2eeeab17f..9b5bcda13d218b118d469587f6aec605cccec1b9 100644 (file)
@@ -905,7 +905,7 @@ size_t btr_get_output_queue_size(struct btr_node *btrn)
  * \return \p -ENOTSUP if no parent node of \a btrn understands \a command.
  * Otherwise the return value of the command handler is returned.
  *
- * \sa \ref receiver::execute, filter::execute, writer::execute.
+ * \sa \ref receiver::execute, \ref filter::execute, \ref writer::execute.
  */
 int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result)
 {
@@ -935,7 +935,7 @@ int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result)
  *
  * \return A pointer to the \a context address specified at node creation time.
  *
- * \sa btr_new_node(), struct \ref btr_node_description.
+ * \sa \ref btr_new_node(), struct \ref btr_node_description.
  */
 void *btr_context(struct btr_node *btrn)
 {
index 0957fe0364c914b7f50c3c1148ef16cbb2350f31..045158ab2d2d32dd97e000896dce450a2b854496 100644 (file)
@@ -29,22 +29,19 @@ struct wav_params {
        int sample_format_given;
 };
 
-/**
- * Copy the wav parameters.
- *
- * \param dst Usually a pointer to struct wav_params.
- * \param src Usually a pointer to some args_info struct.
- *
- * This can not be implemented as a function since the type of the structure
- * pointed to by \a src depends on the application.
- */
-#define COPY_WAV_PARMS(dst, src) \
-       (dst)->channels_arg = (src)->channels_arg; \
-       (dst)->channels_given = (src)->channels_given; \
-       (dst)->sample_rate_arg = (src)->sample_rate_arg; \
-       (dst)->sample_rate_given = (src)->sample_rate_given; \
-       (dst)->sample_format_arg = (src)->sample_format_arg; \
-       (dst)->sample_format_given = (src)->sample_format_given;
+#define LLS_COPY_WAV_PARMS(_dst, _pfx, _lpr) \
+       (_dst)->channels_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_CHANNELS, (_lpr))); \
+       (_dst)->sample_rate_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+       (_dst)->sample_format_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_FORMAT, (_lpr))); \
+       (_dst)->channels_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_CHANNELS, (_lpr))); \
+       (_dst)->sample_rate_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+       (_dst)->sample_format_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_FORMAT, (_lpr)));
 
 struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
index f1100df49e144b873779ca84d226c1829cb1c1a3..2607ee203ee6556a35535f0a293f41dd00203b2e 100644 (file)
--- a/client.c
+++ b/client.c
@@ -8,12 +8,13 @@
 
 #include <regex.h>
 #include <signal.h>
+#include <lopsub.h>
 
+#include "client.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "crypt.h"
-#include "client.cmdline.h"
 #include "string.h"
 #include "stdin.h"
 #include "stdout.h"
@@ -36,8 +37,7 @@ __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_cmd.lsg.h"
 
 struct exec_task {
        struct task *task;
@@ -78,17 +78,52 @@ out:
        return 0;
 }
 
-static int make_client_argv(const char *line)
+/* Called from the line handler and the completers. This overwrites ct->lpr. */
+static int create_merged_lpr(const char *line)
 {
-       int ret;
+       const struct lls_command *cmd = CLIENT_CMD_PTR;
+       int argc, ret;
+       char *cmdline, **argv, *errctx;
+       struct lls_parse_result *argv_lpr;
+       static struct lls_parse_result *orig_lpr;
 
-       free_argv(ct->conf.inputs);
-       ret = create_argv(line, " ", &ct->conf.inputs);
-       if (ret >= 0)
-               ct->conf.inputs_num = ret;
+       if (!orig_lpr)
+               orig_lpr = ct->lpr;
+       ct->lpr = NULL;
+       cmdline = make_message("-- %s", line);
+       ret = create_shifted_argv(cmdline, " ", &argv);
+       free(cmdline);
+       if (ret < 0)
+               return ret;
+       argc = ret;
+       if (argc == 2) { /* no words (only blanks in line) */
+               free_argv(argv);
+               return 0;
+       }
+       argv[0] = para_strdup("--");
+       /*
+        * The original lpr for the interactive session has no non-option
+        * arguments. We create a fresh lpr from the words in "line" and merge
+        * it with the orignal lpr.
+        */
+       ret = lls(lls_parse(argc, argv, cmd, &argv_lpr, &errctx));
+       free_argv(argv);
+       if (ret < 0)
+               goto fail;
+       ret = lls(lls_merge(orig_lpr, argv_lpr, cmd, &ct->lpr, &errctx));
+       lls_free_parse_result(argv_lpr, cmd);
+       if (ret < 0)
+               goto fail;
+       return 1;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       free(errctx);
+       assert(ret < 0);
        return ret;
 }
 
+/* called from completers */
 static int execute_client_command(const char *cmd, char **result)
 {
        int ret;
@@ -97,9 +132,11 @@ static int execute_client_command(const char *cmd, char **result)
                .result_buf = para_strdup(""),
                .result_size = 1,
        };
+       struct lls_parse_result *old_lpr = ct->lpr;
+
        *result = NULL;
-       ret = make_client_argv(cmd);
-       if (ret < 0)
+       ret = create_merged_lpr(cmd);
+       if (ret <= 0)
                goto out;
        exec_task.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "exec_collect"));
@@ -114,6 +151,8 @@ static int execute_client_command(const char *cmd, char **result)
                goto out;
        schedule(&command_sched);
        sched_shutdown(&command_sched);
+       lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+       ct->lpr = old_lpr;
        *result = exec_task.result_buf;
        btr_remove_node(&exec_task.btrn);
        ret = 1;
@@ -174,7 +213,7 @@ static void complete_lsblob(const char *blob_type,
                struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-i", "-l", "-r", NULL};
+       char *opts[] = {LSG_SERVER_CMD_LSIMG_OPTS, NULL};
 
        if (ci->word[0] == '-')
                return i9e_complete_option(opts, ci, cr);
@@ -218,18 +257,17 @@ 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,
+static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", NULL};
+       char *opts[] = {LSG_SERVER_CMD_STAT_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
-static void stat_completer(struct i9e_completion_info *ci,
+static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-n=", "-p", NULL};
-       //PARA_CRIT_LOG("word: %s\n", ci->word);
+       char *opts[] = {LSG_SERVER_CMD_VERSION_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
@@ -266,7 +304,7 @@ static void sender_completer(struct i9e_completion_info *ci,
 static void add_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-l", "-f", "-v", "--", NULL};
+       char *opts[] = {LSG_SERVER_CMD_ADD_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -276,11 +314,7 @@ static void add_completer(struct i9e_completion_info *ci,
 static void ls_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {
-               "--", "-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
-       };
+       char *opts[] = {LSG_SERVER_CMD_LS_OPTS, NULL};
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
        cr->filename_completion_desired = true;
@@ -323,7 +357,7 @@ out:
 static void lsatt_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-i", "-l", "-r", NULL};
+       char *opts[] = {LSG_SERVER_CMD_LSATT_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
@@ -342,14 +376,14 @@ static void rmatt_completer(struct i9e_completion_info *ci,
 static void check_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-m", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_CHECK_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
 static void rm_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", "-f", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_RM_OPTS, NULL};
 
        if (ci->word[0] == '-') {
                i9e_complete_option(opts, ci, cr);
@@ -361,7 +395,7 @@ static void rm_completer(struct i9e_completion_info *ci,
 static void touch_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-n=", "-l=", "-y=", "-i=", "-a=", "-v", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_TOUCH_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -371,7 +405,7 @@ static void touch_completer(struct i9e_completion_info *ci,
 static void cpsi_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-y", "-i", "-l", "-n", "-v", NULL};
+       char *opts[] = {LSG_SERVER_CMD_CPSI_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -447,9 +481,13 @@ DEFINE_BLOB_COMPLETER(mv, pl)
 static int client_i9e_line_handler(char *line)
 {
        int ret;
+       static bool first = true;
 
-       PARA_DEBUG_LOG("line: %s\n", line);
-       ret = make_client_argv(line);
+       if (first)
+               first = false;
+       else
+               lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+       ret = create_merged_lpr(line);
        if (ret <= 0)
                return ret;
        ret = client_connect(ct, &sched, NULL, NULL);
@@ -460,15 +498,16 @@ static int client_i9e_line_handler(char *line)
 }
 
 static struct i9e_completer completers[] = {
-       SERVER_COMPLETERS
-       AFS_COMPLETERS
+#define LSG_SERVER_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_SERVER_CMD_SUBCOMMANDS
+#undef LSG_SERVER_CMD_CMD
        {.name = NULL}
 };
 
 __noreturn static void interactive_session(void)
 {
        int ret;
-       char *history_file;
        struct sigaction act;
        struct i9e_client_info ici = {
                .fds = {0, 1, 2},
@@ -479,16 +518,15 @@ __noreturn static void interactive_session(void)
        };
 
        PARA_NOTICE_LOG("\n%s\n", version_text("client"));
-       if (ct->conf.history_file_given)
-               history_file = para_strdup(ct->conf.history_file_arg);
+       if (CLIENT_OPT_GIVEN(HISTORY_FILE, ct->lpr))
+               ici.history_file = para_strdup(CLIENT_OPT_STRING_VAL(
+                       HISTORY_FILE, ct->lpr));
        else {
                char *home = para_homedir();
-               history_file = make_message("%s/.paraslash/client.history",
+               ici.history_file = make_message("%s/.paraslash/client.history",
                        home);
                free(home);
        }
-       ici.history_file = history_file;
-
        act.sa_handler = i9e_signal_dispatch;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
@@ -512,7 +550,9 @@ out:
 
 __noreturn static void print_completions(void)
 {
-       int ret = i9e_print_completions(completers);
+       int ret;
+
+       ret = i9e_print_completions(completers);
        exit(ret <= 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
@@ -575,7 +615,8 @@ static struct supervisor_task supervisor_task;
  *
  * \return EXIT_SUCCESS or EXIT_FAILURE
  *
- * \sa client_open(), stdin.c, stdout.c, para_client(1), para_server(1)
+ * \sa \ref client_open(), \ref stdin.c, \ref stdout.c, para_client(1),
+ * para_server(1).
  */
 int main(int argc, char *argv[])
 {
@@ -587,7 +628,7 @@ int main(int argc, char *argv[])
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
        if (ret < 0)
                goto out;
-       if (ct->conf.complete_given)
+       if (CLIENT_OPT_GIVEN(COMPLETE, ct->lpr))
                print_completions();
        if (ret == 0)
                interactive_session(); /* does not return */
index 2f257221510b1cb0b0848eb615d1dfb894f0cc81..87cfc618d64825684d0d607a07cf4605d4476ae2 100644 (file)
--- a/client.h
+++ b/client.h
@@ -38,8 +38,8 @@ struct client_task {
        struct btr_node *btrn[2];
        /** The hash value of the decrypted challenge. */
        unsigned char *challenge_hash;
-       /** The configuration (including the command). */
-       struct client_args_info conf;
+       /** The parsed command line (including the command). */
+       struct lls_parse_result *lpr;
        /** The config file for client options. */
        char *config_file;
        /** The RSA private key. */
@@ -52,6 +52,16 @@ struct client_task {
        char **features;
 };
 
+#define CLIENT_CMD_PTR (lls_cmd(0, client_suite))
+#define CLIENT_OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_CLIENT_PARA_CLIENT_OPT_ ## _name, _lpr))
+#define CLIENT_OPT_GIVEN(_name, _lpr) \
+       (lls_opt_given(CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_UINT32_VAL(_name, _lpr) \
+       (lls_uint32_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_STRING_VAL(_name, _lpr) \
+       (lls_string_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+
 void client_close(struct client_task *ct);
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel);
index eea7510fd0589e86fb4058dd7f4361f3c79901aa..39eb8b4cce9f6403132329fd0edfbf7b7025caaf 100644 (file)
@@ -13,7 +13,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "client.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "fd.h"
 #include "sideband.h"
 #include "string.h"
-#include "client.cmdline.h"
 #include "client.h"
 #include "buffer_tree.h"
 #include "version.h"
-#include "ggo.h"
 
 /** The size of the receiving buffer. */
 #define CLIENT_BUFSIZE 4000
@@ -46,7 +46,7 @@ void client_close(struct client_task *ct)
        free(ct->user);
        free(ct->config_file);
        free(ct->key_file);
-       client_cmdline_parser_free(&ct->conf);
+       lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
        free(ct->challenge_hash);
        sb_free(ct->sbc[0]);
        sb_free(ct->sbc[1]);
@@ -90,7 +90,7 @@ static void client_pre_select(struct sched *s, void *context)
                        else if (ret > 0)
                                para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                }
-               /* fall though */
+               /* fallthrough */
        case CL_EXECUTING:
                if (ct->btrn[0]) {
                        ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
@@ -241,26 +241,23 @@ out:
        return ret;
 }
 
-static bool has_feature(const char *feature, struct client_task *ct)
-{
-       return find_arg(feature, ct->features) >= 0? true : false;
-}
-
 static int send_sb_command(struct client_task *ct)
 {
        int i;
        char *command, *p;
        size_t len = 0;
+       unsigned num_inputs = lls_num_inputs(ct->lpr);
 
        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;
+       for (i = 0; i < num_inputs; i++)
+               len += strlen(lls_input(i, ct->lpr)) + 1;
        p = command = para_malloc(len);
-       for (i = 0; i < ct->conf.inputs_num; i++) {
-               strcpy(p, ct->conf.inputs[i]);
-               p += strlen(ct->conf.inputs[i]) + 1;
+       for (i = 0; i < num_inputs; i++) {
+               const char *str = lls_input(i, ct->lpr);
+               strcpy(p, str);
+               p += strlen(str) + 1;
        }
        PARA_DEBUG_LOG("--> %s\n", command);
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
@@ -297,8 +294,8 @@ static int client_post_select(struct sched *s, void *context)
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
-                       has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
+               sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
+                       ct->user);
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
@@ -314,7 +311,6 @@ static int client_post_select(struct sched *s, void *context)
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
-               bool use_aes;
 
                ret = recv_sb(ct, &s->rfds, &sbb);
                if (ret <= 0)
@@ -333,10 +329,9 @@ static int client_post_select(struct sched *s, void *context)
                        goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
-               use_aes = has_feature("aes_ctr128", ct);
-               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
+               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN, use_aes);
+                       SESSION_KEY_LEN);
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
@@ -467,12 +462,12 @@ int client_connect(struct client_task *ct, struct sched *s,
                struct btr_node *parent, struct btr_node *child)
 {
        int ret;
+       const char *host = CLIENT_OPT_STRING_VAL(HOSTNAME, ct->lpr);
+       uint32_t port = CLIENT_OPT_UINT32_VAL(SERVER_PORT, ct->lpr);
 
-       PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
-               ct->conf.server_port_arg);
+       PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
-                                              ct->conf.server_port_arg);
+       ret = para_connect_simple(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
@@ -498,13 +493,19 @@ err_out:
        return ret;
 }
 
-__noreturn static void print_help_and_die(struct client_task *ct)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(client);
-       bool d = ct->conf.detailed_help_given;
+       char *help;
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (CLIENT_OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CLIENT_CMD_PTR);
+       else if (CLIENT_OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CLIENT_CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 /**
@@ -528,65 +529,90 @@ __noreturn static void print_help_and_die(struct client_task *ct)
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel)
 {
-       char *home = para_homedir();
-       int ret;
-       struct client_task *ct = para_calloc(sizeof(struct client_task));
-
-       *ct_ptr = ct;
-       ct->scc.fd = -1;
-       ret = -E_CLIENT_SYNTAX;
-       if (client_cmdline_parser(argc, argv, &ct->conf))
-               goto out;
-       version_handle_flag("client", ct->conf.version_given);
-       if (ct->conf.help_given || ct->conf.detailed_help_given)
-               print_help_and_die(ct);
-
-       ct->config_file = ct->conf.config_file_given?
-               para_strdup(ct->conf.config_file_arg) :
-               make_message("%s/.paraslash/client.conf", home);
-       ret = file_exists(ct->config_file);
-       if (!ret && ct->conf.config_file_given) {
-               ret = -E_NO_CONFIG;
+       const struct lls_command *cmd = CLIENT_CMD_PTR;
+       void *map;
+       size_t sz;
+       struct lls_parse_result *lpr;
+       int ret, ll;
+       struct client_task *ct;
+       char *cf = NULL, *kf = NULL, *user, *errctx, *home = para_homedir();
+
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       if (ret) {
-               struct client_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 0
-               };
-               ret = -E_BAD_CONFIG;
-               if (client_cmdline_parser_config_file(ct->config_file,
-                       &ct->conf, &params))
+       ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+
+       if (CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
+               cf = para_strdup(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr));
+       else
+               cf = make_message("%s/.paraslash/client.conf", home);
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto out;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) &&
+                               CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
+                       goto out;
+       } else {
+               int cf_argc;
+               char **cf_argv;
+               struct lls_parse_result *cf_lpr, *merged_lpr;
+               ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+               para_munmap(map, sz);
+               if (ret < 0)
+                       goto out;
+               cf_argc = ret;
+               ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+               lls_free_argv(cf_argv);
+               if (ret < 0)
                        goto out;
+               ret = lls(lls_merge(lpr, cf_lpr, cmd, &merged_lpr,
+                       &errctx));
+               lls_free_parse_result(cf_lpr, cmd);
+               if (ret < 0)
+                       goto out;
+               lls_free_parse_result(lpr, cmd);
+               lpr = merged_lpr;
        }
-       ct->user = ct->conf.user_given?
-               para_strdup(ct->conf.user_arg) : para_logname();
+       /* success */
+       user = CLIENT_OPT_GIVEN(USER, lpr)?
+               para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname();
 
-       if (ct->conf.key_file_given)
-               ct->key_file = para_strdup(ct->conf.key_file_arg);
+       if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
+               kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
        else {
-               ct->key_file = make_message("%s/.paraslash/key.%s",
-                       home, ct->user);
-               if (!file_exists(ct->key_file)) {
-                       free(ct->key_file);
-                       ct->key_file = make_message("%s/.ssh/id_rsa", home);
+               kf = make_message("%s/.paraslash/key.%s", home, user);
+               if (!file_exists(kf)) {
+                       free(kf);
+                       kf = make_message("%s/.ssh/id_rsa", home);
                }
        }
-
+       PARA_INFO_LOG("user: %s\n", user);
+       PARA_INFO_LOG("config file: %s\n", cf);
+       PARA_INFO_LOG("key file: %s\n", kf);
+       PARA_INFO_LOG("loglevel: %d\n", ll);
+       ct = para_calloc(sizeof(*ct));
+       ct->scc.fd = -1;
+       ct->lpr = lpr;
+       ct->key_file = kf;
+       ct->config_file = cf;
+       ct->user = user;
+       *ct_ptr = ct;
        if (loglevel)
-               *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
-       PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
-       PARA_INFO_LOG("config_file: %s\n", ct->config_file);
-       PARA_INFO_LOG("key_file: %s\n", ct->key_file);
-       ret = ct->conf.inputs_num;
+               *loglevel = ll;
+       ret = lls_num_inputs(lpr);
 out:
        free(home);
        if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               client_close(ct);
+               lls_free_parse_result(lpr, cmd);
+               free(cf);
+               free(kf);
                *ct_ptr = NULL;
        }
        return ret;
index c606d98c46aeb4c9a15e76b90ea5856231c6a38e..655dbc562a9dc2ed55711fad44a1dab713db25b9 100644 (file)
@@ -19,7 +19,7 @@ static int initialized;
 /**
  * Describes an element of the close-on-fork list.
  *
- * \sa list.h
+ * \sa \ref list.h.
  */
 struct close_on_fork {
        /** The file descriptor which should be closed after fork(). */
index fe4b92329b417009613e0f1034f747783699c2e0..5802d37ab7a7353c545bc5dbc9c998f297a2f576 100644 (file)
--- a/command.c
+++ b/command.c
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
 #include "sideband.h"
 #include "command.h"
-#include "server.cmdline.h"
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
 #include "daemon.h"
 #include "fd.h"
 #include "ipc.h"
+#include "server_cmd.lsg.h"
 #include "user_list.h"
-#include "server.command_list.h"
-#include "afs.command_list.h"
 #include "signal.h"
 #include "version.h"
 
-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};
+#define SERVER_CMD_AUX_INFO(_arg) _arg,
+static const unsigned server_command_perms[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
+#define SERVER_CMD_AUX_INFO(_arg) #_arg,
+static const char * const server_command_perms_txt[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
 
 /** Commands including options must be shorter than this. */
 #define MAX_COMMAND_LEN 32768
@@ -93,21 +77,6 @@ static char *vss_status_tohuman(unsigned int flags)
        return para_strdup("paused");
 }
 
-/*
- * return human readable permission string. Never returns NULL.
- */
-static char *cmd_perms_itohuman(unsigned int perms)
-{
-       char *msg = para_malloc(5 * sizeof(char));
-
-       msg[0] = perms & AFS_READ? 'a' : '-';
-       msg[1] = perms & AFS_WRITE? 'A' : '-';
-       msg[2] = perms & VSS_READ? 'v' : '-';
-       msg[3] = perms & VSS_WRITE? 'V' : '-';
-       msg[4] = '\0';
-       return msg;
-}
-
 /*
  * Never returns NULL.
  */
@@ -123,7 +92,7 @@ static char *vss_get_status_flags(unsigned int flags)
        return msg;
 }
 
-static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
+static unsigned get_status(struct misc_meta_data *nmmd, bool parser_friendly,
                char **result)
 {
        char *status, *flags; /* vss status info */
@@ -138,8 +107,8 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
        clock_get_realtime(&current_time);
        /*
         * The calls to WRITE_STATUS_ITEM() below never fail because
-        * b->max_size is zero (unlimited), see para_printf(). However, clang
-        * is not smart enough to prove this and complains nevertheless.
+        * b->max_size is zero (unlimited), see \ref para_printf(). However,
+        * clang is not smart enough to prove this and complains nevertheless.
         * Casting the return value to void silences clang.
         */
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status);
@@ -158,52 +127,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
        return b.offset;
 }
 
-static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
-{
-       int i;
-
-       const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
-       scd->sender_num = -1;
-       if (argc < 3)
-               return -E_COMMAND_SYNTAX;
-       for (i = 0; senders[i].name; i++)
-               if (!strcmp(senders[i].name, argv[1]))
-                       break;
-       PARA_DEBUG_LOG("%d:%s\n", argc, argv[1]);
-       if (!senders[i].name)
-               return -E_COMMAND_SYNTAX;
-       scd->sender_num = i;
-       for (i = 0; subcmds[i]; i++)
-               if (!strcmp(subcmds[i], argv[2]))
-                       break;
-       if (!subcmds[i])
-               return -E_COMMAND_SYNTAX;
-       scd->cmd_num = i;
-       if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
-               return -E_SENDER_CMD;
-       switch (scd->cmd_num) {
-       case SENDER_on:
-       case SENDER_off:
-               if (argc != 3)
-                       return -E_COMMAND_SYNTAX;
-               break;
-       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:
-               if (argc != 4)
-                       return -E_COMMAND_SYNTAX;
-               return parse_fec_url(argv[3], scd);
-       default:
-               return -E_COMMAND_SYNTAX;
-       }
-       return 1;
-}
-
 /**
  * Send a sideband packet through a blocking file descriptor.
  *
@@ -279,6 +202,81 @@ int send_strerror(struct command_context *cc, int err)
        return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
 }
 
+/**
+ * Send an error context to a client,
+ *
+ * \param cc Client info.
+ * \param errctx The error context string.
+ *
+ * \return The return value of the underlying call to send_sb_va().
+ *
+ * This function frees the error context string after it was sent.
+ */
+int send_errctx(struct command_context *cc, char *errctx)
+{
+       int ret;
+
+       if (!errctx)
+               return 0;
+       ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", errctx);
+       free(errctx);
+       return ret;
+}
+
+static int check_sender_args(struct command_context *cc,
+               struct lls_parse_result *lpr, struct sender_command_data *scd)
+{
+       int i, ret;
+       const char *subcmds[] = {SENDER_SUBCOMMANDS};
+       const char *arg;
+       char *errctx;
+       unsigned num_inputs = lls_num_inputs(lpr);
+
+       scd->sender_num = -1;
+       ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       arg = lls_input(0, lpr);
+       for (i = 0; senders[i].name; i++)
+               if (!strcmp(senders[i].name, arg))
+                       break;
+       if (!senders[i].name)
+               return -E_COMMAND_SYNTAX;
+       scd->sender_num = i;
+       arg = lls_input(1, lpr);
+       for (i = 0; subcmds[i]; i++)
+               if (!strcmp(subcmds[i], arg))
+                       break;
+       if (!subcmds[i])
+               return -E_COMMAND_SYNTAX;
+       scd->cmd_num = i;
+       if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
+               return -E_SENDER_CMD;
+       switch (scd->cmd_num) {
+       case SENDER_on:
+       case SENDER_off:
+               if (num_inputs != 2)
+                       return -E_COMMAND_SYNTAX;
+               break;
+       case SENDER_deny:
+       case SENDER_allow:
+               if (num_inputs != 3 || parse_cidr(lls_input(2, lpr), scd->host,
+                               sizeof(scd->host), &scd->netmask) == NULL)
+                       return -E_COMMAND_SYNTAX;
+               break;
+       case SENDER_add:
+       case SENDER_delete:
+               if (num_inputs != 3)
+                       return -E_COMMAND_SYNTAX;
+               return parse_fec_url(lls_input(2, lpr), scd);
+       default:
+               return -E_COMMAND_SYNTAX;
+       }
+       return 1;
+}
+
 /**
  * Send a sideband packet through a blocking file descriptor.
  *
@@ -329,13 +327,13 @@ fail:
        return ret;
 }
 
-static int com_sender(struct command_context *cc)
+static int com_sender(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret = 0;
        char *msg = NULL;
        struct sender_command_data scd;
 
-       if (cc->argc < 2) {
+       if (lls_num_inputs(lpr) == 0) {
                for (i = 0; senders[i].name; i++) {
                        char *tmp;
                        ret = xasprintf(&tmp, "%s%s\n", msg? msg : "",
@@ -345,11 +343,11 @@ static int com_sender(struct command_context *cc)
                }
                return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
        }
-       ret = check_sender_args(cc->argc, cc->argv, &scd);
+       ret = check_sender_args(cc, lpr, &scd);
        if (ret < 0) {
                if (scd.sender_num < 0)
                        return ret;
-               if (strcmp(cc->argv[2], "status") == 0)
+               if (strcmp(lls_input(1, lpr), "status") == 0)
                        msg = senders[scd.sender_num].status();
                else
                        msg = senders[scd.sender_num].help();
@@ -360,7 +358,8 @@ static int com_sender(struct command_context *cc)
        case SENDER_add:
        case SENDER_delete:
                assert(senders[scd.sender_num].resolve_target);
-               ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
+               ret = senders[scd.sender_num].resolve_target(lls_input(2, lpr),
+                       &scd);
                if (ret < 0)
                        return ret;
        }
@@ -380,17 +379,16 @@ static int com_sender(struct command_context *cc)
        }
        return (i < 10)? 1 : -E_LOCK;
 }
+EXPORT_SERVER_CMD_HANDLER(sender);
 
-/* server info */
-static int com_si(struct command_context *cc)
+static int com_si(struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       int ret;
        char *msg, *ut;
+       int ret;
 
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
-       mutex_lock(mmd_mutex);
        ut = daemon_get_uptime_str(now);
+       mutex_lock(mmd_mutex);
        ret = xasprintf(&msg,
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
@@ -404,26 +402,27 @@ static int com_si(struct command_context *cc)
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
-               conf.loglevel_arg,
+               ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
        free(ut);
        return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(si);
 
-/* version */
-static int com_version(struct command_context *cc)
+static int com_version(struct command_context *cc, struct lls_parse_result *lpr)
 {
        char *msg;
        size_t len;
 
-       if (cc->argc > 1 && strcmp(cc->argv[1], "-v") == 0)
+       if (SERVER_CMD_OPT_GIVEN(VERSION, VERBOSE, lpr))
                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);
 }
+EXPORT_SERVER_CMD_HANDLER(version);
 
 /** These status items are cleared if no audio file is currently open. */
 #define EMPTY_STATUS_ITEMS \
@@ -463,7 +462,7 @@ static int com_version(struct command_context *cc)
  *
  * This is used by vss when currently no audio file is open.
  */
-static unsigned empty_status_items(int parser_friendly, char **result)
+static unsigned empty_status_items(bool parser_friendly, char **result)
 {
        char *esi;
        unsigned len;
@@ -491,39 +490,16 @@ static unsigned empty_status_items(int parser_friendly, char **result)
 }
 #undef EMPTY_STATUS_ITEMS
 
-/* stat */
-static int com_stat(struct command_context *cc)
+static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       int i, ret;
+       int ret;
        struct misc_meta_data tmp, *nmmd = &tmp;
        char *s;
-       int32_t num = 0;
-       int parser_friendly = 0;
+       bool parser_friendly = SERVER_CMD_OPT_GIVEN(STAT, PARSER_FRIENDLY,
+               lpr) > 0;
+       uint32_t num = SERVER_CMD_UINT32_VAL(STAT, NUM, lpr);
 
        para_sigaction(SIGUSR1, dummy);
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-n=", 3)) {
-                       ret = para_atoi32(arg + 3, &num);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strcmp(arg, "-p")) {
-                       parser_friendly = 1;
-                       continue;
-               }
-               return -E_COMMAND_SYNTAX;
-       }
-       if (i != cc->argc)
-               return -E_COMMAND_SYNTAX;
        for (;;) {
                /*
                 * Copy the mmd structure to minimize the time we hold the mmd
@@ -555,114 +531,98 @@ static int com_stat(struct command_context *cc)
 out:
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(stat);
 
-static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
-               const char *handler)
+/* fixed-length, human readable permission string */
+const char *server_cmd_perms_str(unsigned int perms)
 {
-       char *msg = NULL;
+       static char result[5];
 
-       for (; cmd->name; cmd++) {
-               char *tmp, *perms = cmd_perms_itohuman(cmd->perms);
-               tmp = make_message("%s\t%s\t%s\t%s\n", cmd->name, handler,
-                       perms, cmd->description);
-               free(perms);
-               msg = para_strcat(msg, tmp);
-               free(tmp);
-       }
-       assert(msg);
-       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
+       result[0] = perms & AFS_READ? 'a' : '-';
+       result[1] = perms & AFS_WRITE? 'A' : '-';
+       result[2] = perms & VSS_READ? 'v' : '-';
+       result[3] = perms & VSS_WRITE? 'V' : '-';
+       result[4] = '\0';
+       return result;
 }
 
-/* returns string that must be freed by the caller */
-static struct server_command *get_cmd_ptr(const char *name, char **handler)
+static int send_list_of_commands(struct command_context *cc)
 {
-       struct server_command *cmd;
-
-       for (cmd = server_cmds; cmd->name; cmd++)
-               if (!strcmp(cmd->name, name)) {
-                       if (handler)
-                               *handler = para_strdup("server"); /* server commands */
-                       return cmd;
-               }
-       /* not found, look for commands supported by afs */
-       for (cmd = afs_cmds; cmd->name; cmd++)
-               if (!strcmp(cmd->name, name)) {
-                       if (handler)
-                               *handler = para_strdup("afs");
-                       return cmd;
-               }
-       return NULL;
+       int i;
+       const struct lls_command *cmd;
+       char *msg = para_strdup("");
+
+       for (i = 1; (cmd = lls_cmd(i, server_cmd_suite)); i++) {
+               const char *perms = server_cmd_perms_str(server_command_perms[i]);
+               char *tmp = make_message("%s%s\t%s\t%s\n", msg,
+                       lls_command_name(cmd), perms, lls_purpose(cmd));
+               free(msg);
+               msg = tmp;
+       }
+       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
 }
 
-/* help */
-static int com_help(struct command_context *cc)
+static int com_help(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct server_command *cmd;
-       char *perms, *handler, *buf;
+       const char *perms;
+       char *long_help, *buf, *errctx;
        int ret;
+       const struct lls_command *cmd;
 
-       if (cc->argc < 2) {
-               /* no argument given, print list of commands */
-               if ((ret = send_list_of_commands(cc, server_cmds, "server")) < 0)
-                       return ret;
-               return send_list_of_commands(cc, afs_cmds, "afs");
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
+       if (lls_num_inputs(lpr) == 0)
+               return send_list_of_commands(cc);
        /* argument given for help */
-       cmd = get_cmd_ptr(cc->argv[1], &handler);
-       if (!cmd)
-               return -E_BAD_CMD;
-       perms = cmd_perms_itohuman(cmd->perms);
-       ret = xasprintf(&buf, "%s - %s\n\n"
-               "handler: %s\n"
-               "permissions: %s\n"
-               "usage: %s\n\n"
-               "%s\n",
-               cc->argv[1],
-               cmd->description,
-               handler,
-               perms,
-               cmd->usage,
-               cmd->help
-       );
-       free(perms);
-       free(handler);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), server_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       cmd = lls_cmd(ret, server_cmd_suite);
+       perms = server_command_perms_txt[ret];
+       long_help = lls_long_help(cmd);
+       assert(long_help);
+       ret = xasprintf(&buf, "%spermissions: %s\n", long_help, perms);
+       free(long_help);
        return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(help);
 
-/* hup */
-static int com_hup(struct command_context *cc)
+static int com_hup(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        kill(getppid(), SIGHUP);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(hup);
 
-/* term */
-static int com_term(struct command_context *cc)
+static int com_term(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        kill(getppid(), SIGTERM);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(term);
 
-static int com_play(struct command_context *cc)
+static int com_play(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->new_vss_status_flags |= VSS_PLAYING;
        mmd->new_vss_status_flags &= ~VSS_NOMORE;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(play);
 
-/* stop */
-static int com_stop(struct command_context *cc)
+static int com_stop(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->new_vss_status_flags &= ~VSS_PLAYING;
        mmd->new_vss_status_flags &= ~VSS_REPOS;
@@ -670,12 +630,11 @@ static int com_stop(struct command_context *cc)
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(stop);
 
-/* pause */
-static int com_pause(struct command_context *cc)
+static int com_pause(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        if (!vss_paused() && !vss_stopped()) {
                mmd->events++;
@@ -685,42 +644,44 @@ static int com_pause(struct command_context *cc)
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(pause);
 
-/* next */
-static int com_next(struct command_context *cc)
+static int com_next(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->events++;
        mmd->new_vss_status_flags |= VSS_NEXT;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(next);
 
-/* nomore */
-static int com_nomore(struct command_context *cc)
+static int com_nomore(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        if (vss_playing() || vss_paused())
                mmd->new_vss_status_flags |= VSS_NOMORE;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(nomore);
 
-/* ff */
-static int com_ff(struct command_context *cc)
+static int com_ff(__a_unused struct command_context *cc,
+               struct lls_parse_result *lpr)
 {
        long promille;
        int ret, backwards = 0;
        unsigned i;
-       char c;
+       char c, *errctx;
 
-       if (cc->argc != 2)
-               return -E_COMMAND_SYNTAX;
-       if (!(ret = sscanf(cc->argv[1], "%u%c", &i, &c)))
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c)))
                return -E_COMMAND_SYNTAX;
        if (ret > 1 && c == '-')
                backwards = 1; /* jmp backwards */
@@ -748,17 +709,22 @@ out:
        mutex_unlock(mmd_mutex);
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(ff);
 
-/* jmp */
-static int com_jmp(struct command_context *cc)
+static int com_jmp(__a_unused struct command_context *cc,
+               struct lls_parse_result *lpr)
 {
        long unsigned int i;
        int ret;
+       char *errctx;
 
-       if (cc->argc != 2)
-               return -E_COMMAND_SYNTAX;
-       if (sscanf(cc->argv[1], "%lu", &i) <= 0)
-               return -E_COMMAND_SYNTAX;
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (sscanf(lls_input(0, lpr), "%lu", &i) <= 0)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total)
@@ -777,26 +743,16 @@ out:
        mutex_unlock(mmd_mutex);
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(jmp);
 
-static int com_tasks(struct command_context *cc)
+static int com_tasks(struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
        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, const struct server_command *cmd_ptr)
-{
-       PARA_DEBUG_LOG("checking permissions\n");
-       return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
+       assert(tl);
+       return send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(tasks);
 
 static void reset_signals(void)
 {
@@ -807,7 +763,7 @@ static void reset_signals(void)
 }
 
 struct connection_features {
-       bool aes_ctr128_requested;
+       int dummy; /* none at the moment */
 };
 
 static int parse_auth_request(char *buf, int len, struct user **u,
@@ -836,7 +792,7 @@ static int parse_auth_request(char *buf, int len, struct user **u,
                        if (strcmp(features[i], "sideband") == 0)
                                continue;
                        if (strcmp(features[i], "aes_ctr128") == 0)
-                               cf->aes_ctr128_requested = true;
+                               continue;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
@@ -856,37 +812,50 @@ out:
 static int run_command(struct command_context *cc, struct iovec *iov,
                const char *peername)
 {
-       int ret, i;
-       char *p, *end;
-       struct server_command *cmd;
+       int ret, i, argc;
+       char *p, *end, **argv;
+       const struct lls_command *lcmd = NULL;
+       unsigned perms;
+       struct lls_parse_result *lpr;
+       char *errctx;
 
        if (iov->iov_base == NULL || iov->iov_len == 0)
-               return -E_BAD_CMD;
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        p = iov->iov_base;
        p[iov->iov_len - 1] = '\0'; /* just to be sure */
-       cmd = get_cmd_ptr(p, NULL);
-       if (!cmd)
-               return -E_BAD_CMD;
-       ret = check_perms(cc->u->perms, cmd);
-       if (ret < 0)
+
+       ret = lls(lls_lookup_subcmd(p, server_cmd_suite, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
                return ret;
+       }
+       perms = server_command_perms[ret];
+       if ((perms & cc->u->perms) != perms)
+               return -E_PERM;
+       lcmd = lls_cmd(ret, server_cmd_suite);
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
-       cc->argc = i;
-       cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
+       argc = i;
+       argv = para_malloc((argc + 1) * sizeof(char *));
        for (i = 0, p = iov->iov_base; p < end; i++) {
-               cc->argv[i] = para_strdup(p);
+               argv[i] = para_strdup(p);
                p += strlen(p) + 1;
        }
-       cc->argv[cc->argc] = NULL;
-       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
+       argv[argc] = NULL;
+       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", lls_command_name(lcmd),
                cc->u->name, peername);
-       ret = cmd->handler(cc);
-       free_argv(cc->argv);
+       ret = lls(lls_parse(argc, argv, lcmd, &lpr, &errctx));
+       if (ret >= 0) {
+               const struct server_cmd_user_data *ud = lls_user_data(lcmd);
+               ret = ud->handler(cc, lpr);
+               lls_free_parse_result(lpr, lcmd);
+       } else
+               send_errctx(cc, errctx);
+       free_argv(argv);
        mutex_lock(mmd_mutex);
        mmd->num_commands++;
-       if (ret >= 0 && (cmd->perms & AFS_WRITE))
+       if (ret >= 0 && (perms & AFS_WRITE))
                mmd->events++;
        mutex_unlock(mmd_mutex);
        return ret;
@@ -915,7 +884,7 @@ static int run_command(struct command_context *cc, struct iovec *iov,
  * the function if the connection was not authenticated when the timeout
  * expires.
  *
- * \sa alarm(2), crypt.c, crypt.h
+ * \sa alarm(2), \ref crypt.c, \ref crypt.h.
  */
 __noreturn void handle_connect(int fd, const char *peername)
 {
@@ -994,10 +963,9 @@ __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,
-               cf.aes_ctr128_requested);
+       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, cf.aes_ctr128_requested);
+               SESSION_KEY_LEN);
        ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
index 5e1a4ce3edc1a8812948ada45eecd2f76df4f7be..5b01eb79e3500f34d135d1bdc8db6ac8ec4ad59c 100644 (file)
--- a/command.h
+++ b/command.h
@@ -6,19 +6,55 @@ struct command_context {
        const char *peer;
        /** The paraslash user that executes this command. */
        struct user *u;
-       /** Argument count. */
-       int argc;
-       /** Argument vector. */
-       char **argv;
        /** File descriptor and crypto keys. */
        struct stream_cipher_context scc;
 };
 
+/** Prototype of a server command handler. */
+typedef int (*server_cmd_handler_t)(struct command_context *,
+               struct lls_parse_result *);
+
+/**
+ * The lopsub user data structure for server commands.
+ *
+ * One such structure exists for each server command. Lopsub maintains
+ * references to the user data structures and provides lls_user_data() for
+ * applications to fetch the user data pointer of a given command. This
+ * mechanism is used by the dispatcher of command.c to run the command handler.
+ */
+struct server_cmd_user_data {
+       /** Pointer to the command handler. */
+       server_cmd_handler_t handler;
+};
+
+/** Define the user data structure for one command. */
+#define EXPORT_SERVER_CMD_HANDLER(_cmd) \
+       const struct server_cmd_user_data lsg_server_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
+
+/** Get the lopsub command pointer by command name. */
+#define SERVER_CMD_CMD_PTR(_cmd) \
+       (lls_cmd(LSG_SERVER_CMD_CMD_ ## _cmd, server_cmd_suite))
+
+/** Get the lopsub parse result of an option. */
+#define SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+               (lls_opt_result(LSG_SERVER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+
+/** How many times an option was given. */
+#define SERVER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
+/** Fetch the (first) argument given to an option of type uint32. */
+#define SERVER_CMD_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
 int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
                int band, bool dont_free);
 __printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
                const char *fmt, ...);
 int send_strerror(struct command_context *cc, int err);
+int send_errctx(struct command_context *cc, char *errctx);
 int recv_sb(struct stream_cipher_context *scc,
                enum sb_designator expected_band,
                size_t max_size, struct iovec *result);
diff --git a/command_util.bash b/command_util.bash
deleted file mode 100755 (executable)
index 5e9dbcc..0000000
+++ /dev/null
@@ -1,291 +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
-}
-
-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
index d0d96fd960955bcccedc731eb33221d5369e778f..658976423faa1115b710102633beff6f3093151c 100644 (file)
  */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "compress_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
 
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(COMPRESS, _opt, _lpr))
+
 /** Data specific to the compress filter. */
 struct private_compress_data {
        /** The current multiplier. */
        unsigned current_gain;
-       /** Points to the configuration data for this instance of the compress filter. */
-       struct compress_filter_args_info *conf;
        /** Maximal admissible gain. */
        unsigned max_gain;
        /** Number of samples already seen. */
@@ -51,9 +51,9 @@ static int compress_post_select(__a_unused struct sched *s, void *context)
        char *inbuf;
        size_t length, i;
        int16_t *ip, *op;
-       unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
-               mask = (1 << pcd->conf->blocksize_arg) - 1;
-
+       uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+       unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr),
+               mask = (1 << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1;
        //inplace = false;
 next_buffer:
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -86,7 +86,7 @@ next_buffer:
                if (sample > 32767) { /* clip */
                        sample = 32767;
                        pcd->current_gain = (3 * pcd->current_gain +
-                               (1 << pcd->conf->inertia_arg)) / 4;
+                               (1 << inertia)) / 4;
                        pcd->peak = 0;
                } else if (sample > pcd->peak)
                        pcd->peak = sample;
@@ -95,12 +95,12 @@ next_buffer:
                        continue;
 //             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
 //                     pcd->peak);
-               if (pcd->peak < pcd->conf->target_level_arg) {
+               if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) {
                        if (pcd->current_gain < pcd->max_gain)
                                pcd->current_gain++;
                } else
                        pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
-                               1U << pcd->conf->inertia_arg);
+                               1U << inertia);
                pcd->peak = 0;
        }
        if (inplace)
@@ -116,47 +116,21 @@ err:
        return ret;
 }
 
-/** TODO: Add sanity checks */
-static int compress_parse_config(int argc, char **argv, void **config)
-{
-       struct compress_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       compress_filter_cmdline_parser(argc, argv, conf);
-       *config = conf;
-       return 1;
-}
-
 static void compress_open(struct filter_node *fn)
 {
-       struct private_compress_data *pcd = para_calloc(
-               sizeof(struct private_compress_data));
-       pcd->conf = fn->conf;
+       struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+       uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+       uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
+
        fn->private_data = pcd;
        fn->min_iqs = 2; /* 16 bit audio */
-       pcd->current_gain = 1 << pcd->conf->inertia_arg;
-       pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
-}
-
-static void compress_free_config(void *conf)
-{
-       compress_filter_cmdline_parser_free(conf);
+       pcd->current_gain = 1U << inertia;
+       pcd->max_gain = 1U << (inertia + aggressiveness);
 }
 
-/**
- * The init function of the compress filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void compress_filter_init(struct filter *f)
-{
-       struct compress_filter_args_info dummy;
-
-       compress_filter_cmdline_parser_init(&dummy);
-       f->open = compress_open;
-       f->close = compress_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = compress_post_select;
-       f->parse_config = compress_parse_config;
-       f->free_config = compress_free_config;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter);
-}
+const struct filter lsg_filter_cmd_com_compress_user_data = {
+       .open = compress_open,
+       .close = compress_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = compress_post_select,
+};
index 0b00cc2af9f006d492975a54210ce26f4e5aa56e..c2cb47e6f993c6cbbad343208ce634af8bf80173 100644 (file)
@@ -6,7 +6,6 @@ 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_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])])
@@ -51,39 +50,18 @@ AC_DEFUN([LIB_SUBST_FLAGS], [
 AC_USE_SYSTEM_EXTENSIONS
 AC_C_BIGENDIAN()
 
-AC_PATH_PROG([GENGETOPT], [gengetopt])
-test -z "$GENGETOPT" && AC_MSG_ERROR(
-       [gengetopt is required to build this package])
-
 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_PATH_PROG([lopsubgen], [lopsubgen])
+test -z "$lopsubgen" && AC_MSG_ERROR(
+       [lopsubgen is required to build this package])
 
 AC_PROG_CC
 AC_PROG_CPP
 
 executables="recv filter audioc write afh play"
-################################################################## clock_gettime
-clock_gettime_lib=
-AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
-       AC_CHECK_LIB([rt], [clock_gettime], [clock_gettime_lib=rt], [], [])
-])
-if test -n "$clock_gettime_lib"; then
-       AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [
-               define to 1 if clock_gettime() is supported])
-fi
-if test "$clock_gettime_lib" = "rt"; then
-       AC_SUBST(clock_gettime_ldflags, -lrt)
-fi
-
 ########################################################################### osl
 STASH_FLAGS
 LIB_ARG_WITH([osl], [-losl])
@@ -92,6 +70,22 @@ AC_CHECK_HEADER(osl.h, [], [HAVE_OSL=no])
 AC_CHECK_LIB([osl], [osl_open_table], [], [HAVE_OSL=no])
 LIB_SUBST_FLAGS(osl)
 UNSTASH_FLAGS
+######################################################################## lopsub
+STASH_FLAGS
+LIB_ARG_WITH([lopsub], [-llopsub])
+HAVE_LOPSUB=yes
+AC_CHECK_HEADER(lopsub.h, [], [HAVE_LOPSUB=no])
+AC_CHECK_LIB([lopsub], [lls_merge], [], [HAVE_LOPSUB=no])
+if test $HAVE_LOPSUB = no; then AC_MSG_ERROR([
+       The lopsub library is required to build this software, but
+       the above checks indicate it is not installed on your system.
+       Run the following command to download a copy.
+               git clone git://git.tuebingen.mpg.de/lopsub.git
+       Install the library, then run this configure script again.
+])
+fi
+LIB_SUBST_FLAGS([lopsub])
+UNSTASH_FLAGS
 ######################################################################## openssl
 STASH_FLAGS
 HAVE_OPENSSL=yes
@@ -182,15 +176,6 @@ AC_MSG_RESULT($have_ucred)
 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 &&
-AC_CHECK_DECL(
-       [gengetopt_args_info_description],
-       [ggo_descriptions_declared=yes],
-       [ggo_descriptions_declared=no],
-       [#include "conftest-ggo.h"]
-)
-AC_SUBST(ggo_descriptions_declared)
 ########################################################################### curses
 STASH_FLAGS
 LIB_ARG_WITH([curses], [])
@@ -211,25 +196,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 ]])],[have_ip_mreqn=yes],[have_ip_mreqn=no])
 AC_MSG_RESULT($have_ip_mreqn)
 if test ${have_ip_mreqn} = yes; then
-       AC_DEFINE(HAVE_IP_MREQN, 1, define to 1 you have struct ip_mreqn)
-fi
-########################################################################### osx
-
-AC_MSG_CHECKING(for CoreAudio (MacOs))
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-       #include <CoreAudio/CoreAudio.h>
-]], [[
-       AudioDeviceID id;
-]])],[have_core_audio=yes],[have_core_audio=no])
-AC_MSG_RESULT($have_core_audio)
-if test ${have_core_audio} = yes; then
-       f1="-framework CoreAudio"
-       f2="-framework AudioToolbox"
-       f3="-framework AudioUnit"
-       f4="-framework CoreServices"
-       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)
+       AC_DEFINE(HAVE_IP_MREQN, 1, define to 1 if you have struct ip_mreqn)
 fi
 ########################################################################### ogg
 STASH_FLAGS
@@ -297,10 +264,12 @@ AC_DEFUN([NEED_FLAC_OBJECTS], [{
 }])
 ########################################################################### faad
 STASH_FLAGS
-LIB_ARG_WITH([faad], [-lfaad])
+LIB_ARG_WITH([faad], [-lfaad -lmp4ff])
 HAVE_FAAD=yes
 AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
+AC_CHECK_HEADER(mp4ff.h, [], HAVE_FAAD=no)
 AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
+AC_CHECK_LIB([mp4ff], [mp4ff_meta_get_artist], [], HAVE_FAAD=no)
 LIB_SUBST_FLAGS(faad)
 UNSTASH_FLAGS
 ########################################################################### mad
@@ -377,19 +346,10 @@ 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
@@ -428,7 +388,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
                wma_common
                sideband
                version
-               ggo
        "
        if test "$CRYPTOLIB" = openssl; then
                server_errlist_objs="$server_errlist_objs crypt"
@@ -440,10 +399,10 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
        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"
+       if test $HAVE_FAAD = yes; then
+               server_errlist_objs="$server_errlist_objs aac_afh"
        fi
-       server_objs="add_cmdline($server_cmdline_objs) $server_errlist_objs"
+       server_objs="$server_errlist_objs"
        AC_SUBST(server_objs, add_dot_o($server_objs))
 else
        build_server="no"
@@ -452,7 +411,6 @@ fi
 if test -n "$CRYPTOLIB"; then
        build_client="yes"
        executables="$executables client"
-       client_cmdline_objs="client"
        client_errlist_objs="
                client
                net
@@ -468,7 +426,6 @@ if test -n "$CRYPTOLIB"; then
                crypt_common
                base64
                version
-               ggo
        "
        if test "$CRYPTOLIB" = openssl; then
                client_errlist_objs="$client_errlist_objs crypt"
@@ -478,8 +435,8 @@ if test -n "$CRYPTOLIB"; then
        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))
+       client_objs="$client_errlist_objs"
+       AC_SUBST(client_objs, add_dot_o($client_errlist_objs))
 else
        build_client="no"
 fi
@@ -488,18 +445,6 @@ 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
@@ -526,7 +471,6 @@ if test -n "$CRYPTOLIB"; then
                audiod_command
                fecdec_filter
                client_common
-               ggo
                udp_recv
                color
                fec
@@ -544,10 +488,6 @@ if test -n "$CRYPTOLIB"; then
        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"
@@ -565,31 +505,26 @@ if test -n "$CRYPTOLIB"; then
                audiod_audio_formats="$audiod_audio_formats flac"
        }
        if test $HAVE_FAAD = yes; then
-               audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
+               audiod_errlist_objs="$audiod_errlist_objs aacdec_filter"
                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"
+       audiod_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)"
@@ -600,53 +535,27 @@ if test -n "$CRYPTOLIB"; then
 else
        build_audiod="no"
 fi
-########################################################################### fade
+########################################################################### mixer
 if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
-       build_fade="yes"
-       executables="$executables fade"
-       fade_cmdline_objs="fade"
-       fade_errlist_objs="fade exec string fd version ggo"
+       build_mixer="yes"
+       executables="$executables mixer"
+       mixer_errlist_objs="mixer exec string fd version"
        if test $HAVE_OSS = yes; then
-               fade_errlist_objs="$fade_errlist_objs oss_mix"
-               mixers="${mixers}oss "
-               default_mixer="OSS_MIX"
+               mixer_errlist_objs="$mixer_errlist_objs oss_mix"
        fi
        if test $HAVE_ALSA = yes; then
-               fade_errlist_objs="$fade_errlist_objs alsa_mix"
-               mixers="${mixers}alsa "
-               default_mixer="ALSA_MIX"
+               mixer_errlist_objs="$mixer_errlist_objs alsa_mix"
        fi
-       fade_objs="add_cmdline($fade_cmdline_objs) $fade_errlist_objs"
-       AC_SUBST(fade_objs, add_dot_o($fade_objs))
-       enum="$(
-               for i in $mixers; do
-                       printf "${i}_MIX, " | tr '[a-z]' '[A-Z]'
-               done
-       )"
-       AC_DEFINE_UNQUOTED(MIXER_ENUM, $enum NUM_SUPPORTED_MIXERS,
-               enum of supported mixers)
-       AC_DEFINE_UNQUOTED(DEFAULT_MIXER, $default_mixer,
-               use this mixer if none was specified)
-       names="$(for i in $mixers; do printf \"$i\",' ' ; done)"
-       AC_DEFINE_UNQUOTED(MIXER_NAMES, $names, supported mixer names)
-       inits="$(
-               for i in $mixers; do
-                       printf 'extern void '$i'_mix_init(struct mixer *); '
-               done
-       )"
-       AC_DEFINE_UNQUOTED(DECLARE_MIXER_INITS, $inits,
-               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_objs="$mixer_errlist_objs"
+       AC_SUBST(mixer_objs, add_dot_o($mixer_objs))
 else
-       build_fade="no"
+       build_mixer="no"
        AC_MSG_WARN([no mixer support])
 fi
 ########################################################################### gui
 if test $HAVE_CURSES = yes; then
        build_gui="yes"
        executables="$executables gui"
-       gui_cmdline_objs="gui"
        gui_errlist_objs="
                exec
                signal
@@ -659,24 +568,14 @@ if test $HAVE_CURSES = yes; then
                time
                sched
                version
-               ggo
        "
-       gui_objs="add_cmdline($gui_cmdline_objs) $gui_errlist_objs"
+       gui_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
 ######################################################################## filter
-filters="
-       compress
-       wav
-       amp
-       fecdec
-       wmadec
-       prebuffer
-       sync
-"
 filter_errlist_objs="
        filter_common
        wav_filter
@@ -688,7 +587,6 @@ filter_errlist_objs="
        sched
        fd
        amp_filter
-       ggo
        fecdec_filter
        fec
        version
@@ -702,65 +600,23 @@ filter_errlist_objs="
        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"
-}
+NEED_VORBIS_OBJECTS && filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+NEED_SPEEX_OBJECTS && filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
+NEED_OPUS_OBJECTS && filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+NEED_FLAC_OBJECTS && filter_errlist_objs="$filter_errlist_objs flacdec_filter"
 if test $HAVE_FAAD = yes; then
-       filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
-       filters="$filters aacdec"
+       filter_errlist_objs="$filter_errlist_objs aacdec_filter"
 fi
 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_SAMPLERATE = yes; then
        filter_errlist_objs="$filter_errlist_objs resample_filter check_wav"
-       filter_cmdline_objs="$filter_cmdline_objs resample_filter"
-       filters="$filters resample"
 fi
-filters="$(echo $filters)"
-AC_SUBST(filters)
-filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs"
+filter_objs="$filter_errlist_objs"
 
 AC_SUBST(filter_objs, add_dot_o($filter_objs))
-
-enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
-       enum of supported filters)
-inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
-array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
 ########################################################################## recv
-recv_cmdline_objs="
-       recv
-       http_recv
-       dccp_recv
-       udp_recv
-       afh_recv
-"
-
 recv_errlist_objs="
        http_recv
        recv_common
@@ -772,7 +628,6 @@ recv_errlist_objs="
        fd
        sched
        stdout
-       ggo
        udp_recv
        buffer_tree
        afh_recv
@@ -788,15 +643,13 @@ NEED_SPEEX_OBJECTS && recv_errlist_objs="$recv_errlist_objs spx_afh spx_common"
 NEED_OPUS_OBJECTS && recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
 NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
 
-if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then
-       recv_errlist_objs="$recv_errlist_objs aac_afh aac_common"
+if test $HAVE_FAAD = yes; then
+       recv_errlist_objs="$recv_errlist_objs aac_afh"
 fi
-recv_objs="add_cmdline($recv_cmdline_objs) $recv_errlist_objs"
-AC_SUBST(receivers, "http dccp udp afh")
+recv_objs="$recv_errlist_objs"
 AC_SUBST(recv_objs, add_dot_o($recv_objs))
 ########################################################################### afh
 audio_format_handlers="mp3 wma"
-afh_cmdline_objs="afh"
 afh_errlist_objs="
        afh
        string
@@ -807,7 +660,6 @@ afh_errlist_objs="
        wma_afh
        wma_common
        version
-       ggo
 "
 NEED_OGG_OBJECTS && afh_errlist_objs="$afh_errlist_objs ogg_afh_common"
 NEED_VORBIS_OBJECTS && {
@@ -826,12 +678,12 @@ 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"
+if test $HAVE_FAAD = yes; then
+       afh_errlist_objs="$afh_errlist_objs aac_afh"
        audio_format_handlers="$audio_format_handlers aac"
 fi
 
-afh_objs="add_cmdline($afh_cmdline_objs) $afh_errlist_objs"
+afh_objs="$afh_errlist_objs"
 
 AC_SUBST(afh_objs, add_dot_o($afh_objs))
 ########################################################################## play
@@ -839,7 +691,6 @@ play_errlist_objs="
        play
        fd
        sched
-       ggo
        buffer_tree
        time
        string
@@ -868,22 +719,6 @@ play_errlist_objs="
        version
        sync_filter
 "
-play_cmdline_objs="
-       http_recv
-       dccp_recv
-       udp_recv
-       afh_recv
-       compress_filter
-       amp_filter
-       prebuffer_filter
-       file_write
-       play
-       sync_filter
-"
-if test "$have_core_audio" = "yes"; then
-       play_errlist_objs="$play_errlist_objs osx_write ipc"
-       play_cmdline_objs="$play_cmdline_objs osx_write"
-fi
 NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common"
 NEED_VORBIS_OBJECTS && {
        play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
@@ -901,45 +736,30 @@ NEED_FLAC_OBJECTS && {
        play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
 }
 if test $HAVE_FAAD = yes; then
-       play_errlist_objs="$play_errlist_objs aacdec_filter"
-fi
-if test $HAVE_MP4V2 = yes; then
-       play_errlist_objs="$play_errlist_objs aac_afh"
-fi
-if test $HAVE_MP4V2 = yes || test $HAVE_FAAD = yes; then
-       play_errlist_objs="$play_errlist_objs aac_common"
+       play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter"
 fi
 if test $HAVE_MAD = yes; then
-       play_cmdline_objs="$play_cmdline_objs mp3dec_filter"
        play_errlist_objs="$play_errlist_objs mp3dec_filter"
 fi
 if test $HAVE_OSS = yes; then
        play_errlist_objs="$play_errlist_objs oss_write"
-       play_cmdline_objs="$play_cmdline_objs oss_write"
 fi
 if test $HAVE_ALSA = yes; then
        play_errlist_objs="$play_errlist_objs alsa_write"
-       play_cmdline_objs="$play_cmdline_objs alsa_write"
 fi
 NEED_AO_OBJECTS && {
        play_errlist_objs="$play_errlist_objs ao_write"
-       play_cmdline_objs="$play_cmdline_objs ao_write"
 }
 if test $HAVE_READLINE = yes; then
        play_errlist_objs="$play_errlist_objs interactive"
 fi
 if test $HAVE_SAMPLERATE = yes; then
        play_errlist_objs="$play_errlist_objs resample_filter check_wav"
-       play_cmdline_objs="$play_cmdline_objs resample_filter"
 fi
 
-play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs"
+play_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
@@ -950,59 +770,28 @@ write_errlist_objs="
        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"
+write_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
@@ -1012,7 +801,7 @@ if test $HAVE_READLINE = yes; then
                time
        "
 fi
-audioc_objs="add_cmdline($audioc_cmdline_objs) $audioc_errlist_objs"
+audioc_objs="$audioc_errlist_objs"
 AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
 ################################################################## status items
 
@@ -1022,7 +811,7 @@ attributes_txt decoder_flags audiod_status play_time attributes_bitmap
 offset seconds_total stream_start current_time audiod_uptime image_id
 lyrics_id duration directory lyrics_name image_name path hash channels
 last_played num_chunks chunk_time amplification artist title year album
-comment"
+comment max_chunk_size"
 
 result=
 for i in $status_items; do
@@ -1052,15 +841,11 @@ unix socket credentials: $have_ucred
 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
 
 para_server: $build_server
 para_gui: $build_gui
-para_fade: $build_fade
+para_mixer: $build_mixer
 para_client: $build_client
 para_audiod: $build_audiod
 ])
diff --git a/crypt.c b/crypt.c
index 8116fb6eeacdbeaa0f1f22d14cfcc3726ee3bbbf..f1e42d4a228f7c0c498ac505575b6a0f3667bf27 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -11,7 +11,6 @@
 #include <sys/socket.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
-#include <openssl/rc4.h>
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
@@ -61,41 +60,24 @@ void init_random_seed_or_die(void)
        srandom(seed);
 }
 
-static EVP_PKEY *load_key(const char *file, int private)
+static int get_private_key(const char *path, RSA **rsa)
 {
-       BIO *key;
-       EVP_PKEY *pkey = NULL;
-       int ret = check_key_file(file, private);
-
-       if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               return NULL;
-       }
-       key = BIO_new(BIO_s_file());
-       if (!key)
-               return NULL;
-       if (BIO_read_filename(key, file) > 0) {
-               if (private == LOAD_PRIVATE_KEY)
-                       pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL);
-               else
-                       pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL);
-       }
-       BIO_free(key);
-       return pkey;
-}
-
-static int get_openssl_key(const char *key_file, RSA **rsa, int private)
-{
-       EVP_PKEY *key = load_key(key_file, private);
-
-       if (!key)
-               return (private == LOAD_PRIVATE_KEY)? -E_PRIVATE_KEY
-                       : -E_PUBLIC_KEY;
-       *rsa = EVP_PKEY_get1_RSA(key);
-       EVP_PKEY_free(key);
-       if (!*rsa)
-               return -E_RSA;
-       return RSA_size(*rsa);
+       EVP_PKEY *pkey;
+       BIO *bio = BIO_new(BIO_s_file());
+
+       *rsa = NULL;
+       if (!bio)
+               return -E_PRIVATE_KEY;
+       if (BIO_read_filename(bio, path) <= 0)
+               goto bio_free;
+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+       if (!pkey)
+               goto bio_free;
+       *rsa = EVP_PKEY_get1_RSA(pkey);
+       EVP_PKEY_free(pkey);
+bio_free:
+       BIO_free(bio);
+       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
 }
 
 /*
@@ -160,8 +142,7 @@ fail:
        return ret;
 }
 
-int get_asymmetric_key(const char *key_file, int private,
-               struct asymmetric_key **result)
+int get_public_key(const char *key_file, struct asymmetric_key **result)
 {
        struct asymmetric_key *key = NULL;
        void *map = NULL;
@@ -171,21 +152,13 @@ int get_asymmetric_key(const char *key_file, int private,
        char *cp;
 
        key = para_malloc(sizeof(*key));
-       if (private) {
-               ret = get_openssl_key(key_file, &key->rsa, LOAD_PRIVATE_KEY);
-               goto out;
-       }
        ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
        if (ret < 0)
                goto out;
        ret = is_ssh_rsa_key(map, map_size);
        if (!ret) {
-               ret = para_munmap(map, map_size);
-               map = NULL;
-               if (ret < 0)
-                       goto out;
-               ret = get_openssl_key(key_file, &key->rsa, LOAD_PUBLIC_KEY);
-               goto out;
+               para_munmap(map, map_size);
+               return -E_SSH_PARSE;
        }
        cp = map + ret;
        encoded_size = map_size - ret;
@@ -215,7 +188,7 @@ out:
        return ret;
 }
 
-void free_asymmetric_key(struct asymmetric_key *key)
+void free_public_key(struct asymmetric_key *key)
 {
        if (!key)
                return;
@@ -229,11 +202,17 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
        struct asymmetric_key *priv;
        int ret;
 
+       ret = check_private_key_file(key_file);
+       if (ret < 0)
+               return ret;
        if (inlen < 0)
                return -E_RSA;
-       ret = get_asymmetric_key(key_file, LOAD_PRIVATE_KEY, &priv);
-       if (ret < 0)
+       priv = para_malloc(sizeof(*priv));
+       ret = get_private_key(key_file, &priv->rsa);
+       if (ret < 0) {
+               free(priv);
                return ret;
+       }
        /*
         * RSA is vulnerable to timing attacks. Generate a random blinding
         * factor to protect against this kind of attack.
@@ -247,7 +226,8 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
        if (ret <= 0)
                ret = -E_DECRYPT;
 out:
-       free_asymmetric_key(priv);
+       RSA_free(priv->rsa);
+       free(priv);
        return ret;
 }
 
@@ -264,26 +244,16 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
 }
 
 struct stream_cipher {
-       bool use_aes;
-       union {
-               RC4_KEY rc4_key;
-               EVP_CIPHER_CTX *aes;
-       } context;
+       EVP_CIPHER_CTX *aes;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len,
-               bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
 
-       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,
+       sc->aes = EVP_CIPHER_CTX_new();
+       EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
                data + AES_CRT128_BLOCK_SIZE);
        return sc;
 }
@@ -292,40 +262,10 @@ void sc_free(struct stream_cipher *sc)
 {
        if (!sc)
                return;
-       EVP_CIPHER_CTX_free(sc->context.aes);
+       EVP_CIPHER_CTX_free(sc->aes);
        free(sc);
 }
 
-/**
- * The RC4() implementation of openssl apparently reads and writes data in
- * blocks of 8 bytes. So we have to make sure our buffer sizes are a multiple
- * of this.
- */
-#define RC4_ALIGN 8
-
-static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst)
-{
-       size_t len = src->iov_len, l1, l2;
-
-       assert(len > 0);
-       assert(len < ((typeof(src->iov_len))-1) / 2);
-       l1 = ROUND_DOWN(len, RC4_ALIGN);
-       l2 = ROUND_UP(len, RC4_ALIGN);
-
-       *dst = (typeof(*dst)) {
-               /* Add one for the terminating zero byte. */
-               .iov_base = para_malloc(l2 + 1),
-               .iov_len = len
-       };
-       RC4(key, l1, src->iov_base, dst->iov_base);
-       if (len > l1) {
-               unsigned char remainder[RC4_ALIGN] = "";
-               memcpy(remainder, src->iov_base + l1, len - l1);
-               RC4(key, len - l1, remainder, dst->iov_base + l1);
-       }
-       ((char *)dst->iov_base)[len] = '\0';
-}
-
 static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
                struct iovec *dst)
 {
@@ -347,9 +287,7 @@ static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
 
 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);
+       return aes_ctr128_crypt(sc->aes, src, dst);
 }
 
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
diff --git a/crypt.h b/crypt.h
index 9be7a23e6da4d6d1796d4d98da7d8280152c6c2a..5e25748c09551bf1b33e7b142baaafd73df5bd36 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -51,22 +51,21 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
  * Read an asymmetric key from a file.
  *
  * \param key_file The file containing the key.
- * \param private if non-zero, read the private key, otherwise the public key.
  * \param result The key structure is returned here.
  *
  * \return The size of the key on success, negative on errors.
  */
-int get_asymmetric_key(const char *key_file, int private,
-               struct asymmetric_key **result);
+int get_public_key(const char *key_file, struct asymmetric_key **result);
 
 /**
- * Deallocate an asymmetric key structure.
+ * Deallocate a public key.
  *
  * \param key Pointer to the key structure to free.
  *
- * This must be called for any key obtained by get_asymmetric_key().
+ * This should be called for keys obtained by get_public_key() if the key is no
+ * longer needed.
  */
-void free_asymmetric_key(struct asymmetric_key *key);
+void free_public_key(struct asymmetric_key *key);
 
 
 /**
@@ -119,16 +118,14 @@ struct stream_cipher_context {
 };
 
 /**
- * Allocate and initialize a stream cipher structure.
+ * Allocate and initialize an aes_ctr128 stream cipher structure.
  *
  * \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,
-               bool use_aes);
+struct stream_cipher *sc_new(const unsigned char *data, int len);
 
 /**
  * Encrypt or decrypt a buffer using a stream cipher.
index f9a69d9490057382126e4efe04a15bfda9025419..fccdd2efbda6da1f8dac7a3a4731885e4e8f7290 100644 (file)
@@ -14,4 +14,4 @@
 size_t is_ssh_rsa_key(char *data, size_t size);
 uint32_t read_ssh_u32(const void *vp);
 int check_ssh_key_header(const unsigned char *blob, int blen);
-int check_key_file(const char *file, bool private_key);
+int check_private_key_file(const char *file);
index a05572df85b1de5fb5e1043abfc34c66e26c65e7..1fd8189ca3c5a201bd2d227bb366bd800757a5a5 100644 (file)
@@ -103,25 +103,22 @@ int check_ssh_key_header(const unsigned char *blob, int blen)
 }
 
 /**
- * Check existence and permissions of a key file.
+ * Check existence and permissions of a private key file.
  *
  * \param file The path of the key file.
- * \param private_key Whether this is a private key.
  *
- * This checks whether the file exists. If it is a private key, we additionally
- * check that the permissions are restrictive enough. It is considered an error
- * if we own the file and it is readable for others.
+ * This checks whether the file exists and its permissions are restrictive
+ * enough. It is considered an error if we own the file and it is readable for
+ * others.
  *
  * \return Standard.
  */
-int check_key_file(const char *file, bool private_key)
+int check_private_key_file(const char *file)
 {
        struct stat st;
 
        if (stat(file, &st) != 0)
                return -ERRNO_TO_PARA_ERROR(errno);
-       if (!private_key)
-               return 0;
        if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0)
                return -E_KEY_PERM;
        return 1;
index 20cdc6ae605a99f56f35bf41f0abeff7d508936d..4e15824f350989ea7d695d0c995bbc2160656b11 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -374,8 +374,6 @@ time_t daemon_get_uptime(const struct timeval *current_time)
  * \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 *daemon_get_uptime_str(const struct timeval *current_time)
 {
index 253586e1f7ee4f90867a157757a34a227458047a..3385d66c42d113b382d18eb3fc8c4a7c60b44f74 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
 #include "string.h"
 #include "net.h"
 #include "fd.h"
 
-#include "dccp_recv.cmdline.h"
-
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -39,25 +38,56 @@ static void dccp_recv_close(struct receiver_node *rn)
        btr_pool_free(rn->btrp);
 }
 
+/* Check whether the host supports the requested 'ccid' arguments. */
+static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
+{
+       uint8_t *ccids;
+       int i, j, ret, nccids;
+       unsigned given = RECV_CMD_OPT_GIVEN(DCCP, CCID, lpr);
+
+       ret = dccp_available_ccids(&ccids);
+       if (ret < 0)
+               return ret;
+       nccids = ret;
+       for (i = 0; i < given; i++) {
+               uint32_t val = lls_uint32_val(i,
+                       RECV_CMD_OPT_RESULT(DCCP, CCID, lpr));
+               for (j = 0; j < nccids && ccids[j] != val; j++)
+                       ;
+               if (j == nccids) {
+                       PARA_ERROR_LOG("'CCID-%u' not supported on this host\n",
+                               val);
+                       return -ERRNO_TO_PARA_ERROR(EINVAL);
+               }
+       }
+       return 1;
+}
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
-       struct dccp_recv_args_info *conf = rn->conf;
+       struct lls_parse_result *lpr = rn->lpr;
        struct flowopts *fo = NULL;
        uint8_t *ccids = NULL;
        int fd, ret, i;
+       const struct lls_opt_result *r_c = RECV_CMD_OPT_RESULT(DCCP, CCID, lpr);
+       const char *host = RECV_CMD_OPT_STRING_VAL(DCCP, HOST, lpr);
+       uint32_t port = RECV_CMD_OPT_UINT32_VAL(DCCP, PORT, lpr);
+       unsigned given;
 
+       ret = dccp_recv_ccid_support_check(lpr);
+       if (ret < 0)
+               return ret;
        /* Copy CCID preference list (u8 array required) */
-       if (conf->ccid_given) {
-               ccids = para_malloc(conf->ccid_given);
-               fo    = flowopt_new();
-
-               for (i = 0; i < conf->ccid_given; i++)
-                       ccids[i] = conf->ccid_arg[i];
-
+       given = lls_opt_given(r_c);
+       if (given) {
+               ccids = para_malloc(given);
+               fo = flowopt_new();
+               for (i = 0; i < given; i++)
+                       ccids[i] = lls_int32_val(i, r_c);
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo);
+       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
@@ -83,42 +113,6 @@ err:
        return ret;
 }
 
-/**
- * Check whether the host supports the requested 'ccid' arguments.
- * \param conf DCCP receiver arguments.
- * \return True if all CCIDs requested in \a conf are supported.
- */
-static bool dccp_recv_ccid_support_check(struct dccp_recv_args_info *conf)
-{
-       uint8_t *ccids;
-       int i, j, nccids;
-
-       nccids = dccp_available_ccids(&ccids);
-       if (nccids <= 0)
-               return false;
-
-       for (i = 0; i < conf->ccid_given; i++) {
-               for (j = 0; j < nccids && ccids[j] != conf->ccid_arg[i]; j++)
-                       ;
-               if (j == nccids) {
-                       PARA_ERROR_LOG("'CCID-%d' not supported on this host.\n",
-                                       conf->ccid_arg[i]);
-                       return false;
-               }
-       }
-       return true;
-}
-
-static void *dccp_recv_parse_config(int argc, char **argv)
-{
-       struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
-       dccp_recv_cmdline_parser(argc, argv, tmp);
-       if (!dccp_recv_ccid_support_check(tmp))
-               exit(EXIT_FAILURE);
-       return tmp;
-}
-
 static void dccp_recv_pre_select(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
@@ -161,30 +155,10 @@ out:
        return ret;
 }
 
-static void dccp_recv_free_config(void *conf)
-{
-       dccp_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the dccp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void dccp_recv_init(struct receiver *r)
-{
-       struct dccp_recv_args_info dummy;
-
-       dccp_recv_cmdline_parser_init(&dummy);
-       r->open = dccp_recv_open;
-       r->close = dccp_recv_close;
-       r->pre_select = dccp_recv_pre_select;
-       r->post_select = dccp_recv_post_select;
-       r->parse_config = dccp_recv_parse_config;
-       r->free_config = dccp_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv);
-       dccp_recv_cmdline_parser_free(&dummy);
-}
+/** See \ref recv_init(). */
+const struct receiver lsg_recv_cmd_com_dccp_user_data = {
+       .open = dccp_recv_open,
+       .close = dccp_recv_close,
+       .pre_select = dccp_recv_pre_select,
+       .post_select = dccp_recv_post_select,
+};
index 92dd933347d2c48d857355da34c13673da54968b..4ad25e73d0e915bcd8c039a6688854694456dc80 100644 (file)
@@ -18,7 +18,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
@@ -32,7 +34,6 @@
 #include "fd.h"
 #include "close_on_fork.h"
 #include "chunk_queue.h"
-#include "server.cmdline.h"
 #include "acl.h"
 
 static struct sender_status dccp_sender_status, *dss = &dccp_sender_status;
@@ -91,6 +92,7 @@ static int dccp_init_fec(struct sender_client *sc)
 {
        int mps, ret;
        socklen_t ml = sizeof(mps);
+       uint32_t mss; /* max slize size */
 
        /* If call fails, return some sensible minimum value */
        ret = getsockopt(sc->fd, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &mps, &ml);
@@ -100,8 +102,9 @@ static int dccp_init_fec(struct sender_client *sc)
        }
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
        assert(mps > 0);
-       if (conf.dccp_max_slice_size_arg > 0)
-               mps = PARA_MIN(mps, conf.dccp_max_slice_size_arg);
+       mss = OPT_UINT32_VAL(DCCP_MAX_SLICE_SIZE);
+       if (mss > 0 && mss <= INT_MAX)
+               mps = PARA_MIN(mps, (int)mss);
        return mps;
 }
 
@@ -118,6 +121,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        struct sender_client *sc;
        struct dccp_fec_client *dfc;
        int tx_ccid;
+       uint32_t k, n;
 
        sc = accept_sender_client(dss, rfds);
        if (!sc)
@@ -134,7 +138,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        /*
         * Bypass unused CCID paths: the sender does not receive application data
         * from the client; by shutting down this unused communication path we can
-        * reduce processing costs a bit. See analogous comment in dccp_recv.c.
+        * reduce processing costs a bit. See analogous comment in \ref dccp_recv.c.
         */
        if (shutdown(sc->fd, SHUT_RD) < 0) {
                PARA_WARNING_LOG("%s\n", strerror(errno));
@@ -143,8 +147,16 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        }
        dfc = para_calloc(sizeof(*dfc));
        sc->private_data = dfc;
-       dfc->fcp.data_slices_per_group  = conf.dccp_data_slices_per_group_arg;
-       dfc->fcp.slices_per_group       = conf.dccp_slices_per_group_arg;
+       k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
+       n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
+       if (k == 0 || n == 0 || k >= n) {
+               PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
+               dfc->fcp.data_slices_per_group = 3;
+               dfc->fcp.slices_per_group = 4;
+       } else {
+               dfc->fcp.data_slices_per_group = k;
+               dfc->fcp.slices_per_group = n;
+       }
        dfc->fcp.init_fec               = dccp_init_fec;
        dfc->fcp.send_fec               = dccp_send_fec;
        dfc->fcp.need_periodic_header   = false;
@@ -216,7 +228,7 @@ static char *dccp_status(void)
  */
 void dccp_send_init(struct sender *s)
 {
-       int ret, k, n;
+       int ret;
 
        s->status = dccp_status;
        s->send = NULL;
@@ -232,18 +244,9 @@ void dccp_send_init(struct sender *s)
        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;
-
-       if (k <= 0 || n <= 0 || k >= n) {
-               PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
-               conf.dccp_data_slices_per_group_arg = 3;
-               conf.dccp_slices_per_group_arg = 4;
-       }
-
-       init_sender_status(dss, conf.dccp_access_arg, conf.dccp_access_given,
-               conf.dccp_port_arg, conf.dccp_max_clients_arg,
-               conf.dccp_default_deny_given);
+       init_sender_status(dss, OPT_RESULT(DCCP_ACCESS),
+               OPT_UINT32_VAL(DCCP_PORT), OPT_UINT32_VAL(DCCP_MAX_CLIENTS),
+               OPT_GIVEN(DCCP_DEFAULT_DENY));
        ret = generic_com_on(dss, IPPROTO_DCCP);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
diff --git a/error.h b/error.h
index 97d678176ef4b49a9892fcf67a06fc05ec902a7d..e5d2469bd5492986c231eeded9b311f0753c804f 100644 (file)
--- a/error.h
+++ b/error.h
@@ -9,11 +9,9 @@
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
-       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(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(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(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(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(MP3DEC_CORRUPT, "too many corrupt frames"), \
        PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
-       PARA_ERROR(MP4ASC, "audio spec config error"), \
-       PARA_ERROR(MP4V2, "mp4v2 library error"), \
-       PARA_ERROR(MPI_PRINT, "could not convert multi-precision integer"), \
+       PARA_ERROR(MP4FF_BAD_CHANNEL_COUNT, "mp4ff: invalid number of channels"), \
+       PARA_ERROR(MP4FF_BAD_SAMPLE, "mp4ff: invalid sample number"), \
+       PARA_ERROR(MP4FF_BAD_SAMPLERATE, "mp4ff: invalid sample rate"), \
+       PARA_ERROR(MP4FF_BAD_SAMPLE_COUNT, "mp4ff: invalid number of samples"), \
+       PARA_ERROR(MP4FF_META_READ, "mp4ff: could not read mp4 metadata"), \
+       PARA_ERROR(MP4FF_META_WRITE, "mp4ff: could not update mp4 metadata"), \
+       PARA_ERROR(MP4FF_OPEN, "mp4ff: open failed"), \
+       PARA_ERROR(MP4FF_TRACK, "mp4ff: no audio track"), \
        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(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
        PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
-       PARA_ERROR(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(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(RSA_DECODE, "RSA decoding 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(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(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"), \
@@ -288,19 +273,28 @@ extern const char * const para_errlist[];
  */
 #define OSL_ERROR_BIT 29
 
+#define LLS_ERROR_BIT 28
+
 /** Check whether the system error bit is set. */
 #define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT)))
 
 /** Check whether the osl error bit is set. */
 #define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT)))
 
+/** Check whether the lopsub error bit is set. */
+#define IS_LLS_ERROR(num) (!!((num) & (1 << LLS_ERROR_BIT)))
+
 /** Set the system error bit for the given number. */
 #define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT))
 
 /** Set the osl error bit for the given number. */
 #define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
 
+/** Set the lopsub error bit for the error code. */
+#define LLS_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << LLS_ERROR_BIT))
+
 static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
+static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror")));
 /**
  * Paraslash's version of strerror(3).
  *
@@ -315,6 +309,10 @@ _static_inline_ const char *para_strerror(int num)
                assert(weak_osl_strerror);
                return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
        }
+       if (IS_LLS_ERROR(num)) {
+               assert(weak_lls_strerror);
+               return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+       }
        if (IS_SYSTEM_ERROR(num))
                return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
        return para_errlist[num];
@@ -337,3 +335,16 @@ _static_inline_ int osl(int ret)
                return ret;
        return -OSL_ERRNO_TO_PARA_ERROR(-ret);
 }
+
+/**
+ * Wrapper for lopsub library calls.
+ *
+ * \param ret See \ref osl().
+ * \return See \ref osl().
+ */
+_static_inline_ int lls(int ret)
+{
+       if (ret >= 0)
+               return ret;
+       return -LLS_ERRNO_TO_PARA_ERROR(-ret);
+}
diff --git a/fade.c b/fade.c
deleted file mode 100644 (file)
index 67dc4d5..0000000
--- a/fade.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file fade.c A volume fader and alarm clock for OSS. */
-
-#include <regex.h>
-
-#include "fade.cmdline.h"
-#include "para.h"
-#include "fd.h"
-#include "string.h"
-#include "mix.h"
-#include "error.h"
-#include "ggo.h"
-#include "version.h"
-
-/** Array of error strings. */
-DEFINE_PARA_ERRLIST;
-
-static struct fade_args_info conf;
-
-enum mixer_id {MIXER_ENUM};
-static char *mixer_name[] = {MIXER_NAMES};
-DECLARE_MIXER_INITS;
-static struct mixer supported_mixer[] = {MIXER_ARRAY};
-#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
-
-static int loglevel;
-static __printf_2_3 void date_log(int ll, const char *fmt, ...)
-{
-       va_list argp;
-       time_t t1;
-       struct tm *tm;
-
-       if (ll < loglevel)
-               return;
-       time(&t1);
-       tm = localtime(&t1);
-       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)
-{
-       int vol, diff, incr, ret;
-       unsigned secs;
-       struct timespec ts;
-       unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
-
-       if (fade_time <= 0)
-               return m->set(h, new_vol);
-       secs = fade_time;
-       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);
-               goto out;
-       }
-       incr = diff > 0? 1: -1;
-       diff = diff > 0? diff: -diff;
-       tmp = secs * 1000 / diff;
-       tmp2 = tmp % 1000;
-       while ((new_vol - vol) * incr > 0) {
-               ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
-               ts.tv_sec = tmp / 1000; /* really nec ?*/
-               //printf("ts.tv_sec: %i\n", ts.tv_nsec);
-               vol += incr;
-               ret = m->set(h, vol);
-               if (ret < 0)
-                       goto out;
-               //printf("vol = %i\n", vol);
-               nanosleep(&ts, NULL);
-       }
-out:
-       return ret;
-}
-
-static void client_cmd(const char *cmd)
-{
-       int ret, status, fds[3] = {0, 0, 0};
-       pid_t pid;
-       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
-
-       PARA_NOTICE_LOG("%s\n", cmdline);
-       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
-       free(cmdline);
-       if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               goto fail;
-       }
-       do
-               pid = waitpid(pid, &status, 0);
-       while (pid == -1 && errno == EINTR);
-       if (pid < 0) {
-               PARA_ERROR_LOG("%s\n", strerror(errno));
-               goto fail;
-       }
-       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
-               goto fail;
-       return;
-fail:
-       PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
-       exit(EXIT_FAILURE);
-}
-
-static void change_afs_mode(char *afs_mode)
-{
-       char *cmd;
-
-       client_cmd("stop");
-       if (!afs_mode)
-               return;
-       cmd = make_message("select %s", afs_mode);
-       client_cmd(cmd);
-       free(cmd);
-}
-
-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)
-{
-       time_t t1, wake_time_epoch;
-       unsigned int delay;
-       struct tm *tm;
-       int ret, min = conf.wake_min_arg;
-       char *fo_mood = conf.fo_mood_arg;
-       char *fi_mood = conf.fi_mood_arg;
-       char *sleep_mood = conf.sleep_mood_arg;
-       int fit = conf.fi_time_arg;
-       int fot = conf.fo_time_arg;
-       int fiv = conf.fi_vol_arg;
-       int fov = conf.fo_vol_arg;
-
-       /* calculate wake time */
-       time(&t1);
-       if (conf.wake_hour_given) {
-               int hour = conf.wake_hour_arg;
-               tm = localtime(&t1);
-               if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
-                       t1 += 86400; /* wake time is tomorrow */
-                       tm = localtime(&t1);
-               }
-               tm->tm_hour = hour;
-               tm->tm_min = min;
-               tm->tm_sec = 0;
-       } else {
-               t1 += 9 * 60 * 60; /* nine hours from now */
-               PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1);
-               tm = localtime(&t1);
-       }
-       wake_time_epoch = mktime(tm);
-       PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
-       client_cmd("stop");
-       sleep(1);
-       if (fot) {
-               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;
-               ret = fade(m, h, fov, fot);
-               if (ret < 0)
-                       return ret;
-       } else {
-               ret = m->set(h, fov);
-               if (ret < 0)
-                       return ret;
-       }
-       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)
-                       break;
-               delay = wake_time_epoch - t1 - fit;
-               PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
-                       delay, delay / 3600,
-                       (delay % 3600) / 60);
-               sleep(delay);
-       }
-       client_cmd("play");
-       ret = fade(m, h, fiv, fit);
-       PARA_INFO_LOG("fade complete, returning\n");
-       return ret;
-}
-
-static int snooze(struct mixer *m, struct mixer_handle *h)
-{
-       int ret, val;
-
-       if (conf.so_time_arg <= 0)
-               return 1;
-       ret = m->get(h);
-       if (ret < 0)
-               return ret;
-       val = ret;
-       if (val < conf.so_vol_arg)
-               ret = m->set(h, conf.so_vol_arg);
-       else
-               ret = fade(m, h, conf.so_vol_arg, conf.so_time_arg);
-       if (ret < 0)
-               return ret;
-       client_cmd("pause");
-       PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg);
-       sleep(conf.snooze_time_arg);
-       client_cmd("play");
-       return fade(m, h, conf.si_vol_arg, conf.si_time_arg);
-}
-
-static int configfile_exists(void)
-{
-       static char *config_file;
-
-       if (!conf.config_file_given) {
-               char *home = para_homedir();
-               free(config_file);
-               config_file = make_message("%s/.paraslash/fade.conf", home);
-               free(home);
-               conf.config_file_arg = config_file;
-       }
-       return file_exists(conf.config_file_arg);
-}
-
-static void init_mixers(void)
-{
-       int i;
-
-       FOR_EACH_MIXER(i) {
-               struct mixer *m = &supported_mixer[i];
-               PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n",
-                       i, mixer_name[i]);
-               m->init(m);
-       }
-}
-
-static int set_val(struct mixer *m, struct mixer_handle *h)
-{
-       return m->set(h, conf.val_arg);
-}
-
-static struct mixer *get_mixer_or_die(void)
-{
-       int i;
-
-       if (!conf.mixer_api_given)
-               i = DEFAULT_MIXER;
-       else
-               FOR_EACH_MIXER(i)
-                       if (!strcmp(mixer_name[i], conf.mixer_api_arg))
-                               break;
-       if (i < NUM_SUPPORTED_MIXERS) {
-               PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]);
-               return supported_mixer + i;
-       }
-       printf("available mixer APIs: ");
-       FOR_EACH_MIXER(i) {
-               int d = (i == DEFAULT_MIXER);
-               printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : "");
-       }
-       printf("\n");
-       exit(EXIT_FAILURE);
-}
-
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(fade);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       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;
-       struct mixer *m;
-       struct mixer_handle *h = NULL;
-
-       fade_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("fade", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       ret = configfile_exists();
-       if (!ret && conf.config_file_given) {
-               PARA_EMERG_LOG("can not read config file %s\n",
-                       conf.config_file_arg);
-               exit(EXIT_FAILURE);
-       }
-       if (ret) {
-               struct fade_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1
-               };
-               fade_cmdline_parser_config_file(conf.config_file_arg,
-                       &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       }
-       init_mixers();
-       m = get_mixer_or_die();
-       ret = m->open(conf.mixer_device_arg, &h);
-       if (ret < 0)
-               goto out;
-       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) {
-       case mode_arg_fade:
-               ret = fade(m, h, conf.fade_vol_arg, conf.fade_time_arg);
-               break;
-       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;
-       }
-out:
-       m->close(&h);
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/fd.c b/fd.c
index 6a26ce5e3d4d5f2993affc76a544e96db1a5738c..dc4fd59c93320929bd7e7f781a37f3e903a13c8f 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -405,7 +405,7 @@ __must_check int mark_fd_nonblocking(int fd)
  * This wrapper for FD_SET() passes its first two arguments to \p FD_SET. Upon
  * return, \a max_fileno contains the maximum of the old_value and \a fd.
  *
- * \sa para_select.
+ * \sa \ref para_select.
 */
 void para_fd_set(int fd, fd_set *fds, int *max_fileno)
 {
@@ -674,7 +674,7 @@ out:
  *
  * \return Standard.
  *
- * \sa munmap(2), mmap_full_file().
+ * \sa munmap(2), \ref mmap_full_file().
  */
 int para_munmap(void *start, size_t length)
 {
@@ -716,8 +716,6 @@ int write_ok(int fd)
  *
  * Common approach that opens /dev/null until it gets a file descriptor greater
  * than two.
- *
- * \sa okir's Black Hats Manual.
  */
 void valid_fd_012(void)
 {
index 1c3a37849d1309fbf3b676bf04c513628dc1c657..1b95ea4c5fd76ca926e8caec733243947254c7ca 100644 (file)
@@ -12,7 +12,6 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -481,15 +480,9 @@ static void fecdec_open(struct filter_node *fn)
        fn->min_iqs = FEC_HEADER_SIZE;
 }
 
-/**
- * The init function of the fecdec filter.
- *
- * \param f Struct to initialize.
- */
-void fecdec_filter_init(struct filter *f)
-{
-       f->close = fecdec_close;
-       f->open = fecdec_open;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = fecdec_post_select;
-}
+const struct filter lsg_filter_cmd_com_fecdec_user_data = {
+       .open = fecdec_open,
+       .pre_select = generic_filter_pre_select,
+       .post_select = fecdec_post_select,
+       .close = fecdec_close,
+};
index 5e66607e4dba4a0595da7f8fcfeae0dba0bf3bad..9837e810278ee220fce4a313d695db3b9772f3ad 100644 (file)
@@ -8,17 +8,16 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
 #include "string.h"
 #include "fd.h"
-#include "file_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the file writer. */
@@ -47,31 +46,31 @@ __must_check __malloc static char *random_filename(void)
 
 static int prepare_output_file(struct writer_node *wn)
 {
-       struct file_write_args_info *conf = wn->conf;
-       char *filename;
-       int ret;
-       struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd));
-
-       if (conf->filename_given)
-               filename = conf->filename_arg;
-       else
-               filename = random_filename();
-       ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
-       if (!conf->filename_given)
-               free(filename);
-       if (ret < 0)
-               goto out;
-       pfwd->fd = ret;
-       ret = mark_fd_blocking(pfwd->fd);
+       const unsigned flags = O_WRONLY | O_CREAT, mode = S_IRUSR | S_IWUSR;
+       int ret, fd;
+       struct private_file_write_data *pfwd;
+
+
+       if (WRITE_CMD_OPT_GIVEN(FILE, FILENAME, wn->lpr)) {
+               const char *path = WRITE_CMD_OPT_STRING_VAL(FILE, FILENAME,
+                       wn->lpr);
+               ret = para_open(path, flags, mode);
+       } else {
+               char *path = random_filename();
+               ret = para_open(path, flags, mode);
+               free(path);
+       }
        if (ret < 0)
-               goto out_close;
-       wn->private_data = pfwd;
+               return ret;
+       fd = ret;
+       ret = mark_fd_blocking(fd);
+       if (ret < 0) {
+               close(fd);
+               return ret;
+       }
+       pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+       pfwd->fd = fd;
        return 1;
-out_close:
-       close(pfwd->fd);
-out:
-       free(pfwd);
-       return ret;
 }
 
 static void file_write_pre_select(struct sched *s, void *context)
@@ -131,31 +130,9 @@ out:
        return ret;
 }
 
-__malloc static void *file_write_parse_config_or_die(int argc, char **argv)
-{
-       struct file_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       file_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void file_write_free_config(void *conf)
-{
-       file_write_cmdline_parser_free(conf);
-}
-
 /** the init function of the file writer */
-void file_write_init(struct writer *w)
-{
-       struct file_write_args_info dummy;
-
-       file_write_cmdline_parser_init(&dummy);
-       w->pre_select = file_write_pre_select;
-       w->post_select = file_write_post_select;
-       w->parse_config_or_die = file_write_parse_config_or_die;
-       w->free_config = file_write_free_config;
-       w->close = file_write_close;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(file_write);
-       file_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_file_user_data = {
+       .pre_select = file_write_pre_select,
+       .post_select = file_write_post_select,
+       .close = file_write_close,
+};
index 81901896aef25294a64599c9d77f63bf541f91e4..d5dac6758cc1a2e2041a97bf8cba4b2abe75e90a 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -7,22 +7,31 @@
 /** \file filter.c The stand-alone filter program. */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter.lsg.h"
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "stdin.h"
 #include "stdout.h"
 #include "error.h"
+#include "fd.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *lpr; /* command line options */
+
+#define CMD_PTR (lls_cmd(0, filter_suite))
+#define OPT_RESULT(_name) \
+       (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
 
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
@@ -45,48 +54,75 @@ static struct stdout_task stdout_task_struct;
 /** Pointer to the stdout task. */
 static struct stdout_task *sot = &stdout_task_struct;
 
-/** Gengetopt struct that holds the command line args. */
-static struct filter_args_info conf;
-
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel);
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(filter);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_filter_helps(OPT_GIVEN(DETAILED_HELP));
        exit(EXIT_SUCCESS);
 }
 
 static int parse_config(void)
 {
-       static char *cf; /* config file */
+       char *home, *cf; /* config file */
        struct stat statbuf;
+       int ret;
 
-       version_handle_flag("filter", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       if (!cf) {
-               char *home = para_homedir();
-               cf = make_message("%s/.paraslash/filter.conf", home);
-               free(home);
-       }
+       version_handle_flag("filter", OPT_GIVEN(VERSION));
+       handle_help_flag();
+       home = para_homedir();
+       cf = make_message("%s/.paraslash/filter.conf", home);
+       free(home);
        if (!stat(cf, &statbuf)) {
-               struct filter_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1
-               };
-               filter_cmdline_parser_config_file(cf, &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
+               void *map;
+               size_t sz;
+               int cf_argc;
+               char **cf_argv, *errctx;
+               struct lls_parse_result *cf_lpr, *merged_lpr;
+
+               ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+               if (ret != -E_EMPTY) {
+                       if (ret < 0)
+                               return ret;
+                       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
+                               &errctx));
+                       para_munmap(map, sz);
+                       if (ret < 0) {
+                               PARA_ERROR_LOG("syntax error in %s\n", cf);
+                               return ret;
+                       }
+                       cf_argc = ret;
+                       ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr,
+                               &errctx));
+                       lls_free_argv(cf_argv);
+                       if (ret < 0) {
+                               PARA_ERROR_LOG("parse error in %s\n", cf);
+                               return ret;
+                       }
+                       ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, &errctx));
+                       lls_free_parse_result(cf_lpr, CMD_PTR);
+                       if (ret < 0)
+                               return ret;
+                       lls_free_parse_result(lpr, CMD_PTR);
+                       lpr = merged_lpr;
+                       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+               }
+       }
+       if (!OPT_GIVEN(FILTER)) {
+               print_filter_list();
+               exit(EXIT_SUCCESS);
        }
-       if (!conf.filter_given)
-               return -E_NO_FILTERS;
        return 1;
 }
 
@@ -109,36 +145,37 @@ int main(int argc, char *argv[])
        const struct filter *f;
        struct btr_node *parent;
        struct filter_node **fns;
+       struct lls_parse_result *filter_lpr; /* per-filter options */
+       char *errctx;
 
-       filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       filter_init();
-       ret = parse_config();
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
        if (ret < 0)
                goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       ret = parse_config();
+       if (ret < 0)
+               goto free_lpr;
        sit->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdin"));
        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];
+       fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+       for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
+               const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
+               const char *name;
                struct filter_node *fn;
                struct task_info ti;
 
                fn = fns[i] = para_calloc(sizeof(*fn));
-               ret = check_filter_arg(fa, &fn->conf);
-               if (ret < 0) {
-                       free(fn);
-                       goto out_cleanup;
-               }
-               fn->filter_num = ret;
+               fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
+               name = filter_name(fn->filter_num);
+               fn->lpr = filter_lpr;
+               PARA_DEBUG_LOG("filter #%d: %s\n", i, 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,
+                       EMBRACE(.name = name, .parent = parent,
                        .handler = f->execute, .context = fn));
-               ti.name = f->name;
+               ti.name = name;
                ti.pre_select = f->pre_select;
                ti.post_select = f->post_select;
                ti.context = fn;
@@ -156,7 +193,6 @@ int main(int argc, char *argv[])
        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];
 
@@ -164,15 +200,21 @@ out_cleanup:
                if (f->close)
                        f->close(fn);
                btr_remove_node(&fn->btrn);
-               if (f->free_config)
-                       f->free_config(fn->conf);
+               if (f->teardown)
+                       f->teardown(fn->lpr, fn->conf);
                free(fn);
        }
        free(fns);
        btr_remove_node(&sit->btrn);
        btr_remove_node(&sot->btrn);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR);
 out:
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       }
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
index 0bd546903927d412fccef946cf8f43783ed8bc70..ab98c2442b9d13ae5158716fc2fe7f24904685e6 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -6,9 +6,6 @@
 
 /** \file filter.h Filter-related structures and exported symbols from filter_common.c. */
 
-/** The list of supported filters. */
-enum filter_enum {FILTER_ENUM};
-
 /**
  * Describes one running instance of a filter.
 */
@@ -24,6 +21,8 @@ struct filter_node {
        struct list_head callbacks;
        /** A pointer to the configuration of this instance. */
        void *conf;
+       /** The parsed command line, merged with options given in the config file. */
+       struct lls_parse_result *lpr;
        /** The buffer tree node. */
        struct btr_node *btrn;
        /** The task corresponding to this filter node. */
@@ -35,26 +34,16 @@ struct filter_node {
 /**
  * The structure associated with a paraslash filter.
  *
- * Paraslash filters are "modules" which are used to transform an audio stream.
- * struct filter contains pointers to functions that must be supplied by the
- * filter code in order to be used by the driving application (currently
- * para_audiod and para_filter).
+ * Paraslash filters are "modules" which transform an audio stream. struct
+ * filter contains methods which are implemented by each filter.
  *
  * Note: As several instances of the same filter may be running at the same
  * time, all these filter functions must be reentrant; no static non-constant
  * variables may be used.
- * \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node
+ *
+ * \sa \ref filter_node.
  */
 struct filter {
-       /** The name of the filter. */
-       const char *name;
-       /**
-        * Pointer to the filter init routine.
-        *
-        * This function is only called once at startup. It must initialize the
-        * other non-optional function pointers of this structure.
-        */
-       void (*init)(struct filter *f);
        /**
         * Open one instance of this filter.
         *
@@ -73,27 +62,29 @@ struct filter {
         */
        void (*close)(struct filter_node *fn);
        /**
-        * A pointer to the filter's command line parser.
+        * Prepare the filter according to command line options.
         *
-        * If this optional function pointer is not NULL, any filter options
-        * are passed from the main program to this command line parser once at
-        * application startup. The command line parser should check its
-        * command line options given by \a argc and \a argv and abort on
-        * errors. Success must be indicated by a non-negative return value. In
-        * this case the function should return a pointer to the
-        * filter-specific configuration data determined by \a argc and \a
-        * argv. On failure, a negative paraslash error code must be returned.
+        * In addition to the syntactic checks which are automatically performed
+        * by the lopsub functions, some filters like to also check the command
+        * line arguments semantically. Moreover, since applications may open
+        * the filter many times with the same options, filters need a method
+        * which allows them to precompute once those parts of the setup which
+        * depend only on the command line options.
+        *
+        * If this function pointer is not NULL, the function is called once at
+        * startup. The returned pointer value is made available to the ->open
+        * method via the ->conf pointer of struct filter_node.
+        *
+        * Filters are supposed to abort if the setup fails. If the function
+        * returns, it is assumed to have succeeded.
         */
-       int (*parse_config)(int argc, char **argv, void **config);
+       void *(*setup)(const struct lls_parse_result *lpr);
        /**
-        * Deallocate the memory for the configuration.
+        * Deallocate precomputed resources.
         *
-        * This is called to free whatever ->parse_config() has allocated.
+        * This should free whatever ->setup() has allocated.
         */
-       void (*free_config)(void *conf);
-
-       /** The help texts for this filter. */
-       struct ggo_help help;
+       void (*teardown)(const struct lls_parse_result *lpr, void *conf);
        /**
         * Set scheduler timeout and add file descriptors to fd sets.
         *
@@ -121,9 +112,22 @@ struct filter {
        btr_command_handler execute;
 };
 
-void filter_init(void);
-int check_filter_arg(const char *fa, void **conf);
-void print_filter_helps(unsigned flags);
+void print_filter_helps(bool detailed);
+void print_filter_list(void);
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp);
+#define FILTER_CMD(_num) (lls_cmd(_num, filter_cmd_suite))
+#define FILTER_CMD_OPT(_cmd, _opt) (lls_opt( \
+       LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, \
+       FILTER_CMD(LSG_FILTER_CMD_CMD_ ## _cmd)))
+#define FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+       (lls_opt_result(LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define FILTER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+       (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
 void generic_filter_pre_select(struct sched *s, void *context);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
@@ -139,6 +143,5 @@ static inline void write_int16_host_endian(char *buf, int val)
 #endif
 }
 
-DECLARE_FILTER_INITS
-
 const struct filter *filter_get(int filter_num);
+const char *filter_name(int filter_num);
index e9b97e54633a8770009c0f746b7daa712d4135fb..b406951e8ff145542e9ad579bde5311b7e3be1aa 100644 (file)
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "fd.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #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. */
-static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+/** Iterate over all filters. */
+#define FOR_EACH_FILTER(j) for (j = 1; lls_cmd(j, filter_cmd_suite); j++)
 
 /**
  * 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.
+ * \return Pointer to the filter identified by the given filter number, or
+ * NULL if the filter number is out of range.
  *
- * It is a fatal error if the given number is out of range. In this case
- * the function aborts.
+ * \sa filter_name().
  */
 const struct filter *filter_get(int filter_num)
 {
-       assert(filter_num >= 0);
-       assert(filter_num < NUM_SUPPORTED_FILTERS);
-       return filters + filter_num;
+       if (filter_num < 1 || filter_num > LSG_NUM_FILTER_CMD_SUBCOMMANDS)
+               return NULL;
+       return lls_user_data(FILTER_CMD(filter_num));
 }
 
-/**
- * Call the init function of each supported filter.
- * \sa filter::init
- */
-void filter_init(void)
+static inline bool filter_supported(int filter_num)
 {
-       int i;
-
-       FOR_EACH_SUPPORTED_FILTER(i)
-               filter_get(i)->init((struct filter *)filter_get(i));
+       return lls_user_data(FILTER_CMD(filter_num));
 }
 
-/*
- * If the filter has a command line parser and options is not NULL, run it.
- * Returns filter_num on success, negative on errors
+/**
+ * Return the name of a filter, given its number.
+ *
+ * \param filter_num See \ref filter_get().
+ *
+ * \return A pointer to a string literal, or NULL if filter_num is out of
+ * range. The caller must not attempt to call free(3) on the returned pointer.
  */
-static int parse_filter_args(int filter_num, const char *options, void **conf)
+const char *filter_name(int filter_num)
 {
-       const struct filter *f = filter_get(filter_num);
-       int ret, argc;
-       char **argv;
-
-       if (!f->parse_config)
-               return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
-       argc = create_shifted_argv(options, " \t", &argv);
-       if (argc < 0)
-               return -E_BAD_FILTER_OPTIONS;
-       argv[0] = para_strdup(f->name);
-       ret = f->parse_config(argc, argv, conf);
-       free_argv(argv);
-       return ret < 0? ret : filter_num;
+       if (filter_num < 1 || filter_num > LSG_NUM_FILTER_CMD_SUBCOMMANDS)
+               return NULL;
+       return lls_command_name(FILTER_CMD(filter_num));
 }
 
 /**
- * Check the filter command line options.
+ * Parse a filter command line and call the corresponding ->setup method.
  *
  * \param fa The filter argument.
- * \param conf Points to the filter configuration upon successful return.
+ * \param conf Points to filter-specific setup upon successful return.
+ * \param lprp Parsed command line options are returned here.
  *
  * 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.
- * On errors, a negative value is returned.
+ * command line parser of that filter and its ->setup method.
  *
- * Note: If \a fa specifies a filter that has no command line parser success is
- * returned, and \a conf is initialized to \p NULL.
- *
- * \sa filter::parse_config
+ * \return This function either succeeds or does not return. On success, the
+ * number of the filter is returned and conf is initialized to point to the
+ * filter configuration as returned by the filter's ->setup() method, if any.
+ * Moreover, *lprp is initialized to contain the parsed command line options.
+ * On errors, the function calls exit(EXIT_FAILURE).
  */
-int check_filter_arg(const char *fa, void **conf)
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp)
 {
-       int j;
-
-       *conf = NULL;
-//     PARA_DEBUG_LOG("arg: %s\n", fa);
-       FOR_EACH_SUPPORTED_FILTER(j) {
-               const char *name = filter_get(j)->name;
-               size_t len = strlen(name);
-               char c;
-               if (strlen(fa) < len)
-                       continue;
-               if (strncmp(name, fa, len))
-                       continue;
-               c = fa[len];
-               if (c && c != ' ')
-                       continue;
-               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);
+       int ret, filter_num, argc;
+       char *errctx = NULL, **argv;
+       const struct lls_command *cmd;
+       const struct filter *f;
+
+       ret = create_argv(fa, " \t\n", &argv);
+       if (ret < 0)
+               goto fail;
+       argc = ret;
+       ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       filter_num = ret;
+       cmd = FILTER_CMD(filter_num);
+       if (!filter_supported(filter_num)) {
+               ret = -E_UNSUPPORTED_FILTER;
+               errctx = make_message("bad filter name: %s",
+                       lls_command_name(cmd));
+               goto free_argv;
        }
-       return -E_UNSUPPORTED_FILTER;
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       f = filter_get(filter_num);
+       *conf = f->setup? f->setup(*lprp) : NULL;
+       ret = filter_num;
+free_argv:
+       free_argv(argv);
+       if (ret >= 0)
+               return ret;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 /**
  * Print help text of each filter to stdout.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print short or long help.
  */
-void print_filter_helps(unsigned flags)
+void print_filter_helps(bool detailed)
 {
        int i, num = 0;
 
-       printf_or_die("\nAvailable filters: ");
-       FOR_EACH_SUPPORTED_FILTER(i) {
+       printf("\nAvailable filters: ");
+       FOR_EACH_FILTER(i) {
+               if (!filter_supported(i))
+                       continue;
                if (num > 50) {
-                       printf_or_die("\n                  ");
+                       printf("\n                  ");
                        num = 0;
                }
-               num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name);
+               num += printf("%s%s", i? " " : "", filter_name(i));
+       }
+       printf("\n");
+
+       FOR_EACH_FILTER(i) {
+               const struct lls_command *cmd = FILTER_CMD(i);
+               char *help;
+
+               if (!filter_supported(i))
+                       continue;
+               help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
+                       continue;
+               printf("%s\n", help);
+               free(help);
        }
-       printf_or_die("\n");
+}
 
-       FOR_EACH_SUPPORTED_FILTER(i) {
-               struct filter *f = (struct filter *)filter_get(i);
+/**
+ * Print a short summary of all available filters to stdout.
+ *
+ * For each supported filter, the filter name and the purpose text is printed
+ * in a single line. Since no options are shown, the filter list is more
+ * concise than the text obtained from print_filter_helps().
+ */
+void print_filter_list(void)
+{
+       int i;
 
-               if (!f->help.short_help)
+       printf("Available filters:\n");
+       FOR_EACH_FILTER(i) {
+               const struct lls_command *cmd = FILTER_CMD(i);
+               if (!filter_supported(i))
                        continue;
-               printf_or_die("\nOptions for %s (%s):", f->name,
-                       f->help.purpose);
-               ggo_print_help(&f->help, flags);
+               printf("%-9s %s\n", filter_name(i), lls_purpose(cmd));
        }
 }
 
index 385d4f0c3a0235b9be24cdc83666308c30b959b8..e2d5380240240ddfaf3ce2b989ab0847f2e6e4fb 100644 (file)
@@ -391,6 +391,7 @@ static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
                        break;
        }
        afhi->chunks_total = c;
+       set_max_chunk_size(afhi);
        ret = 1;
 free_decoder:
        FLAC__stream_decoder_finish(decoder);
index bbacb3dad87025073b5bf8e99dc1ea8fa6bf5800..16237acbdcce863f37e1ab81988812ce4c53563c 100644 (file)
@@ -12,7 +12,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -296,18 +295,10 @@ static void flacdec_open(struct filter_node *fn)
        fn->min_iqs = 0;
 }
 
-/**
- * The init function of the flacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void flacdec_filter_init(struct filter *f)
-{
-       f->open = flacdec_open;
-       f->close = flacdec_close;
-       f->pre_select = flacdec_pre_select;
-       f->post_select = flacdec_post_select;
-       f->execute = flacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_flacdec_user_data = {
+       .open = flacdec_open,
+       .close = flacdec_close,
+       .pre_select = flacdec_pre_select,
+       .post_select = flacdec_post_select,
+       .execute = flacdec_execute,
+};
index 7c19aeb0c3edc022c5b0f0425a202e37fd38d906..1fde479e1be97656bd01002a2071a3d6f1b4b026 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -19,9 +19,6 @@
 
 //#define GCRYPT_DEBUG 1
 
-static bool libgcrypt_has_oaep;
-static const char *rsa_decrypt_sexp;
-
 #ifdef GCRYPT_DEBUG
 static void dump_buffer(const char *msg, unsigned char *buf, int len)
 {
@@ -63,134 +60,25 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
  * don't have to initialize any random seed here, but we must initialize the
  * gcrypt library. This task is performed by gcry_check_version() which can
  * also check that the gcrypt library version is at least the minimal required
- * version. This function also tells us whether we have to use our own OAEP
- * padding code.
+ * version.
  */
 void init_random_seed_or_die(void)
 {
-       const char *ver, *req_ver;
-
-       ver = gcry_check_version(NULL);
-       req_ver = "1.4.0";
-       if (!gcry_check_version(req_ver)) {
-               PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
-                       req_ver, ver);
-               exit(EXIT_FAILURE);
-       }
-       req_ver = "1.5.0";
-       if (gcry_check_version(req_ver)) {
-               libgcrypt_has_oaep = true;
-               rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))";
-       } else {
-               libgcrypt_has_oaep = false;
-               rsa_decrypt_sexp = "(enc-val(rsa(a %m)))";
-       }
+       const char *req_ver = "1.5.0";
+
+       if (gcry_check_version(req_ver))
+               return;
+       PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
+               req_ver, gcry_check_version(NULL));
+       exit(EXIT_FAILURE);
 }
 
 /** S-expression for the public part of an RSA key. */
 #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)))"
-
-/* rfc 3447, appendix B.2 */
-static void mgf1(unsigned char *seed, size_t seed_len, unsigned result_len,
-               unsigned char *result)
-{
-       gcry_error_t gret;
-       gcry_md_hd_t handle;
-       size_t n;
-       unsigned char *md;
-       unsigned char octet_string[4], *rp = result, *end = rp + result_len;
-
-       assert(result_len / HASH_SIZE < 1ULL << 31);
-       gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0);
-       assert(gret == 0);
-       for (n = 0; rp < end; n++) {
-               gcry_md_write(handle, seed, seed_len);
-               octet_string[0] = (unsigned char)((n >> 24) & 255);
-               octet_string[1] = (unsigned char)((n >> 16) & 255);
-               octet_string[2] = (unsigned char)((n >> 8)) & 255;
-               octet_string[3] = (unsigned char)(n & 255);
-               gcry_md_write(handle, octet_string, 4);
-               gcry_md_final(handle);
-               md = gcry_md_read(handle, GCRY_MD_SHA1);
-               memcpy(rp, md, PARA_MIN(HASH_SIZE, (int)(end - rp)));
-               rp += HASH_SIZE;
-               gcry_md_reset(handle);
-       }
-       gcry_md_close(handle);
-}
-
-/** The sha1 hash of an empty file. */
-static const unsigned char empty_hash[HASH_SIZE] =
-       "\xda" "\x39" "\xa3" "\xee" "\x5e"
-       "\x6b" "\x4b" "\x0d" "\x32" "\x55"
-       "\xbf" "\xef" "\x95" "\x60" "\x18"
-       "\x90" "\xaf" "\xd8" "\x07" "\x09";
-
-/* rfc3447, section 7.1.1 */
-static void pad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
-               size_t out_len)
-{
-       size_t ps_len = out_len - in_len - 2 * HASH_SIZE - 2;
-       size_t n, mask_len = out_len - HASH_SIZE - 1;
-       unsigned char *seed = out + 1, *db = seed + HASH_SIZE,
-               *ps = db + HASH_SIZE, *one = ps + ps_len;
-       unsigned char *db_mask, seed_mask[HASH_SIZE];
-
-       assert(in_len <= out_len - 2 - 2 * HASH_SIZE);
-       assert(out_len > 2 * HASH_SIZE + 2);
-       PARA_DEBUG_LOG("padding %zu byte input -> %zu byte output\n",
-               in_len, out_len);
-       dump_buffer("unpadded buffer", in, in_len);
-
-       out[0] = '\0';
-       get_random_bytes_or_die(seed, HASH_SIZE);
-       memcpy(db, empty_hash, HASH_SIZE);
-       memset(ps, 0, ps_len);
-       *one = 0x01;
-       memcpy(one + 1, in, in_len);
-       db_mask = para_malloc(mask_len);
-       mgf1(seed, HASH_SIZE, mask_len, db_mask);
-       for (n = 0; n < mask_len; n++)
-               db[n] ^= db_mask[n];
-       mgf1(db, mask_len, HASH_SIZE, seed_mask);
-       for (n = 0; n < HASH_SIZE; n++)
-               seed[n] ^= seed_mask[n];
-       free(db_mask);
-       dump_buffer("padded buffer", out, out_len);
-}
-
-/* rfc 3447, section 7.1.2 */
-static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
-               size_t *out_len)
-{
-       unsigned char *masked_seed = in + 1;
-       unsigned char *db = in + 1 + HASH_SIZE;
-       unsigned char seed[HASH_SIZE], seed_mask[HASH_SIZE];
-       unsigned char *db_mask, *p;
-       size_t n, mask_len = in_len - HASH_SIZE - 1;
-
-       mgf1(db, mask_len, HASH_SIZE, seed_mask);
-       for (n = 0; n < HASH_SIZE; n++)
-               seed[n] = masked_seed[n] ^ seed_mask[n];
-       db_mask = para_malloc(mask_len);
-       mgf1(seed, HASH_SIZE, mask_len, db_mask);
-       for (n = 0; n < mask_len; n++)
-               db[n] ^= db_mask[n];
-       free(db_mask);
-       if (memcmp(db, empty_hash, HASH_SIZE))
-               return -E_OEAP;
-       for (p = db + HASH_SIZE; p < in + in_len - 1; p++)
-               if (*p != '\0')
-                       break;
-       if (p >= in + in_len - 1)
-               return -E_OEAP;
-       p++;
-       *out_len = in + in_len - p;
-       memcpy(out, p, *out_len);
-       return 1;
-}
+/** S-expression for decryption. */
+#define RSA_DECRYPT_SEXP "(enc-val(flags oaep)(rsa(a %m)))"
 
 struct asymmetric_key {
        gcry_sexp_t sexp;
@@ -301,64 +189,6 @@ static inline int get_long_form_num_length_bytes(unsigned char c)
        return c & 0x7f;
 }
 
-static int find_pubkey_bignum_offset(const unsigned char *data, int len)
-{
-       const unsigned char *p = data, *end = data + len;
-
-       /* the whole thing starts with one sequence */
-       if (*p != ASN1_TYPE_SEQUENCE)
-               return -E_ASN1_PARSE;
-       p++;
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       if (is_short_form(*p))
-               p++;
-       else
-               p += 1 + get_long_form_num_length_bytes(*p);
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       /* another sequence containing the object id, skip it */
-       if (*p != ASN1_TYPE_SEQUENCE)
-               return -E_ASN1_PARSE;
-       p++;
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       if (!is_short_form(*p))
-               return -E_ASN1_PARSE;
-       p += 1 + get_short_form_length(*p);
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       /* all numbers are wrapped in a bit string object that follows */
-       if (*p != ASN1_TYPE_BIT_STRING)
-               return -E_ASN1_PARSE;
-       p++;
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       if (is_short_form(*p))
-               p++;
-       else
-               p += 1 + get_long_form_num_length_bytes(*p);
-       p++; /* skip number of unused bits in the bit string */
-       if (p >= end)
-               return -E_ASN1_PARSE;
-
-       /* next, we have a sequence of two integers (n and e) */
-       if (*p != ASN1_TYPE_SEQUENCE)
-               return -E_ASN1_PARSE;
-       p++;
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       if (is_short_form(*p))
-               p++;
-       else
-               p += 1 + get_long_form_num_length_bytes(*p);
-       if (p >= end)
-               return -E_ASN1_PARSE;
-       if (*p != ASN1_TYPE_INTEGER)
-               return -E_ASN1_PARSE;
-       return p - data;
-}
-
 /*
  * Returns: Number of bytes scanned. This may differ from the value returned via
  * bn_bytes because the latter does not include the ASN.1 prefix and a leading
@@ -460,6 +290,7 @@ static int get_private_key(const char *key_file, struct asymmetric_key **result)
        gcry_sexp_t sexp;
        struct asymmetric_key *key;
 
+       *result = NULL;
        ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER,
                &blob);
        if (ret < 0)
@@ -538,65 +369,6 @@ free_blob:
        return ret;
 }
 
-/** Public keys start with this header. */
-#define PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----"
-/** Public keys end with this footer. */
-#define PUBLIC_KEY_FOOTER "-----END PUBLIC KEY-----"
-
-static int get_asn_public_key(const char *key_file, struct asymmetric_key **result)
-{
-       gcry_mpi_t n = NULL, e = NULL;
-       unsigned char *blob, *cp, *end;
-       int blob_size, ret, n_size;
-       gcry_error_t gret;
-       size_t erroff;
-       gcry_sexp_t sexp;
-       struct asymmetric_key *key;
-
-       ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER,
-               &blob);
-       if (ret < 0)
-               return ret;
-       blob_size = ret;
-       end = blob + blob_size;
-       ret = find_pubkey_bignum_offset(blob, blob_size);
-       if (ret < 0)
-               goto free_blob;
-       PARA_DEBUG_LOG("decoding public RSA params at offset %d\n", ret);
-       cp = blob + ret;
-
-       ret = read_bignum(cp, end, &n, &n_size);
-       if (ret < 0)
-               goto free_blob;
-       cp += ret;
-
-       ret = read_bignum(cp, end, &e, NULL);
-       if (ret < 0)
-               goto release_n;
-
-       gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
-       if (gret) {
-               PARA_ERROR_LOG("offset %zu: %s\n", erroff,
-                       gcry_strerror(gcry_err_code(gret)));
-               ret = -E_SEXP_BUILD;
-               goto release_e;
-       }
-       key = para_malloc(sizeof(*key));
-       key->sexp = sexp;
-       key->num_bytes = n_size;
-       *result = key;
-       ret = n_size;
-       PARA_INFO_LOG("successfully read %d bit asn public key\n", n_size * 8);
-
-release_e:
-       gcry_mpi_release(e);
-release_n:
-       gcry_mpi_release(n);
-free_blob:
-       free(blob);
-       return ret;
-}
-
 static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
 {
        int ret;
@@ -658,8 +430,7 @@ free_blob:
        return ret;
 }
 
-int get_asymmetric_key(const char *key_file, int private,
-               struct asymmetric_key **result)
+int get_public_key(const char *key_file, struct asymmetric_key **result)
 {
        int ret, ret2;
        void *map;
@@ -668,17 +439,13 @@ int get_asymmetric_key(const char *key_file, int private,
        gcry_sexp_t sexp;
        struct asymmetric_key *key;
 
-       if (private)
-               return get_private_key(key_file, result);
        ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
        if (ret < 0)
                return ret;
        ret = is_ssh_rsa_key(map, map_size);
        if (!ret) {
-               ret = para_munmap(map, map_size);
-               if (ret < 0)
-                       return ret;
-               return get_asn_public_key(key_file, result);
+               para_munmap(map, map_size);
+               return -E_SSH_PARSE;
        }
        start = map + ret;
        end = map + map_size;
@@ -699,7 +466,7 @@ unmap:
        return ret;
 }
 
-void free_asymmetric_key(struct asymmetric_key *key)
+void free_public_key(struct asymmetric_key *key)
 {
        if (!key)
                return;
@@ -707,78 +474,27 @@ void free_asymmetric_key(struct asymmetric_key *key)
        free(key);
 }
 
-static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf,
-               size_t *nbytes)
+static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes)
 {
-       int ret;
-       gcry_error_t gret;
-       unsigned char oaep_buf[512];
-       gcry_mpi_t out_mpi;
-
-       if (libgcrypt_has_oaep) {
-               const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
-
-               if (!p) {
-                       PARA_ERROR_LOG("could not get data from list\n");
-                       return -E_OEAP;
-               }
-               memcpy(outbuf, p, *nbytes);
-               return 1;
-       }
-       out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG);
-       if (!out_mpi)
-               return -E_SEXP_FIND;
-       gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf),
-               nbytes, out_mpi);
-       if (gret) {
-               PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret));
-               ret = -E_MPI_PRINT;
-               goto out_mpi_release;
-       }
-       /*
-        * An oaep-encoded buffer always starts with at least one zero byte.
-        * However, leading zeroes in an mpi are omitted in the output of
-        * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The
-        * alternative, GCRYMPI_FMT_STD, does not work either because here the
-        * leading zero(es) might also be omitted, depending on the value of
-        * the second byte.
-        *
-        * To circumvent this, we shift the oaep buffer to the right. But first
-        * we check that the buffer actually started with a zero byte, i.e. that
-        * nbytes < key_size. Otherwise a decoding error occurred.
-        */
-       ret = -E_SEXP_DECRYPT;
-       if (*nbytes >= key_size)
-               goto out_mpi_release;
-       memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes);
-       memset(oaep_buf, 0, key_size - *nbytes);
+       const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
 
-       PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
-               key_size);
-       dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);
-       ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes);
-       if (ret < 0)
-               goto out_mpi_release;
-       PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
-               *nbytes);
-       dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);
-       ret = 1;
-out_mpi_release:
-       gcry_mpi_release(out_mpi);
-       return ret;
+       if (!p)
+               return -E_RSA_DECODE;
+       memcpy(outbuf, p, *nbytes);
+       return 1;
 }
 
 int priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen)
 {
        gcry_error_t gret;
-       int ret, key_size;
+       int ret;
        struct asymmetric_key *priv;
        gcry_mpi_t in_mpi = NULL;
        gcry_sexp_t in, out, priv_key;
        size_t nbytes;
 
-       ret = check_key_file(key_file, true);
+       ret = check_private_key_file(key_file);
        if (ret < 0)
                return ret;
        PARA_INFO_LOG("decrypting %d byte input\n", inlen);
@@ -786,7 +502,6 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
        ret = get_private_key(key_file, &priv);
        if (ret < 0)
                return ret;
-       key_size = ret / 8;
 
        /* asymmetric key priv -> sexp priv_key */
        ret = -E_SEXP_FIND;
@@ -803,7 +518,7 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
                goto key_release;
        }
        /* in_mpi -> in sexp */
-       gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi);
+       gret = gcry_sexp_build(&in, NULL, RSA_DECRYPT_SEXP, in_mpi);
        if (gret) {
                PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
                ret = -E_SEXP_BUILD;
@@ -817,7 +532,7 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
                ret = -E_SEXP_DECRYPT;
                goto in_release;
        }
-       ret = decode_rsa(out, key_size, outbuf, &nbytes);
+       ret = decode_rsa(out, outbuf, &nbytes);
        if (ret < 0)
                goto out_release;
        PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes);
@@ -831,7 +546,8 @@ in_mpi_release:
 key_release:
        gcry_sexp_release(priv_key);
 free_key:
-       free_asymmetric_key(priv);
+       gcry_sexp_release(priv->sexp);
+       free(priv);
        return ret;
 }
 
@@ -850,18 +566,7 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
        if (!pub_key)
                return -E_SEXP_FIND;
-       if (libgcrypt_has_oaep) {
-               gret = gcry_sexp_build(&in, NULL,
-                       "(data(flags oaep)(value %b))", len, inbuf);
-       } else {
-               unsigned char padded_input[256];
-               const size_t pad_size = 256;
-               /* inbuf -> padded inbuf */
-               pad_oaep(inbuf, len, padded_input, pad_size);
-               /* padded inbuf -> in sexp */
-               gret = gcry_sexp_build(&in, NULL,
-                       "(data(flags raw)(value %b))", pad_size, padded_input);
-       }
+       gret = gcry_sexp_build(&in, NULL, "(data(flags oaep)(value %b))", len, inbuf);
        if (gret) {
                PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
                ret = -E_SEXP_BUILD;
@@ -912,33 +617,20 @@ struct stream_cipher {
        gcry_cipher_hd_t handle;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len,
-               bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        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) {
-               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
-               free(sc);
-               return NULL;
-       }
-       gret = gcry_cipher_setkey(sc->handle, data, (size_t)len);
+       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;
 }
diff --git a/ggo.c b/ggo.c
deleted file mode 100644 (file)
index 81ab464..0000000
--- a/ggo.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.c Function for printing help. */
-
-
-#include "para.h"
-#include "ggo.h"
-#include "version.h"
-
-/**
- * Wrapper for printf() that exits on errors.
- *
- * \param fmt Usual format string.
- *
- * \return The return value of the underlying (successful) call to vprintf(3),
- * i.e. the number of characters printed, excluding the terminating null byte.
- */
-__printf_1_2 int printf_or_die(const char *fmt, ...)
-{
-       va_list argp;
-       int ret;
-
-       va_start(argp, fmt);
-       ret = vprintf(fmt, argp);
-       va_end(argp);
-       if (ret >= 0)
-               return ret;
-       exit(EXIT_FAILURE);
-}
-
-/**
- * Print one of the two given help texts.
- *
- * \param help contains the help texts.
- * \param flags What to print, see \ref ggo_print_help_flags.
- */
-void ggo_print_help(struct ggo_help *help, unsigned flags)
-{
-       const char **p;
-
-       if (help->purpose && (flags & GPH_PRINT_NAME_PURPOSE))
-               printf_or_die("para_%s - %s\n", help->prefix, help->purpose);
-       if (help->usage && (flags & GPH_PRINT_USAGE))
-               printf_or_die("\n%s\n", help->usage);
-       if (help->description && (flags & GPH_PRINT_DESCRIPTION))
-               printf_or_die("\n%s\n", help->description);
-       printf_or_die("\n");
-       if (flags & GPH_DETAILED)
-               p = help->detailed_help;
-       else
-               p = help->short_help;
-       if (!p)
-               return;
-       for (; *p; p++)
-               printf_or_die("%s\n", *p);
-}
diff --git a/ggo.h b/ggo.h
deleted file mode 100644 (file)
index 7e52403..0000000
--- a/ggo.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.h Functions and structures for help text handling. */
-
-/**
- * Information extracted from the .cmdline.h header files.
- */
-struct ggo_help {
-       /** Program or module (receiver, filter, writer) name. */
-       const char *prefix;
-       /** Generated by gengetopt from the options in the .ggo file. */
-       const char **short_help;
-       /** Like \a short_help, plus the \a details section. */
-       const char **detailed_help;
-       /** The purpose text as specified in the ggo file. */
-       const char *purpose;
-       /** Generated by gengetopt and exported via the *.cmdline.h file. */
-       const char *usage;
-       /** The description text given in the .ggo file. */
-       const char *description;
-};
-
-/**
- * Control the output of \ref ggo_print_help().
- *
- * Any combination of these flags may be passed to ggo_print_help().
- * Note that the list of supported options is always printed.
- */
-enum ggo_print_help_flags {
-       /** Whether to print the short help or the detailed help. */
-       GPH_DETAILED = 1 << 0,
-       /** Print the program or module name and the purpose text. */
-       GPH_PRINT_NAME_PURPOSE = 1 << 1,
-       /** Print the synopsis. */
-       GPH_PRINT_USAGE = 1 << 2,
-       /** Print the description text. */
-       GPH_PRINT_DESCRIPTION = 1 << 3,
-};
-
-/** Used to print the normal help of programs (--help) */
-#define GPH_STANDARD_FLAGS \
-       (GPH_PRINT_NAME_PURPOSE | GPH_PRINT_USAGE)
-
-/** Additional information for --detailed-help. */
-#define GPH_STANDARD_FLAGS_DETAILED \
-       (GPH_STANDARD_FLAGS | GPH_DETAILED | GPH_PRINT_DESCRIPTION)
-
-/** For module help embedded in a program help. */
-#define GPH_MODULE_FLAGS 0
-
-/** Modules help with detailed descriptions. */
-#define GPH_MODULE_FLAGS_DETAILED GPH_DETAILED | GPH_PRINT_DESCRIPTION
-
-/** Make a ggo_help structure using information from the .cmdline.h file. */
-#define DEFINE_GGO_HELP(_prefix) \
-       { \
-               .prefix = #_prefix, \
-               .short_help = _prefix ## _args_info_help, \
-               .detailed_help = _prefix ## _args_info_detailed_help, \
-               .purpose = _prefix ## _args_info_purpose, \
-               .usage = _prefix ## _args_info_usage, \
-               .description = _prefix ## _args_info_description, \
-       }
-
-void ggo_print_help(struct ggo_help *help, unsigned flags);
-__printf_1_2 int printf_or_die(const char *fmt, ...);
index 926f47927c7617f0aa53cae4eeec7f42026ce9f6..382b0470b3fb26f08be12ac0fca11d9eb7cb9e91 100644 (file)
@@ -8,11 +8,13 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "grab_client.h"
 #include "audiod.h"
@@ -210,77 +212,62 @@ err:
        return ret;
 }
 
-static int gc_check_args(int argc, char **argv, struct grab_client *gc)
+static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
 {
-       int i;
+       const struct lls_opt_result *r;
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-m", 2)) {
-                       if (*(arg + 3))
-                               return -E_GC_SYNTAX;
-                       switch(*(arg + 2)) {
-                       case 's':
-                               gc->mode = GM_SLOPPY;
-                               continue;
-                       case 'a':
-                               gc->mode = GM_AGGRESSIVE;
-                               continue;
-                       case 'p':
-                               gc->mode = GM_PEDANTIC;
-                               continue;
-                       default:
-                               return -E_GC_SYNTAX;
-                       }
-               }
-               if (!strcmp(arg, "-o")) {
-                       gc->flags |= GF_ONE_SHOT;
-                       continue;
-               }
-               if (!strncmp(arg, "-p=", 3)) {
-                       gc->parent = para_strdup(arg + 3);
-                       continue;
-               }
-               if (!strncmp(arg, "-n=", 3)) {
-                       gc->name = para_strdup(arg + 3);
-                       continue;
-               }
-               return -E_GC_SYNTAX;
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_MODE, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               if (strcmp(arg, "s") == 0)
+                       gc->mode = GM_SLOPPY;
+               else if (strcmp(arg, "a") == 0)
+                       gc->mode = GM_AGGRESSIVE;
+               else if (strcmp(arg, "p") == 0)
+                       gc->mode = GM_PEDANTIC;
+               else
+                       return -E_GC_SYNTAX;
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_ONE_SHOT, lpr);
+       if (lls_opt_given(r) > 0)
+               gc->flags |= GF_ONE_SHOT;
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_PARENT, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->parent = para_strdup(arg);
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_NAME, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->name = para_strdup(arg);
        }
-       if (i != argc)
-               return -E_GC_SYNTAX;
        return 1;
 }
 
 /**
- * Check the command line options and allocate a grab_client structure.
+ * Create and activate a grab client.
  *
  * \param fd The file descriptor of the client.
- * \param argc Argument count.
- * \param argv Argument vector.
+ * \param lpr The parsed command line of the grab command.
  * \param s The scheduler to register the grab client task to.
  *
- * If the command line options given by \a argc and \a argv are valid.
- * allocate a struct grab_client and initialize it with this valid
- * configuration.
- *
- * If the new grab client can be added to an existing buffer tree, activate it.
- * Otherwise, add it to the inactive list for later activation.
+ * This function semantically parses the arguments given as options to the grab
+ * command. On success it allocates a struct grab_client, associates it with
+ * the given file descriptor and activates it. If the new grab client can not
+ * be attached to an existing buffer tree node it is put into the inactive list
+ * for later activation.
  *
  * \return Standard.
  */
-int grab_client_new(int fd, int argc, char **argv, struct sched *s)
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
        struct grab_client *gc = para_calloc(sizeof(struct grab_client));
 
-       ret = gc_check_args(argc, argv, gc);
+       ret = gc_check_args(lpr, gc);
        if (ret < 0)
                goto err_out;
        ret = dup(fd);
index 7a752cee96052fcc7516f9e4e58a1abe252e3e9b..3f3a0c036206de61e55cf2ca6c7f4dda23b839eb 100644 (file)
@@ -6,5 +6,5 @@
 
 /** \file grab_client.h exported symbols from grab_client.c */
 
-int grab_client_new(int fd, int argc, char **argv, struct sched *s);
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s);
 void activate_grab_clients(struct sched *s);
diff --git a/gui.c b/gui.c
index 8e83cc53c6d55f53ccdb0a9dfbb4c89f0bb7525a..5bad7e14616f505eddb0545f51658f70759fe996 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -12,8 +12,9 @@
 #include <curses.h>
 #include <locale.h>
 #include <sys/time.h>
+#include <lopsub.h>
 
-#include "gui.cmdline.h"
+#include "gui.lsg.h"
 #include "para.h"
 #include "gui.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
 #include "signal.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *cmdline_lpr, *lpr;
+
+#define CMD_PTR (lls_cmd(0, gui_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_GUI_PARA_GUI_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define FOR_EACH_KEY_MAP(_i) for (_i = 0; _i < OPT_GIVEN(KEY_MAP); _i++)
+
 static char *stat_content[NUM_STAT_ITEMS];
 
 static struct gui_window {
@@ -47,11 +56,8 @@ struct rb_entry {
 static struct ringbuffer *bot_win_rb;
 
 static unsigned scroll_position;
-
 static pid_t exec_pid;
-
 static int exec_fds[2] = {-1, -1};
-static struct gui_args_info conf;
 static int loglevel;
 
 /** Type of the process currently being executed. */
@@ -245,7 +251,7 @@ static char *km_keyname(int c)
 }
 
 /* Print given number of spaces to curses window. */
-static void add_spaces(WINDOWwin, unsigned int num)
+static void add_spaces(WINDOW *win, unsigned int num)
 {
        const char space[] = "                                ";
        const unsigned sz = sizeof(space) - 1; /* number of spaces */
@@ -261,10 +267,10 @@ static void add_spaces(WINDOW* win, unsigned int num)
 }
 
 /*
- * print aligned string to curses window. This function always prints
+ * Print aligned string to curses window. This function always prints
  * exactly len chars.
  */
-static int align_str(WINDOWwin, const char *str, unsigned int len,
+static int align_str(WINDOW *win, const char *str, unsigned int len,
                unsigned int align)
 {
        int ret, num; /* of spaces */
@@ -450,10 +456,8 @@ static void rb_add_entry(int color, char *msg)
        waddstr(bot.win, msg);
 }
 
-/*
- * print formated output to bot win and refresh
- */
-__printf_2_3 static void outputf(int color, const char* fmt,...)
+/* Print formatted output to bot win and refresh. */
+__printf_2_3 static void outputf(int color, const char *fmt,...)
 {
        char *msg;
        va_list ap;
@@ -496,8 +500,9 @@ static __printf_2_3 void curses_log(int ll, const char *fmt,...)
                vfprintf(stderr, fmt, ap);
        va_end(ap);
 }
+
 /** The log function of para_gui, always set to curses_log(). */
-__printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
+__printf_2_3 void (*para_log)(int, const char *, ...) = curses_log;
 
 /* Call endwin() to reset the terminal into non-visual mode. */
 static void shutdown_curses(void)
@@ -514,8 +519,8 @@ static void shutdown_curses(void)
        endwin();
 }
 
-/* disable curses, print a message, kill running processes and exit */
-__noreturn __printf_2_3 static void die(int exit_code, const charfmt, ...)
+/* 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;
 
@@ -538,9 +543,7 @@ __noreturn __printf_2_3 static void die(int exit_code, const char* fmt, ...)
        exit(exit_code);
 }
 
-/*
- * Print stat item #i to curses window
- */
+/* Print stat item #i to curses window. */
 static void print_stat_item(int i)
 {
        char *tmp;
@@ -644,7 +647,8 @@ static int status_post_select(struct sched *s, void *context)
                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);
+               ret = para_exec_cmdline_pid(&st->pid,
+                       OPT_STRING_VAL(STAT_CMD), fds);
                if (ret < 0)
                        return 0;
                ret = mark_fd_nonblocking(fds[1]);
@@ -689,9 +693,7 @@ static int status_post_select(struct sched *s, void *context)
        return 0;
 }
 
-/*
- * init all windows
- */
+/* Initialize all windows. */
 static void init_wins(int top_lines)
 {
        int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
@@ -831,12 +833,13 @@ static void check_key_map_args_or_die(void)
 {
        int i;
        char *tmp = NULL;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *handler, *arg;
 
                free(tmp);
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg))
                        break;
                if (strlen(handler) != 1)
@@ -849,84 +852,90 @@ static void check_key_map_args_or_die(void)
                if (find_cmd_byname(arg) < 0)
                        break;
        }
-       if (i != conf.key_map_given)
-               die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
+       if (i != OPT_GIVEN(KEY_MAP))
+               die(EXIT_FAILURE, "invalid key map: %s\n",
+                       lls_string_val(i, lor));
        free(tmp);
 }
 
-static void parse_config_file_or_die(bool override)
+static void parse_config_file_or_die(bool reload)
 {
-       bool err;
-       char *config_file;
-       struct gui_cmdline_parser_params params = {
-               .override = override,
-               .initialize = 0,
-               .check_required = !override,
-               .check_ambiguity = 0,
-               .print_errors = 1,
-       };
+       int ret;
+       char *cf = NULL, *errctx = NULL;
+       void *map;
+       size_t sz;
+       int cf_argc;
+       char **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
 
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/gui.conf", home);
+               cf = 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;
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+                       goto free_cf;
+               ret = 0;
+               lpr = cmdline_lpr;
+               goto success;
        }
-
-       gui_cmdline_parser_config_file(config_file, &conf, &params);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+       para_munmap(map, sz);
+       if (ret < 0)
+               goto free_cf;
+       cf_argc = ret;
+       ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+       lls_free_argv(cf_argv);
+       if (ret < 0)
+               goto free_cf;
+       if (reload) /* config file overrides command line */
+               ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       else /* command line options override config file options */
+               ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       lls_free_parse_result(cf_lpr, CMD_PTR);
+       if (ret < 0)
+               goto free_cf;
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(lpr, CMD_PTR);
+       lpr = merged_lpr;
+success:
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
        check_key_map_args_or_die();
-       err = false;
-out:
-       free(config_file);
-       if (err)
+       theme_init(OPT_STRING_VAL(THEME), &theme);
+free_cf:
+       free(cf);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
-       theme_init(conf.theme_arg, &theme);
+       }
 }
 
-/* reread configuration, terminate on errors */
+/* 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.
+        * If the reload of the config file fails, we are about to exit. In
+        * this case we print the error message to stderr rather than to the
+        * curses window.  So we have to shutdown curses first.
         */
        shutdown_curses();
-       parse_config_file_or_die(true /* override */);
+       parse_config_file_or_die(true);
        init_curses();
        print_in_bar(COLOR_MSG, "config file reloaded\n");
 }
 
-/*
- * React to various signal-related events
- */
+/* React to various signal-related events. */
 static int signal_post_select(struct sched *s, __a_unused void *context)
 {
        int ret = para_next_signal(&s->rfds);
@@ -1027,7 +1036,7 @@ static void input_pre_select(struct sched *s, __a_unused void *context)
                sched_min_delay(s);
 }
 
-/* read from command pipe and print data to bot window */
+/* 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};
@@ -1061,9 +1070,7 @@ static void exec_para(const char *args)
        free(file_and_args);
 }
 
-/*
- * shutdown curses and stat pipe before executing external commands
- */
+/* Shutdown curses and stat pipe before executing external commands. */
 static void exec_external(char *file_and_args)
 {
        int fds[3] = {-1, -1, -1};
@@ -1077,12 +1084,13 @@ static void exec_external(char *file_and_args)
 static void handle_command(int c)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
        /* first check user-defined key bindings */
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *tmp, *handler, *arg;
 
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg)) {
                        free(tmp);
                        return;
@@ -1116,7 +1124,8 @@ static void handle_command(int c)
                km_keyname(c));
 }
 
-static int input_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int input_post_select(__a_unused struct sched *s,
+               __a_unused void *context)
 {
        int ret;
        enum exec_status exs = exec_status();
@@ -1329,9 +1338,10 @@ static void com_reread_conf(void)
 static void com_help(void)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
-               char *handler, *arg, *tmp = para_strdup(conf.key_map_arg[i]);
+       FOR_EACH_KEY_MAP(i) {
+               char *handler, *arg, *tmp = para_strdup(lls_string_val(i, lor));
                const char *handler_text = "???", *desc = NULL;
 
                if (!split_key_map(tmp, &handler, &arg)) {
@@ -1415,15 +1425,6 @@ static void com_prev_theme(void)
        com_refresh();
 }
 
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(gui);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
-}
-
 static int setup_tasks_and_schedule(void)
 {
        int ret;
@@ -1431,12 +1432,7 @@ static int setup_tasks_and_schedule(void)
        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,
-               },
-       };
+       struct sched sched = {.default_timeout = {.tv_sec = 1}};
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
@@ -1476,6 +1472,21 @@ static int setup_tasks_and_schedule(void)
        return ret;
 }
 
+static void handle_help_flags(void)
+{
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
+}
+
 /**
  * The main function of para_gui.
  *
@@ -1503,15 +1514,31 @@ static int setup_tasks_and_schedule(void)
  */
 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();
-       parse_config_file_or_die(false /* override */);
+       int ret;
+       char *errctx;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       lpr = cmdline_lpr;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("gui", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_file_or_die(false);
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
        setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
-       return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
+       ret = setup_tasks_and_schedule();
+out:
+       lls_free_parse_result(lpr, CMD_PTR);
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       }
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 2f334787200e4a0134df6a108a0c4dc8a7dd0775..67691a34f121db02365a884ef1fd4e63cc63d918 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "http.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "http_recv.cmdline.h"
 #include "net.h"
 #include "string.h"
 #include "fd.h"
@@ -30,7 +30,7 @@
 /**
  * the possible states of a http receiver node
  *
- * \sa receiver_node
+ * \sa \ref receiver_node.
  */
 enum http_recv_status {HTTP_CONNECTED, HTTP_SENT_GET_REQUEST, HTTP_STREAMING};
 
@@ -46,7 +46,7 @@ struct private_http_recv_data {
         * It gets initialized to \p HTTP_CONNECTED by the open function of the
         * http receiver.
         *
-        * \sa receiver::open, receiver_node.
+        * \sa \ref receiver::open, \ref receiver_node.
         */
        enum http_recv_status status;
 };
@@ -144,20 +144,13 @@ static void http_recv_close(struct receiver_node *rn)
        free(rn->private_data);
 }
 
-static void *http_recv_parse_config(int argc, char **argv)
-{
-       struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
-       http_recv_cmdline_parser(argc, argv, tmp);
-       return tmp;
-}
-
 static int http_recv_open(struct receiver_node *rn)
 {
        struct private_http_recv_data *phd;
-       struct http_recv_args_info *conf = rn->conf;
-       int fd, ret = para_connect_simple(IPPROTO_TCP, conf->host_arg,
-                                                      conf->port_arg);
+       struct lls_parse_result *lpr = rn->lpr;
+       const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
+       uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
+       int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
@@ -174,30 +167,10 @@ static int http_recv_open(struct receiver_node *rn)
        return 1;
 }
 
-static void http_recv_free_config(void *conf)
-{
-       http_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the http receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void http_recv_init(struct receiver *r)
-{
-       struct http_recv_args_info dummy;
-
-       http_recv_cmdline_parser_init(&dummy);
-       r->open = http_recv_open;
-       r->close = http_recv_close;
-       r->pre_select = http_recv_pre_select;
-       r->post_select = http_recv_post_select;
-       r->parse_config = http_recv_parse_config;
-       r->free_config = http_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv);
-       http_recv_cmdline_parser_free(&dummy);
-}
+/** See \ref recv_init(). */
+const struct receiver lsg_recv_cmd_com_http_user_data = {
+       .open = http_recv_open,
+       .close = http_recv_close,
+       .pre_select = http_recv_pre_select,
+       .post_select = http_recv_post_select,
+};
index 9d0f49aee03cd6b30d6a73e9eda74dcb9be4cfc0..26536d07eea58350de76196c29c5512a0e0963a5 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
-#include "server.cmdline.h"
 #include "afh.h"
 #include "server.h"
 #include "http.h"
@@ -263,10 +264,10 @@ void http_send_init(struct sender *s)
        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,
-               conf.http_default_deny_given);
-       if (conf.http_no_autostart_given)
+       init_sender_status(hss, OPT_RESULT(HTTP_ACCESS),
+               OPT_UINT32_VAL(HTTP_PORT), OPT_UINT32_VAL(HTTP_MAX_CLIENTS),
+               OPT_GIVEN(HTTP_DEFAULT_DENY));
+       if (OPT_GIVEN(HTTP_NO_AUTOSTART))
                return;
        ret = generic_com_on(hss, IPPROTO_TCP);
        if (ret < 0)
diff --git a/imdct.c b/imdct.c
index d2a0681898b6497f2742e842d3b9ef1ed104621a..5791353b5a2d0c806f58229fd6861f3561b7e0c6 100644 (file)
--- a/imdct.c
+++ b/imdct.c
@@ -141,28 +141,26 @@ __a_const static int split_radix_permutation(int i, int n)
 }
 
 /* z[0...8n - 1], w[1...2n - 1] */
-#define PASS(name)\
-static void name(struct fft_complex *z, const fftsample_t *wre, unsigned int n)\
-{\
-       fftsample_t t1, t2, t3, t4, t5, t6;\
-       int o1 = 2 * n;\
-       int o2 = 4 * n;\
-       int o3 = 6 * n;\
-       const fftsample_t *wim = wre + o1;\
-       n--;\
-\
-       TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]);\
-       TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\
-       do {\
-               z += 2;\
-               wre += 2;\
-               wim -= 2;\
-               TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]);\
-               TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\
-       } while (--n);\
+static void pass(struct fft_complex *z, const fftsample_t *wre, unsigned int n)
+{
+       fftsample_t t1, t2, t3, t4, t5, t6;
+       int o1 = 2 * n;
+       int o2 = 4 * n;
+       int o3 = 6 * n;
+       const fftsample_t *wim = wre + o1;
+
+       n--;
+       TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]);
+       TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);
+       do {
+               z += 2;
+               wre += 2;
+               wim -= 2;
+               TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]);
+               TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);
+       } while (--n);
 }
 
-PASS(pass)
 #undef BUTTERFLIES
 #define BUTTERFLIES BUTTERFLIES_BIG
 
index c34a0f6eb3c5ff3fd6fcb0e85546a180d21fd880..91ab1559629725f4b9ae25a3ec167231937bc27d 100644 (file)
@@ -206,26 +206,7 @@ void i9e_attach_to_stdout(struct btr_node *producer)
 
 static void wipe_bottom_line(void)
 {
-       char x[] = "          ";
-       int n = i9ep->num_columns;
-
-       /*
-        * For reasons beyond my understanding, writing more than 68 characters
-        * here causes MacOS to mess up the terminal. Writing a line of spaces
-        * in smaller chunks works fine though. Weird.
-        */
-       fprintf(i9ep->stderr_stream, "\r");
-       while (n > 0) {
-               if (n >= sizeof(x)) {
-                       fprintf(i9ep->stderr_stream, "%s", x);
-                       n -= sizeof(x);
-                       continue;
-               }
-               x[n] = '\0';
-               fprintf(i9ep->stderr_stream, "%s", x);
-               break;
-       }
-       fprintf(i9ep->stderr_stream, "\r");
+       fprintf(i9ep->stderr_stream, "\r%s\r", i9ep->empty_line);
 }
 
 #ifndef RL_FREE_KEYMAP_DECLARED
index e6d53dee81ec13853a403cbfbedbf68b43accb2d..82d3cd71c4cc870d2a2535faeedd22f489444ab3 100644 (file)
@@ -33,7 +33,7 @@ struct i9e_completion_result {
  *
  * \param name Determines the name of the function to be defined.
  */
-#define I9E_DUMMY_COMPLETER(name) void name ## _completer( \
+#define I9E_DUMMY_COMPLETER(name) static void name ## _completer( \
                __a_unused struct i9e_completion_info *ciname, \
                struct i9e_completion_result *result) {result->matches = NULL;}
 
diff --git a/ipc.c b/ipc.c
index 9488224a1d4a622860adc957b27977e2bcbfc7e2..3cc5c15695a76c0442b1ceb4da889c6f5b2baeb1 100644 (file)
--- a/ipc.c
+++ b/ipc.c
@@ -68,7 +68,7 @@ static void para_semop(int id, struct sembuf *sops, int num)
  *
  * This function either succeeds or aborts.
  *
- * \sa semop(2), struct misc_meta_data.
+ * \sa semop(2), struct \ref misc_meta_data.
  */
 void mutex_lock(int id)
 {
@@ -94,7 +94,7 @@ void mutex_lock(int id)
  *
  * This function either succeeds or aborts.
  *
- * \sa semop(2), struct misc_meta_data.
+ * \sa semop(2), struct \ref misc_meta_data.
  */
 void mutex_unlock(int id)
 {
@@ -161,6 +161,27 @@ int shm_attach(int id, enum shm_attach_mode mode, void **result)
        return *result == (void *) -1? -ERRNO_TO_PARA_ERROR(errno) : 1;
 }
 
+/**
+ * Get the size of a shared memory segment.
+ *
+ * \param id The shared memory segment identifier.
+ * \param result Size in bytes is returned here, zero on errors.
+ *
+ * \return Standard.
+ *
+ * \sa shmctl(2).
+ */
+int shm_size(int id, size_t *result)
+{
+       struct shmid_ds ds; /* data structure */
+
+       *result = 0;
+       if (shmctl(id, IPC_STAT, &ds) < 0)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       *result = ds.shm_segsz;
+       return 1;
+}
+
 /**
  * Detach a shared memory segment.
  *
diff --git a/ipc.h b/ipc.h
index c8d31c0c991641a1f71627c855705bdf7decd5c6..c44371045cdfb7a4313068a5e91bfb22fd366a84 100644 (file)
--- a/ipc.h
+++ b/ipc.h
@@ -11,4 +11,5 @@ int shm_new(size_t size);
 int shm_attach(int id, enum shm_attach_mode mode, void **result);
 int shm_detach(void *addr);
 int shm_destroy(int id);
+int shm_size(int id, size_t *result);
 size_t shm_get_shmmax(void);
diff --git a/m4/gengetopt/afh.m4 b/m4/gengetopt/afh.m4
deleted file mode 100644 (file)
index 9b8a650..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
-
-purpose "Print information about audio file(s)"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-
-###################################
-section "printing meta information"
-###################################
-
-option "chunk-table" c
-#~~~~~~~~~~~~~~~~~~~~~
-"print also the chunk table"
-flag off
-details = "
-       The 'chunk table' of an audio file is an array of offsets
-       within the audio file. Each offset corresponds to chunk
-       of encoded data. The exact meaning of 'chunk' depends on
-       the audio format.
-
-       Programs which are unaware of the particular audio format can
-       read the chunk table to obtain the timing information needed
-       to stream the file.
-"
-
-option "parser-friendly" p
-#~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not use human-readable output format"
-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.
-
-       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>
diff --git a/m4/gengetopt/afh_recv.m4 b/m4/gengetopt/afh_recv.m4
deleted file mode 100644 (file)
index 28a0a9e..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Make an audio stream from a local file"
-
-description "
-       The afh (audio format handler) receiver can be used to write
-       selected parts of the given audio file without decoding
-       the data.
-
-       The selected parts of the content of the audio file are passed
-       to the child nodes of the buffer tree. Only complete chunks
-       with respect of the underlying audio format are passed.
-"
-
-include(header.m4)
-<qu>
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"file to open"
-string typestr = "filename"
-required
-
-option "begin-chunk" b
-#~~~~~~~~~~~~~~~~~~~~~
-"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 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
-#~~~~~~~~~~~~~~~~~~~
-"only write up to chunk chunk_num"
-int typestr = "chunk_num"
-optional
-details = "
-       For the chunk_num argument the same rules as for --begin-chunk
-       apply. The default is to write up to the last chunk.
-"
-
-option "just-in-time" j
-#~~~~~~~~~~~~~~~~~~~~~~
-"use timed writes"
-flag off
-details = "
-       Write the specified chunks of data 'just in time', i.e. the
-       write of each chunk is delayed until the time it is needed
-       by the decoder/player in order to guarantee an uninterrupted
-       audio stream. This may be useful for third-party software
-       that is capable of reading from stdin.
-"
-
-option "no-header" H
-#~~~~~~~~~~~~~~~~~~~
-"do not write an audio file header"
-flag off
-details = "
-       If an audio format needs information about the audio file
-       in a format-specific header in order to be understood by
-       the decoding software, a suitable header is automatically
-       send. This option changes the default behaviour, i.e. no
-       header is written.
-"
-
-</qu>
diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4
deleted file mode 100644 (file)
index b2c5621..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Native ALSA output plugin"
-
-include(header.m4)
-
-<qu>
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr = "device"
-default = "default"
-optional
-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>
-
diff --git a/m4/gengetopt/amp_filter.m4 b/m4/gengetopt/amp_filter.m4
deleted file mode 100644 (file)
index a02cc5b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Amplify the decoded audio stream"
-
-option "amp" a
-#~~~~~~~~~~~~~
-"amplification value"
-int typestr="number"
-default="32"
-optional
-details="
-       The amplification value determines the scaling factor by
-       which the amplitude of the audio stream is multiplied. The
-       formula for the scaling factor is
-
-               factor = 1 + amp / 64.
-
-       For example, an amplifiction value of zero results in a
-       scaling factor of one while an amplification value of 64
-       means to double the volume.
-"
diff --git a/m4/gengetopt/ao_write.m4 b/m4/gengetopt/ao_write.m4
deleted file mode 100644 (file)
index 29112d7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for libao"
-
-include(header.m4)
-<qu>
-
-option "driver" d
-#~~~~~~~~~~~~~~~~
-"Select a output driver by name"
-string typestr = "name"
-optional
-details = "
-       If this is not given, the driver with the highest priority
-       (see below) will be used.
-"
-
-option "ao-option" o
-#~~~~~~~~~~~~~~~~~~~
-"Pass a key-value pair to the libao driver"
-string typestr = "key:value"
-optional
-multiple
-details = "
-       For each time this option is given, the supplied key-value
-       pair is appended to the list of options for the driver. Invalid
-       keys are silently ignored.
-"
-
-</qu>
diff --git a/m4/gengetopt/audioc.m4 b/m4/gengetopt/audioc.m4
deleted file mode 100644 (file)
index f216204..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
-
-purpose "Communicate with para_audiod through a local socket"
-
-include(header.m4)
-<qu>
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket (default=/var/paraslash/audiod.socket.$HOSTNAME)"
-       string typestr="filename"
-       optional
-
-
-option "bufsize" b
-#~~~~~~~~~~~~~~~~~
-"size of internal buffer"
-       int typestr="bytes"
-       default="8192"
-       optional
-</qu>
-
-define(CURRENT_PROGRAM,para_audioc)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/audioc.history)
-include(loglevel.m4)
-include(history_file.m4)
-include(complete.m4)
diff --git a/m4/gengetopt/audiod.m4 b/m4/gengetopt/audiod.m4
deleted file mode 100644 (file)
index 75c0458..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-args "--no-handle-help --no-handle-version --conf-parser"
-
-purpose "Connect to para_server, receive, decode and play audio streams"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_audiod)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(color.m4)
-include(config_file.m4)
-include(logfile.m4)
-include(log_timing.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-########################
-section "Audiod options"
-########################
-
-option "force" F
-#~~~~~~~~~~~~~~~
-"force startup"
-flag off
-details="
-       If this flag is not given, para_audiod refuses to start if the
-       well-known socket file (see the --socket option) already exists
-       because this usually means that para_audiod is already running
-       and listening on that socket. After a crash or if para_audiod
-       received a SIGKILL signal, a stale socket file might remain and
-       you have to use --force once to force startup of para_audiod.
-"
-
-option "mode" m
-#~~~~~~~~~~~~~~
-"startup mode"
-string typestr="mode"
-default="on"
-optional
-details="
-       Para_audiod supports three modes of operation: On, off and
-       standby (sb).  This option selects the mode that should be
-       used on startup. If para_audiod operates in \"on\" mode, it
-       will connect to para_server in order to receive its status
-       information. If para_server announces the availability of an
-       audio stream, para_audiod will automatically download, decode
-       and play the audio stream according to the given stream I/O
-       options, see below.
-
-       In \"standby\" mode, para_audiod will only receive the
-       status information from para_server but will not download
-       the audio stream.
-
-       In \"off\" mode, para_audiod does not connect para_server at
-       all, but still listens on the local socket for connections.
-"
-
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket"
-string typestr="filename"
-optional
-details="
-       Para_audiod uses a \"well-known\" socket to listen
-       on for connections from para_audioc. This socket is a
-       special file in the file system; its location defaults to
-       /var/paraslash/audiod_sock.<host_name>.
-
-       para_audioc, the client program used to connect to para_audiod,
-       opens this socket in order to talk to para_audiod.  If the
-       default value for para_audiod is changed, para_audioc must be
-       instructed to use also \"filename\" for connecting para_audiod.
-"
-
-option "user-allow" -
-#~~~~~~~~~~~~~~~~~~~~
-"allow this user to connect to audiod"
-string typestr = "username"
-optional
-multiple
-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" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sync clock on startup"
-int typestr="count"
-default="0"
-optional
-details="
-       Check the clock difference between the host running para_server
-       and the local host running para_audiod that many times before
-       starting any stream I/0. Set this to non-zero for non-local
-       setups if the clocks of these two hosts are not synchronized
-       by ntp or similar.
-"
-
-#############################
-section "Stream I/O options"
-#############################
-
-option "receiver" r
-#~~~~~~~~~~~~~~~~~~
-"select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-multiple
-details="
-       This option may be given multiple times, for each audio format
-       separately. If multiple definitions for an audio format are
-       given, the first one is selected.
-
-       The \"receiver_spec\" consists of an audio format specifier
-       and one or more receiver arguments, separated by a colon.
-
-       The audio format specifier is a regular expression which
-       specifies the set of audio formats for which this option
-       should apply.
-
-       If any receiver options are present, the whole receiver
-       argument must be quoted:
-
-               -r 'mp3:http -i my.host.org -p 8009'
-
-       Since a single dot '.' matches the name of any audio format,
-       specifying '.' instead of 'mp3' above activates the http
-       receiver for all audio formats.
-
-"
-
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify the filter configuration."
-string typestr = "filter_spec"
-optional
-multiple
-details = "
-       This option may be given multiple times. The \"filter_spec\"
-       consists of an audio format specifier (see above), the name
-       of the filter, and any options for that filter. Note that
-       order matters.
-
-       The compiled-in defaults apply to all audio formats for which
-       no --filter option was given. These defaults depend on the
-       receiver being used.
-
-       For HTTP streams, only the decoder for the current audio
-       format is activated. UDP and DCCP streams, on the other
-       hand, are sent FEC-encoded by para_server. In order to play
-       such streams, the receiver output must be FEC-decoded first,
-       i.e. fed to the fecdec filter. Therefore the default for UDP
-       and DCCP streams is to activate the fecdec filter, followed
-       by the decoding filter for the audio format.
-
-       Examples:
-
-               --filter 'mp3:mp3dec'
-
-               --filter 'mp3|aac:compress --inertia 5 --damp 2'
-
-               --filter '.:fecdec'
-
-"
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"Specify stream writer."
-string typestr="writer_spec"
-optional
-multiple
-details="
-       May be given multiple times, even multiple times for the same
-       audio format.  Default value is \"alsa\" for all supported
-       audio formats. Example:
-
-               --writer 'aac|wma:oss'
-
-"
-
-option "stream-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"time for client sync"
-int typestr="milliseconds"
-default="200"
-optional
-details="
-       Add the given amount of milliseconds to the stream start time
-       announced by para_server and do not send data to the writer
-       before that time (modulo clock difference).
-
-       This is useful mainly for synchronizing the audio output of
-       different clients.
-"
-</qu>
diff --git a/m4/gengetopt/channels.m4 b/m4/gengetopt/channels.m4
deleted file mode 100644 (file)
index 64c2518..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<qu>
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"specify number of channels"
-int typestr = "num"
-default = "2"
-optional
-details = "
-       It is only necessary to specify this option for raw audio. If
-       it is not given, the channel count is queried from the parent
-       buffer tree nodes (e.g. the decoder) or the wav header. Only
-       if this fails, the default value applies.
-"
-</qu>
diff --git a/m4/gengetopt/client.m4 b/m4/gengetopt/client.m4
deleted file mode 100644 (file)
index a5a27a0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-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"
-
-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
-#~~~~~~~~~~~~~~~~~~
-"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)
-include(config_file.m4)
-include(history_file.m4)
-include(complete.m4)
diff --git a/m4/gengetopt/color.m4 b/m4/gengetopt/color.m4
deleted file mode 100644 (file)
index eb08178..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<qu>
-
-option "color" C
-#~~~~~~~~~~~~~~~
-"activate color output"
-enum typestr="when"
-values = "yes","no","auto"
-default = "auto"
-optional
-
-option "log-color" -
-#~~~~~~~~~~~~~~~~~~~
-"select a color for one type of log message"
-string typestr="color_spec"
-multiple
-optional
-details="
-       The format of \"color_spec\" is [fg [bg]] [attr].
-
-       Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\",
-       \"red\", \"green\", \"yellow\", \"blue\", \"magenta\",
-       \"cyan\", and \"white\".
-
-       The \"attr\" value must be one of \"bold\", \"dim\", \"ul\",
-       \"blink\", \"reverse\".
-
-       Examples:
-
-               --log-color \"debug:green\"
-               --log-color \"info:yellow bold\"
-               --log-color \"notice:white red bold\"
-"
-
-</qu>
diff --git a/m4/gengetopt/complete.m4 b/m4/gengetopt/complete.m4
deleted file mode 100644 (file)
index 14e737c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "complete" -
-#~~~~~~~~~~~~~~~~~~
-"print possible command line completions"
-       flag off
-       details = "
-       If this flag is given, </qu>CURRENT_PROGRAM<qu> reads the environment
-       variables COMP_LINE and COMP_POINT to obtain the current command line
-       and the cursor position respectively, prints possible completions
-       to stdout and exits.
-"
-</qu>
diff --git a/m4/gengetopt/compress_filter.m4 b/m4/gengetopt/compress_filter.m4
deleted file mode 100644 (file)
index 501f46c..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Dynamically adjust the volume of an audio stream"
-
-option "blocksize" b
-#~~~~~~~~~~~~~~~~~~~
-"adjust block size"
-int typestr="number"
-default="15"
-optional
-details = "
-       Larger blocksize means fewer volume adjustments per time unit.
-"
-
-option "aggressiveness" a
-#~~~~~~~~~~~~~~~~~~~~~~~~
- "controls the maximum amount to amplify by"
-int typestr="number"
-default="4"
-optional
-
-option "inertia" i
-#~~~~~~~~~~~~~~~~~
- "how much inertia ramping has"
- int typestr="number"
-default="6"
-optional
-
-option "target-level" t
-#~~~~~~~~~~~~~~~~~~~~~~
-"target signal level (0-32768)"
-int typestr="number"
-default="20000"
-optional
-
-option "damp" d
-#~~~~~~~~~~~~~~
-"if non-zero, scale down after normalizing"
-int typestr="number"
-default="0"
-optional
diff --git a/m4/gengetopt/config_file.m4 b/m4/gengetopt/config_file.m4
deleted file mode 100644 (file)
index 29f66b4..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<qu>
-option "config-file" c
-#~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_CONFIG_FILE<qu>')"
-string typestr="filename"
-optional
-details="
-       </qu>CURRENT_PROGRAM<qu> reads its config file right after parsing
-       the options that were given at the command line. If an
-       option is given both at the command line and in the
-       config file, the value that was specified at the command line
-       takes precedence.
-"
-</qu>
diff --git a/m4/gengetopt/daemon.m4 b/m4/gengetopt/daemon.m4
deleted file mode 100644 (file)
index ebead6a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<qu>
-option "daemon" d
-#~~~~~~~~~~~~~~~~
-"run as background daemon"
-flag off
-details = "
-       If this option is given and no logfile was specified, all
-       messages go to /dev/null.
-"
-</qu>
diff --git a/m4/gengetopt/dccp_recv.m4 b/m4/gengetopt/dccp_recv.m4
deleted file mode 100644 (file)
index 1ba3fb5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive a DCCP audio stream"
-
-option "host" i
-"ip or host"
-string default="localhost"
-optional
-details="
-       Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-"port to connect to"
-int
-default="8000"
-optional
-
-option "ccid" c
-"CCID preference(s) for this connection"
-int
-# restrict the maximum number of times this option can be passed
-optional multiple(-10)
-# currently known CCIDs:
-# - CCID-2 (RFC 4341),
-# - CCID-3 (RFC 4342),
-# - CCID-4 (RFC 5622),
-# - CCID-248 ... CCID-254 are experimental (RFC 4340, 19.5)
-values="2", "3", "4", "248", "249", "250", "251", "252", "253", "254"
-details="
-       When present exactly once, this option mandates the CCID for the
-       sender-receiver connection. If it is passed more than once, it sets
-       a preference list where the order of appearance signifies descending
-       priority. For example, passing 4, 2, 3 creates the preference list
-       (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
-
-       The request is reconciled with the CCIDs on the server through the
-       'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
-       can be listed by calling 'para_client si'.
-
-"
diff --git a/m4/gengetopt/fade.m4 b/m4/gengetopt/fade.m4
deleted file mode 100644 (file)
index 59389ff..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "An alarm clock and volume-fader for OSS and ALSA"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_fade)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/fade.conf)
-<qu>
-section "General options"
-#########################
-
-</qu>
-include(config_file.m4)
-include(loglevel.m4)
-<qu>
-option "mode" o
-#~~~~~~~~~~~~~~
-"how to fade volume"
-       enum typestr = "mode"
-       values = "sleep", "fade", "set", "snooze"
-       default = "sleep"
-       optional
-       details="
-               para_fade knows the following modes:
-
-               sleep mode: Change to the initial volume and select
-               the initial afs mood/playlist. Then fade out until
-               the fade-out volume is reached. Switch to the
-               sleep mood/playlist until wake time minus fade-in
-               time. Finally switch to the wake mood/playlist and
-               fade to the fade-in volume.
-
-               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.
-"
-
-option "mixer-api" a
-#~~~~~~~~~~~~~~~~~~~
-"choose the mixer API"
-       string typestr = "api"
-       optional
-       details = "
-               ALSA is preferred over OSS if both APIs are supported
-               and this option is not given. To see the supported
-               mixer APIs, use this option with an invalid string
-               as the mixer API, e.g. --mixer-api help.
-       "
-
-option "mixer-device" m
-#~~~~~~~~~~~~~~~~~~~~~~
-"choose mixer device"
-       string typestr = "device"
-       optional
-       details = "
-               The default device (used if this option is not given)
-               depends on the selected mixer API. For ALSA, the
-               default is 'hw:0' which corresponds to the first sound
-               device. For OSS, '/dev/mixer' is used as the default.
-       "
-
-option "mixer-channel" C
-#~~~~~~~~~~~~~~~~~~~~~~~
-"select the analog mixer channel"
-       string typestr = "channel"
-       optional
-       details = "
-               For the ALSA mixer API, the possible values are
-               determined at runtime depending on the hardware and
-               can be printed by specifying an invalid mixer channel,
-               for example --mixer-channel help. The default channel
-               is 'Master'.
-
-               For OSS the possible values are invariably 'volume',
-               'bass', 'treble', 'synth', 'pcm', 'speaker', 'line',
-               'mic', 'cd', 'imix', 'altpcm', 'reclev', 'igain',
-               'ogain'. However, not all listed channels might be
-               supported on any particular hardware. The default
-               channel is 'volume'.
-       "
-
-section "Options for sleep mode"
-################################
-
-option "ivol" -
-#~~~~~~~~~~~~~~
-"set initial volume"
-       string typestr = "[channel:]volume"
-       default = "60"
-       optional
-       multiple
-       details = "
-               Used as the start volume, before fading out to the
-               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" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade out"
-       string typestr = "mood_spec"
-       default = "m/fade"
-       optional
-       details = "
-               Select this mood right after setting the
-               volume. Example: --fo-mood m/sleep
-"
-
-option "fo-time" -
-#~~~~~~~~~~~~~~~~~
-"fall asleep fade out time"
-       int typestr = "seconds"
-       default = "1800"
-       optional
-       details = "
-               No fading if set to 0.
-       "
-
-option "fo-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade out to"
-       int typestr = "volume"
-       default = "20"
-       optional
-
-option "sleep-mood" -
-#~~~~~~~~~~~~~~~~~~~~
-"sleep time afs mood/playlist"
-       string typestr = "mood_spec"
-       default = "m/sleep"
-       optional
-       details = "
-               Select the given afs mood/playlist after the fade
-               out is complete. If unset, the \"stop\" command is
-               sent to para_server.
-       "
-
-option "wake-hour" H
-#~~~~~~~~~~~~~~~~~~~
-"(0-23) (default: now + 9 hours)"
-       int typestr = "hour"
-       optional
-
-option "wake-min" M
-#~~~~~~~~~~~~~~~~~~
-"(0-59)"
-       int typestr = "minutes"
-       default = "0"
-       optional
-
-option "fi-mood" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade in"
-       string typestr = "mood_spec"
-       default = "m/wake"
-       optional
-       details = "
-               Change to this afs mood/playlist on wake time.
-       "
-
-option "fi-time" -
-#~~~~~~~~~~~~~~~~~
-"wake up fade in time"
-       int typestr="seconds"
-       default="1200"
-       optional
-       details = "
-               No fading in if set to 0.
-       "
-
-option "fi-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to at wake time"
-       int typestr = "volume"
-       default = "80"
-       optional
-
-section "Options for snooze mode"
-#################################
-
-option "so-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-out time"
-       int typestr = "seconds"
-       default = "30"
-       optional
-
-option "so-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to before snooze"
-       int typestr = "volume"
-       default = "20"
-       optional
-
-option "snooze-time" -
-#~~~~~~~~~~~~~~~~~~~~~
-"delay"
-       int typestr = "seconds"
-       default = "600"
-       optional
-
-option "si-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-in time"
-       int typestr = "seconds"
-       default = "180"
-       optional
-
-option "si-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to after snooze"
-       int typestr = "volume"
-       default = "80"
-       optional
-
-section "Options for fade mode"
-###############################
-
-option "fade-vol" f
-#~~~~~~~~~~~~~~~~~~
-"volume to fade to"
-       int typestr = "volume"
-       default = "50"
-       optional
-
-option "fade-time" t
-#~~~~~~~~~~~~~~~~~~~
-"fading time"
-       int typestr = "seconds"
-       default = "5"
-       optional
-
-section "Options for set mode"
-##############################
-
-option "val" -
-"value to set"
-       int typestr = "value"
-       default = "0"
-       optional
-
-</qu>
diff --git a/m4/gengetopt/file_write.m4 b/m4/gengetopt/file_write.m4
deleted file mode 100644 (file)
index 4f98884..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin that writes to a local file"
-
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"specify output file name"
-string typestr="filename"
-optional
-details="
-       Defaults to a random filename in ~/.paraslash.
-"
-
diff --git a/m4/gengetopt/filter.m4 b/m4/gengetopt/filter.m4
deleted file mode 100644 (file)
index b8b49f6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-args "--no-handle-help --no-handle-version --conf-parser"
-
-purpose "Decode or process audio data from STDIN to STDOUT"
-
-include(header.m4)
-include(loglevel.m4)
-<qu>
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify filter."
-string typestr="filter_spec"
-optional
-multiple
-details="
-       May be given multiple times to 'pipe' the stream through
-       arbitrary many filters (without copying the data). The same
-       filter may appear more than once, order matters.
-
-       Options for a particular filter may be specified for each
-       given '--filter' option separately. You will have to quote
-       these options like this:
-
-               --filter 'compress --inertia 5 --damp 2'
-"
-</qu>
diff --git a/m4/gengetopt/group.m4 b/m4/gengetopt/group.m4
deleted file mode 100644 (file)
index 2a59ad9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-option "group" g
-#~~~~~~~~~~~~~~~
-"set group id"
-string typestr="group"
-optional
-details="
-       This option sets the group id according to 'group'. This option
-       is silently ignored if EUID != 0. Otherwise, real/effective
-       GID and the saved set-group ID are all set to the GID given by
-       'group'. Must not be given in the config file.
-"
-
diff --git a/m4/gengetopt/gui.m4 b/m4/gengetopt/gui.m4
deleted file mode 100644 (file)
index a6b718e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Show para_audiod status in a curses window"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_gui)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/gui.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(config_file.m4)
-include(loglevel.m4)
-
-<qu>
-option "timeout" t
-#~~~~~~~~~~~~~~~~~
-"set timeout"
-int typestr = "milliseconds"
-default = "30"
-optional
-
-option "theme" T
-#~~~~~~~~~~~~~~~
-"select startup theme"
-string typestr = "name"
-optional
-details = "
-       If this option is not given the default theme is used.
-       If the given name is not a valid theme name, the list of
-       available themes is printed and the program terminates.
-"
-
-option "stat-cmd" s
-#~~~~~~~~~~~~~~~~~~
-"command to read status items from"
-string typestr = "command"
-default = "para_audioc -- stat -p"
-optional
-details = "
-       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
-
-       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
-#~~~~~~~~~~~~~~~~~
-"Map key k to command c using mode m."
-
-string typestr = "k:m:c"
-optional
-multiple
-details = "
-       Mode may be d, x or p for display, external and paraslash
-       commands, respectively. Of course, this option may be given
-       multiple times, one for each key mapping.
-"
-</qu>
diff --git a/m4/gengetopt/header.m4 b/m4/gengetopt/header.m4
deleted file mode 100644 (file)
index c423187..0000000
+++ /dev/null
@@ -1 +0,0 @@
-changequote(<qu>,</qu>)
diff --git a/m4/gengetopt/history_file.m4 b/m4/gengetopt/history_file.m4
deleted file mode 100644 (file)
index 73e98a7..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "history-file" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_HISTORY_FILE<qu>')"
-string typestr = "filename"
-optional
-details = "
-       If </qu>CURRENT_PROGRAM<qu> runs in interactive mode, it reads the history
-       file on startup. Upon exit, the in-memory history is appended
-       to the history file.
-"
-</qu>
diff --git a/m4/gengetopt/http_recv.m4 b/m4/gengetopt/http_recv.m4
deleted file mode 100644 (file)
index 6db3ff0..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive an HTTP audio stream"
-
-include(header.m4)
-
-<qu>
-option "host" i
-#~~~~~~~~~~~~~~
-"ip or host"
-string
-default="localhost"
-optional
-details="
-       Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-#~~~~~~~~~~~~~~
-"tcp port to connect to"
-int default="8000"
-optional
-</qu>
diff --git a/m4/gengetopt/log_timing.m4 b/m4/gengetopt/log_timing.m4
deleted file mode 100644 (file)
index ac0ea84..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "log-timing" T
-#~~~~~~~~~~~~~~~~~~~~
-"show milliseconds in log messages"
-flag off
-details = "
-       Selecting this option causes milliseconds to be included in
-       the log message output. This allows to measure the interval
-       between log messages in milliseconds which is useful for
-       identifying timing problems.
-"
-</qu>
diff --git a/m4/gengetopt/logfile.m4 b/m4/gengetopt/logfile.m4
deleted file mode 100644 (file)
index 070d736..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<qu>
-option "logfile" L
-#~~~~~~~~~~~~~~~~~
-"where to write log output"
-string typestr="filename"
-optional
-details="
-       If this option is not given, </qu>CURRENT_PROGRAM<qu> writes the log
-       messages to stderr.
-"
-</qu>
diff --git a/m4/gengetopt/loglevel.m4 b/m4/gengetopt/loglevel.m4
deleted file mode 100644 (file)
index 162d030..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<qu>
-option "loglevel" l
-#~~~~~~~~~~~~~~~~~~
-"set loglevel"
-string typestr="level"
-values = "debug","info","notice","warning","error","crit","emerg"
-default="warning"
-optional
-details="
-       Log only messages with severity greater or equal the given
-       value.
-
-       debug: Produces really noisy output.
-       info: Still noisy, but won't fill up the disk quickly.
-       notice: Indicates normal, but significant event.
-       warning: Unexpected events that can be handled.
-       error: Unhandled error condition.
-       crit: System might be unreliable.
-       emerg: Last message before exit.
-"
-
-</qu>
diff --git a/m4/gengetopt/makefile b/m4/gengetopt/makefile
deleted file mode 100644 (file)
index 0db9d10..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-define ggo_opts
-       --output-dir=$(cmdline_dir) \
-       --set-version="$(GIT_VERSION)" \
-       --arg-struct-name=$(*F)_args_info \
-       --file-name=$(*F).cmdline \
-       --func-name=$(*F)_cmdline_parser \
-       --set-package="para_$(*F)"
-endef
-
-.PRECIOUS: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h $(ggo_dir)/%.ggo
-
-$(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdline_dir)
-       @[ -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
-endif
-
-$(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) $< > $@
diff --git a/m4/gengetopt/mp3dec_filter.m4 b/m4/gengetopt/mp3dec_filter.m4
deleted file mode 100644 (file)
index 8b18783..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Decode an mp3 stream"
-
-include(header.m4)
-
-<qu>
-option "ignore-crc" i
-#~~~~~~~~~~~~~~~~~~~~
-"ignore CRC information in the audio stream."
-flag off
-details="
-       This causes frames with CRC errors to be decoded and played
-       anyway. This option is not recommended, but since some encoders
-       have been known to generate bad CRC information, this option
-       is a work-around to play streams from such encoders.
-"
-</qu>
diff --git a/m4/gengetopt/oss_write.m4 b/m4/gengetopt/oss_write.m4
deleted file mode 100644 (file)
index 578d813..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for the Open Sound System"
-
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr="device"
-default="/dev/dsp"
-optional
-details = ""
diff --git a/m4/gengetopt/osx_write.m4 b/m4/gengetopt/osx_write.m4
deleted file mode 100644 (file)
index 83ed737..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for Mac OS coreaudio"
-
-section "osx options"
-#####################
-
-option "numbuffers" n
-#~~~~~~~~~~~~~~~~~~~~~
-
-"number of audio buffers to allocate (increase if
-you get buffer underruns)"
-
-       int typestr="num"
-       default="20"
-       optional
-       details = ""
diff --git a/m4/gengetopt/play.m4 b/m4/gengetopt/play.m4
deleted file mode 100644 (file)
index 1687559..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help"
-
-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
-like stop, play, pause etc. In command mode the current audio file
-is shown and the program reads single key strokes from stdin. Keys
-may be mapped to commands. Whenever a mapped key is pressed, the
-associated command is executed."
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_play)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/play.history)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-include(loglevel.m4)
-include(config_file.m4)
-include(history_file.m4)
-<qu>
-
-###############################
-section "Options for para_play"
-###############################
-
-option "randomize" z
-#~~~~~~~~~~~~~~~~~~~
-"randomize playlist at startup."
-flag off
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to a command."
-
-string typestr = "key:command [args]"
-optional
-multiple
-details = "
-       This option may be given multiple times, one for each key
-       mapping. Example:
-               5:jmp 50
-"
-</qu>
diff --git a/m4/gengetopt/prebuffer_filter.m4 b/m4/gengetopt/prebuffer_filter.m4
deleted file mode 100644 (file)
index 20ff86f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Delay processing of an audio stream"
-
-option "duration" d
-#~~~~~~~~~~~~~~~~~~
-"prebuffer time"
-int typestr="milliseconds"
-default="200"
-optional
-details="
-       Wait that many milliseconds before letting data go through.
-       The time interval starts when the first data byte is seen by
-       this filter.
-"
-
-option "size" s
-#~~~~~~~~~~~~~~
-"amount of data to prebuffer"
-int typestr="bytes"
-default="0"
-optional
-details="
-       Wait until that many data bytes are available in the input buffer.
-       The default value of zero means to not prebuffer by size at all.
-       If both --duration and --size options are given and non-zero, the
-       filter waits until both conditions are met.
-"
diff --git a/m4/gengetopt/priority.m4 b/m4/gengetopt/priority.m4
deleted file mode 100644 (file)
index 0b37dc0..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-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.
-"
diff --git a/m4/gengetopt/recv.m4 b/m4/gengetopt/recv.m4
deleted file mode 100644 (file)
index 9a9a880..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-args "--no-handle-help --no-handle-version"
-
-purpose "A command line HTTP/DCCP/UDP stream grabber"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-option "receiver" r
-"Select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-details="
-       Any options for the selected receiver must
-       be quoted. Example:
-
-               -r 'http -i www.paraslash.org -p 8009'
-"
-</qu>
diff --git a/m4/gengetopt/resample_filter.m4 b/m4/gengetopt/resample_filter.m4
deleted file mode 100644 (file)
index 2e5f400..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Transform raw audio to a different sample rate"
-
-include(header.m4)
-
-option "converter" C
-#~~~~~~~~~~~~~~~~~~~
-"choose converter type"
-enum typestr = "type"
-values = "best", "medium", "fastest", "zero_order_hold", "linear"
-default = "medium"
-optional
-
-details = "
-       best: This is a bandlimited interpolator derived from the
-       mathematical sinc function and this is the highest quality
-       sinc based converter, providing a worst case Signal-to-Noise
-       Ratio (SNR) of 97 decibels (dB) at a bandwidth of 97%.
-
-       medium: This is another bandlimited interpolator much like the
-       previous one. It has an SNR of 97dB and a bandwidth of 90%. The
-       speed of the conversion is much faster than the previous one.
-
-       fastest: This is the fastest bandlimited interpolator and
-       has an SNR of 97dB and a bandwidth of 80%.
-
-       zero_order_hold: A Zero Order Hold converter (interpolated
-       value is equal to the last value). The quality is poor but
-       the conversion speed is blindlingly fast.
-
-       linear: A linear converter. Again the quality is poor, but
-       the conversion speed is blindingly fast.
-"
-
-include(channels.m4)
-include(sample_rate.m4)
-include(sample_format.m4)
-
-option "dest-sample-rate" d
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sample rate to convert to"
-int typestr = "rate"
-default = "44100"
-optional
diff --git a/m4/gengetopt/sample_format.m4 b/m4/gengetopt/sample_format.m4
deleted file mode 100644 (file)
index c998f9d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<qu>
-option "sample-format" f
-#~~~~~~~~~~~~~~~~~~~~~~~
-"specify sample format"
-# This must match the enum sample_format of para.h
-values = "S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE" enum
-default = "S16_LE"
-optional
-details = "
-       It is only necessary to specify this for raw audio. See the
-       discussion of the --channels option.
-"
-</qu>
diff --git a/m4/gengetopt/sample_rate.m4 b/m4/gengetopt/sample_rate.m4
deleted file mode 100644 (file)
index a8332a4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "sample-rate" s
-#~~~~~~~~~~~~~~~~~~~~~
-"do not guess the input sample rate"
-int typestr = "num"
-default = "44100"
-optional
-details = "
-       It is only necessary to specify this for raw audio. See the
-       discussion of the --channels option.
-"
-</qu>
diff --git a/m4/gengetopt/server.m4 b/m4/gengetopt/server.m4
deleted file mode 100644 (file)
index 48e7a1f..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Manage and stream audio files"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_server)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(log_timing.m4)
-include(color.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-option "port" p
-#~~~~~~~~~~~~~~
-"listening port"
-int typestr="portnumber"
-default="2990"
-optional
-details="
-       para_server listens on this tcp port for incoming connections
-       from clients such as para_client. If the default port is
-       changed, the corresponding option of para_client must be used
-       to connect to para_server.
-"
-
-#############################
-section "Configuration files"
-#############################
-</qu>
-
-include(logfile.m4)
-include(config_file.m4)
-
-<qu>
-option "user-list" -
-#~~~~~~~~~~~~~~~~~~~
-"(default='~/.paraslash/server.users')"
-
-string typestr="filename"
-optional
-
-
-##################################
-section "virtual streaming system"
-##################################
-
-
-option "autoplay" a
-#~~~~~~~~~~~~~~~~~~
-"start playing on startup"
-flag off
-
-option "autoplay-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-"time to wait before streaming"
-int typestr="ms"
-default="0"
-optional
-dependon="autoplay"
-details="
-       If para_server is started with the autoplay option, this option
-       may be used to set up a delay before para_server streams its
-       first audio file. This is useful for example if para_server
-       and para_audiod are started during system startup. The delay
-       time should be choosen large enough so that para_audiod is
-       already up when para_server starts to stream. Of course, this
-       option depends on the autoplay option.
-"
-option "announce-time" A
-#~~~~~~~~~~~~~~~~~~~~~~~
-"grace time for clients"
-
-int typestr="ms"
-default="300"
-optional
-details="
-       Clients such as para_audiod connect to para_server and execute
-       the stat command to find out whether an audio stream is
-       currently available. This sets the delay betweeen announcing
-       the stream via the output of the stat command and sending
-       the first chunk of data.
-"
-
-#############################
-section "audio file selector"
-#############################
-
-option "afs-database-dir" D
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"location of the database"
-string typestr="path"
-optional
-details="
-       Where para_server should look for the osl database of the audio
-       file selector. The default is '~/.paraslash/afs_database-0.4'.
-"
-
-option "afs-socket" s
-#~~~~~~~~~~~~~~~~~~~~
-"Command socket for afs"
-string typestr="path"
-default="/var/paraslash/afs_command_socket-0.4"
-optional
-details="
-       For each server command that is handled by the audio file
-       selector, the child process of para_server connects to the
-       audio file selector via a local socket. This option specifies
-       the location of that socket in the file system.
-"
-option "afs-initial-mode" i
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"Mood or playlist to load on startup."
-string typestr="<specifier>/<name>"
-optional
-
-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
-       loads the playlist named 'foo'.
-"
-
-#####################
-section "http sender"
-#####################
-
-
-option "http-port" -
-#~~~~~~~~~~~~~~~~~~~
-"tcp port for http streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
-       The http sender of para_server listens on this port for
-       incoming connections. Clients are expected to send the usual
-       http request message such as 'GET / HTTP/'.
-"
-
-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
-       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.
-"
-
-option "http-access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the http ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
-       Add given host/network to access control list (whitelist if
-       http-default-deny was given, blacklist otherwise) before
-       opening the tcp port. This option can be given multiple
-       times. Example: '192.168.0.0/24' whitelists/blacklists the
-       256 hosts 192.168.0.x
-"
-
-option "http-no-autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not open tcp port on startup"
-flag off
-details="
-       If this option is given, the http sender does not listen on
-       its tcp port. It may be instructed to open this port at a
-       later time by using the sender command.
-"
-
-option "http-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
-       The http sender will refuse connections if already that number
-       of clients are currently connected. A non-positive value
-       (the default) allows an unlimited number of simultaneous
-       connections.
-"
-
-#####################
-section "dccp sender"
-#####################
-
-
-option "dccp-port" -
-#~~~~~~~~~~~~~~~~~~~
-"port for dccp streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
-       See http-port for details.
-"
-
-option "dccp-default-deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the dccp ACL a whitelist"
-flag off
-details="
-       See http-default-deny for details.
-"
-
-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.
-"
-
-option "dccp-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
-       See http-max-clients for details.
-"
-
-option "dccp-max-slice-size" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"Upper bound for the FEC slice size"
-int typestr = "size"
-optional
-default = "0"
-details = "
-       If this value is non-positive (the default) the dccp sender
-       uses the maximum packet size (MPS) of the connection as the
-       slice size. The MPS is a network parameter and depends on
-       the path maximum transmission unit (path MTU) of an incoming
-       connection, i.e. on the largest packet size that can be
-       transmitted without causing fragmentation.
-
-       This option allows to use a value less than the MPS in order
-       to fine-tune application performance. Values greater than
-       the MPS of an incoming connection can not be set.
-"
-
-option "dccp-data-slices-per-group" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"The number of non-redundant slices per FEC group"
-int typestr = "num"
-optional
-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.
-
-       Note that the duration of a FEC group is proportional to the
-       product dccp-max-slice-size * dccp-data-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 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 you are on a lossy network.
-"
-
-####################
-section "udp sender"
-####################
-
-option "udp-target" -
-#~~~~~~~~~~~~~~~~~~~~
-"add udp target with optional port"
-string typestr="host[:port]"
-optional
-multiple
-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.
-
-       The following examples are possible targets:
-       '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port);
-       '224.0.1.38:1500' (IPv4 multicast); 'localhost:8001' (hostname
-       with port); '[::1]:8001' (IPv6 localhost); '[badc0de::1]' (IPv6
-       host with default port); '[FF00::beef]:1500' (IPv6 multicast).
-
-       This option can be given multiple times, for multiple targets.
-"
-
-option "udp-no-autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not start sending"
-flag off
-details="
-       If this option is given, udp streaming may be activated at
-       a later time by using the sender command.
-"
-
-option "udp-default-port" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"udp port to send to"
-int typestr="port"
-default="8000"
-optional
-
-option "udp-mcast-iface" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"outgoing udp multicast interface"
-string
-optional
-
-option "udp-header-interval" H
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"duration for sending header"
-int typestr = "ms"
-default = "2000"
-optional
-details = "
-       As the udp sender has no idea about connected clients it
-       sends the audio file header periodically if necessary. This
-       option specifies the duration between subsequent headers are
-       sent. Smaller values decrease the average time clients have
-       to wait before starting playback, larger values decrease
-       network traffic.
-
-       Note that this affects only ogg/* and wma streams. Other
-       audio formats, including mp3, don't need an audio file header.
-"
-
-option "udp-ttl" t
-#~~~~~~~~~~~~~~~~~
-"set time to live value"
-int typestr="num"
-default="-1"
-optional
-details="
-       This option applies exclusively to multicast UDPv4/v6 streaming.
-
-       For the sending UDPv4 socket it sets the multicast Time-To-Live
-       value to \"num\".  Traditional TTL scope values are: 0=host,
-       1=network, 32=same site, 64=same region, 128=same continent,
-       255=unrestricted. Please note however that this scoping is not
-       a good solution: RFC 2365 e.g. presents a better alternative.
-
-       When using UDPv6 multicasting, the option sets the number of
-       multicast hops (as described in RFC 3493); a value of -1
-       allows the kernel to auto-select the hop value.
-"
-</qu>
diff --git a/m4/gengetopt/sync_filter.m4 b/m4/gengetopt/sync_filter.m4
deleted file mode 100644 (file)
index 1e6f5f8..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-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.
-"
diff --git a/m4/gengetopt/udp_recv.m4 b/m4/gengetopt/udp_recv.m4
deleted file mode 100644 (file)
index dcdad4f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive an UDP audio stream"
-
-option "host" i
-"ip or host to receive udp packets from"
-string default="224.0.1.38"
-optional
-details="
-       The default address resolves to DANTZ.MCAST.NET and activates
-       multicast.
-"
-
-option "port" p "udp port"
-int typestr="portnumber"
-default="8000"
-optional
-
-option "iface" I "receiving udp multicast interface"
-string
-optional
diff --git a/m4/gengetopt/user.m4 b/m4/gengetopt/user.m4
deleted file mode 100644 (file)
index 1bd5c59..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<qu>
-option "user" u
-#~~~~~~~~~~~~~~
-"run as the given user"
-string typestr="name"
-optional
-details="
-       </qu>CURRENT_PROGRAM<qu> does not need any special privileges.
-
-       If started as root (EUID == 0) this option must
-       be given at the command line (not in the configuration
-       file) so that </qu>CURRENT_PROGRAM<qu> can drop the root
-       privileges right after parsing the command line options,
-       but before parsing the configuration file. In this case,
-       real/effective/saved UID are all set to the UID of 'name'. As
-       the configuration file is read afterwards, those options that
-       have a default value depending on the UID (e.g. the directory
-       for the configuration file) are computed by using the uid of
-       'name'. This option has no effect if </qu>CURRENT_PROGRAM<qu>
-       is started as a non-root user (i.e.  EUID != 0).
-" </qu>
diff --git a/m4/gengetopt/write.m4 b/m4/gengetopt/write.m4
deleted file mode 100644 (file)
index 6667cb8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-args "--no-handle-help --no-handle-version"
-
-purpose "Play wav or raw audio"
-
-include(header.m4)
-include(loglevel.m4)
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"select stream writer"
-string typestr="name"
-optional
-multiple
-details="
-       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)
-include(sample_rate.m4)
-include(sample_format.m4)
diff --git a/m4/lls/afh.suite.m4 b/m4/lls/afh.suite.m4
new file mode 100644 (file)
index 0000000..cf83b97
--- /dev/null
@@ -0,0 +1,94 @@
+m4_define(PROGRAM, para_afh)
+[suite afh]
+version-string = GIT_VERSION()
+[supercommand para_afh]
+       purpose = print or modify meta information about audio file(s)
+       [description]
+               By default para_afh prints information about the given audio files,
+               such as format, duration, bitrate, etc., and the content of the most
+               common meta tags (artist, title, album, year, comment). In modify
+               mode it changes the meta tags to the values given at the command line.
+       [/description]
+       non-opts-name = file...
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       [option modify-section]
+               summary = Printing meta information
+               flag ignored
+       [option chunk-table]
+               short_opt = c
+               summary = print also the chunk table
+               [help]
+                       The chunk table of an audio file is an array of offsets where each
+                       offset corresponds to chunk of encoded data. The exact meaning of
+                       chunk depends on the audio format.
+
+                       Programs which are unaware of the particular audio format can read the
+                       chunk table to obtain the timing information needed to stream the file.
+               [/help]
+       [option parser-friendly]
+               short_opt = p
+               summary = do not use human-readable output format
+               [help]
+                       This option only affects the format of the chunk table, 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.
+               [/help]
+       [option modify-section]
+               summary = Modifying meta tags
+               flag ignored
+       [option modify]
+               short_opt = m
+               summary = modify (rather than print) tags
+               [help]
+                       If this option is given, para_afh creates a temporary copy of the given
+                       file(s), 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.
+               [/help]
+       [option backup]
+               short_opt = b
+               summary = create backup of the original file
+               [help]
+                       The backup suffix is '~'. That is, a single tilde character is appended
+                       to the given file name.
+               [/help]
+       [option year]
+               short_opt = y
+               summary = set the year tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option title]
+               short_opt = t
+               summary = set the title tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option artist]
+               short_opt = a
+               summary = set the artist tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option album]
+               short_opt = A
+               summary = set the album tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option comment]
+               short_opt = C
+               summary = set the comment tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
diff --git a/m4/lls/audioc.suite.m4 b/m4/lls/audioc.suite.m4
new file mode 100644 (file)
index 0000000..1c037bc
--- /dev/null
@@ -0,0 +1,39 @@
+m4_define(PROGRAM, para_audioc)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/audioc.history)
+[suite audioc]
+version-string = GIT_VERSION()
+[supercommand para_audioc]
+       purpose = communicate with para_audiod through a local socket
+       non-opts-name = [command [options]]
+       [description]
+               The client program to control para_audiod at runtime. It allows to
+               enable/disable streaming, to receive status info, or to grab the
+               audio stream at any point of the decoding process.
+
+               If no command is given, para_audioc enters interactive mode.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(complete.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option socket]
+               short_opt = s
+               summary = path to well-known socket
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       The default path of the socket is
+                       /var/paraslash/audiod.socket.$HOSTNAME.
+               [/help]
+       [option bufsize]
+               short_opt = b
+               summary = size of internal buffer
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = bytes
+               default_val = 8192
diff --git a/m4/lls/audiod.suite.m4 b/m4/lls/audiod.suite.m4
new file mode 100644 (file)
index 0000000..31f6042
--- /dev/null
@@ -0,0 +1,191 @@
+m4_define(PROGRAM, para_audiod)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/audiod.conf)
+[suite audiod]
+version-string = GIT_VERSION()
+[supercommand para_audiod]
+       purpose = connect to para_server, receive, decode and play audio streams
+       [description]
+               para_audiod runs on a host with an audio device and connects
+               para_server to obtain status information and receive the current audio
+               stream. The stream is transformed through any number of filters and
+               then written to the configured output device.
+
+               Moreover, para_audiod listens on a local socket and sends status
+               information to local clients on request. Access to the local socket
+               can be restricted by means of Unix socket credentials, if available.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(logfile.m4)
+       m4_include(color.m4)
+       m4_include(log-timing.m4)
+       m4_include(daemon.m4)
+       m4_include(user.m4)
+       m4_include(group.m4)
+       m4_include(priority.m4)
+       m4_include(per-command-options-section.m4)
+       [option force]
+               summary = force startup
+               short_opt = F
+               [help]
+                       If this flag is not given, para_audiod refuses to start if the
+                       well-known socket file (see the --socket option) already exists
+                       because this usually means that para_audiod is already running and
+                       listening on that socket. After a crash or if para_audiod received
+                       a SIGKILL signal, a stale socket file might remain and you have to
+                       use --force once to force startup of para_audiod.
+               [/help]
+       [option mode]
+               summary = select startup mode
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               values = {AUDIOD_ON = "on", AUDIOD_OFF = "off",  AUDIOD_STANDBY = "sb"}
+               default_val = on
+               [help]
+                       para_audiod supports three modes of operation: On, off and standby
+                       (sb).  This option selects the mode that should be used at startup. If
+                       mode is "on", para_audiiod connects para_server to receive status
+                       information. If the server announces the availability of an audio
+                       stream, para_audiod downloads, decodes and plays the audio stream
+                       according to the given stream I/O options, see below.
+
+                       In "standby" mode, para_audiod only receives the status information
+                       from para_server but does not download the audio stream.
+
+                       In "off" mode, para_audiod does not connect para_server at all,
+                       but still listens on the local socket.
+               [/help]
+       [option socket]
+               short_opt = s
+               summary = path to the well-known socket
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       para_audiod listens on a "well-known" socket for connections
+                       from para_audioc. This socket is a special file in the file system;
+                       its location defaults to /var/paraslash/audiod_sock.$hostname.
+
+                       para_audioc, the client program used to connect to para_audiod,
+                       opens this socket in order to talk to para_audiod. If the default
+                       value for para_audiod is changed, para_audioc must be instructed to
+                       use also <path> to connect para_audiod.
+               [/help]
+       [option user-allow]
+               summary = allow this user to connect to audiod
+               arg_info = required_arg
+               arg_type = string
+               typestr = username
+               [help]
+                       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.
+               [/help]
+       [option clock-diff-count]
+               summary = sync clock on startup
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               [help]
+                       Check the clock difference between the host running para_server and
+                       the local host running para_audiod this many times before starting
+                       any stream I/0. Set this to non-zero for non-local setups if the
+                       clocks of these two hosts are not synchronized by ntp or similar.
+               [/help]
+       [option Stream-IO-options]
+               summary = Stream I/O options
+               flag ignored
+       [option receiver]
+               short_opt = r
+               summary = select receiver
+               arg_info = required_arg
+               arg_type = string
+               typestr = receiver_spec
+               default_val = http
+               flag multiple
+               [help]
+                       This option may be given multiple times, for each audio format
+                       separately. If multiple definitions for an audio format are given,
+                       the first one is selected.
+
+                       The <receiver_spec> consists of an audio format specifier and one or
+                       more receiver arguments, separated by a colon.
+
+                       The audio format specifier is a regular expression which specifies
+                       the set of audio formats for which this option should apply.
+
+                       If any receiver options are present, the whole receiver argument must
+                       be quoted:
+
+                               -r 'mp3:http -i my.host.org -p 8009'
+
+                       Since a single dot '.' matches the name of any audio format, specifying
+                       '.' instead of 'mp3' above activates the http receiver for all audio
+                       formats.
+               [/help]
+       [option filter]
+               short_opt = f
+               summary = specify the filter configuration
+               arg_info = required_arg
+               arg_type = string
+               typestr = filter_spec
+               flag multiple
+               [help]
+                       This option may be given multiple times. The <filter_spec< consists
+                       of an audio format specifier (see above), the name of the filter,
+                       and any options for that filter. Note that order matters.
+
+                       The compiled-in defaults apply to all audio formats for which no
+                       --filter option was given. These defaults depend on the receiver
+                       being used.
+
+                       For HTTP streams, only the decoder for the current audio format
+                       is activated. UDP and DCCP streams, on the other hand, are sent
+                       FEC-encoded by para_server. In order to play such streams, the
+                       receiver output must be FEC-decoded first, i.e. fed to the fecdec
+                       filter. Therefore the default for UDP and DCCP streams is to activate
+                       the fecdec filter, followed by the decoding filter for the audio
+                       format.
+
+                       Examples:
+
+                               --filter 'mp3:mp3dec'
+
+                               --filter 'mp3|aac:compress --inertia 5 --damp 2'
+
+                               --filter '.:fecdec'
+               [/help]
+       [option writer]
+               short_opt = w
+               summary = specify one or more stream writers
+               arg_info = required_arg
+               arg_type = string
+               typestr = writer_spec
+               flag multiple
+               [help]
+                       May be given multiple times, even multiple times for the same audio
+                       format. The default is to use the first supported writer. Example:
+
+                               --writer 'aac|wma:oss'
+               [/help]
+       [option stream-delay]
+               summary = specify time interval for client sync
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               [help]
+                       Check the clock difference between the host running para_server and
+                       the local host running para_audiod this many times before starting
+                       any stream I/0. Set this to non-zero for non-local setups if the
+                       clocks of these two hosts are not synchronized by ntp or similar.
+               [/help]
diff --git a/m4/lls/audiod_cmd.suite.m4 b/m4/lls/audiod_cmd.suite.m4
new file mode 100644 (file)
index 0000000..11de987
--- /dev/null
@@ -0,0 +1,100 @@
+[suite audiod_cmd]
+caption = list of audiod commands
+[subcommand cycle]
+       purpose = switch to next operating mode
+       [description]
+               Cycle through the three operation modes (on, standby, off).
+       [/description]
+
+[subcommand help]
+       purpose = display command list or help for given command
+       non-opts-name = [subcommand]
+       [description]
+               When I was younger, so much younger than today, I never needed
+               anybody's help in any way. But now these days are gone, I'm not so self
+               assured. Now I find I've changed my mind and opened up the doors. --
+               Beatles: Help
+       [/description]
+
+[subcommand grab]
+       purpose = grab the audio stream
+       [description]
+               grab ('splice') the audio stream at any position in the buffer tree
+               and send that data back to the client.
+       [/description]
+       [option mode]
+               short_opt = m
+               summary = change grab mode
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               default_val = s
+               [help]
+                       The various grab modes only differ in what happens if an attempt to
+                       write the grabbed audio data would block. Sloppy mode ("s") ignores
+                       the write, pedantic mode ("p") aborts and aggressive mode ("a")
+                       tries to write anyway.
+               [/help]
+       [option parent]
+               short_opt = p
+               summary = Grab output of the given node of the buffer tree
+               arg_info = required_arg
+               arg_type = string
+               typestr = node
+       [option name]
+               short_opt = n
+               summary = Name of the new buffer tree node. Defaults to 'grab'
+               arg_info = optional_arg
+               arg_type = string
+               typestr = string
+       [option one-shot]
+               short_opt = o
+               summary = One-shot mode: Stop grabbing if audio file changes
+
+[subcommand off]
+       purpose = deactivate para_audiod
+       [description]
+               Close connection to para_server and stop all decoders.
+       [/description]
+
+[subcommand on]
+       purpose = activate para_audiod
+       [description]
+               Establish connection to para_server, retrieve para_server's current
+               status. If playing, start corresponding decoder. Otherwise stop
+               all decoders.
+       [/description]
+
+[subcommand sb]
+       purpose = enter standby mode
+       [description]
+               Stop all decoders but leave connection to para_server open.
+       [/description]
+
+[subcommand stat]
+       purpose = print status information
+       non-opts-name = [item...]
+       [description]
+               Dump given status items (all if none given) to stdout.
+       [/description]
+       [option parser-friendly]
+               short_opt = p
+               summary = use parser-friendly output format
+
+[subcommand version]
+       purpose = print the version of para_audiod
+       [option verbose]
+               short_opt = v
+               summary = print detailed (multi-line) version text
+
+[subcommand tasks]
+       purpose = list current tasks
+       [description]
+               Print the list of task ids together with the status of each task.
+       [/description]
+
+[subcommand term]
+       purpose = terminate para_audiod
+       [description]
+               Stop all decoders, shut down connection to para_server and exit.
+       [/description]
diff --git a/m4/lls/client.suite.m4 b/m4/lls/client.suite.m4
new file mode 100644 (file)
index 0000000..30230c4
--- /dev/null
@@ -0,0 +1,63 @@
+m4_define(PROGRAM, para_client)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/client.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
+[suite client]
+version-string = GIT_VERSION()
+[supercommand para_client]
+       purpose = Communicate with para_server through the paraslash control port
+       non-opts-name = [command [options]]
+       [description]
+               para_client is the program used to connect to a running instance of
+               para_server. If a command is provided, this command is sent to the
+               server and any output received from the server is echoed to stdout. If
+               no command is given, para_client enters interactive mode.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(config-file.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option hostname]
+               short_opt = i
+               summary = IP address or name of the host where para_server is running
+               typestr = host
+               arg_info = required_arg
+               arg_type = string
+               default_val = localhost
+               [help]
+                       Both IPv4 and IPv6 addresses are supported.
+               [/help]
+       [option user]
+               short_opt = u
+               summary = the paraslash username to authenticate as
+               arg_info = required_arg
+               arg_type = string
+               typestr = name
+               [help]
+                       If this is not given, the current username is assumed.
+               [/help]
+       [option server-port]
+               short_opt = p
+               summary = the port on the remote host to connect
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 2990
+               [help]
+                       If this is not given, the current username is assumed.
+               [/help]
+       [option key-file]
+               short_opt = k
+               summary = path to private key
+               arg_info = required_arg
+               arg_type = string
+               typestr = filename
+               [help]
+                       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.
+               [/help]
+       m4_include(complete.m4)
diff --git a/m4/lls/copyright.m4 b/m4/lls/copyright.m4
new file mode 100644 (file)
index 0000000..9e5c541
--- /dev/null
@@ -0,0 +1,15 @@
+.SH COPYRIGHT
+Written by Andre Noll
+.br
+Copyright (C) COPYRIGHT_YEAR() Andre Noll
+.br
+License: GNU GPL version 2
+.br
+This is free software: you are free to change and redistribute it.
+.br
+There is NO WARRANTY, to the extent permitted by law.
+.br
+Report bugs to
+.MT <maan@tuebingen.mpg.de>
+Andre Noll
+.ME
diff --git a/m4/lls/filter.suite.m4 b/m4/lls/filter.suite.m4
new file mode 100644 (file)
index 0000000..0cd69eb
--- /dev/null
@@ -0,0 +1,37 @@
+m4_define(PROGRAM, para_filter)
+[suite filter]
+version-string = GIT_VERSION()
+[supercommand para_filter]
+       purpose = decode or process audio data from STDIN to STDOUT
+       [description]
+               This program allows to specify a chain of filters which transform the
+               audio stream read from STDIN. A common mode of operation is to decode
+               an mp3 file with the mp3dec filter, but many other filters exist which
+               transform the audio stream in different ways.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option filter]
+               short_opt = f
+               summary = add one filter to the filter chain
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = filter_spec
+               [help]
+                       A filter specifier begins with the name of a supported filter,
+                       optionally followed by zero or more options for the named filter.
+                       Filter name and options must be separated by whitespace. If the
+                       there is at least one option, the filter specifier needs to be quoted
+                       like this:
+
+                               --filter 'compress --inertia 5 --damp 2'
+
+                       This option may be specified multiple times to 'pipe' the stream
+                       through all given filters (in a single thread without copying the
+                       data). The same filter may appear more than once, and order matters.
+               [/help]
diff --git a/m4/lls/filter_cmd.suite.m4 b/m4/lls/filter_cmd.suite.m4
new file mode 100644 (file)
index 0000000..d269d23
--- /dev/null
@@ -0,0 +1,205 @@
+[suite filter_cmd]
+caption = filters
+[subcommand aacdec]
+       purpose = decode an aac stream
+[subcommand amp]
+       purpose = amplify (scale) a raw audio stream
+       [option amp]
+               short_opt = a
+               summary = amplification value
+               typestr = number
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 32
+               [help]
+                       The amplification value determines the scaling factor by which the
+                       amplitude of the audio stream is multiplied. The formula for the
+                       scaling factor is
+
+                               factor = 1 + amp / 64.
+
+                       For example, an amplification value of zero results in a scaling factor
+                       of one while an amplification value of 64 means to double the volume.
+               [/help]
+[subcommand compress]
+       purpose = dynamically adjust the volume of an audio stream
+       [option blocksize]
+               short_opt = b
+               summary = use blocks of size 2**bits
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 15
+               [help]
+                       Larger blocksize means fewer volume adjustments per time unit.
+               [/help]
+       [option aggressiveness]
+               short_opt = a
+               summary = controls the maximum amount to amplify by
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 4
+       [option inertia]
+               short_opt = i
+               summary = how much inertia ramping has
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 6
+       [option target-level]
+               short_opt = t
+               summary = target signal level (0-32768)
+               typestr = level
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 20000
+       [option damp]
+               short_opt = d
+               summary = if non-zero, scale down after normalizing
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 0
+[subcommand fecdec]
+       purpose = decode a (lossy) input stream using forward error correction
+[subcommand flacdec]
+       purpose = decode a flac stream
+[subcommand mp3dec]
+       purpose = decode an mp3 stream
+       [option ignore-crc]
+               short_opt = i
+               summary = ignore CRC information in the audio stream
+               [help]
+                       This causes frames with CRC errors to be decoded and played
+                       anyway. This option is not recommended, but since some encoders
+                       have been known to generate bad CRC information, this option is a
+                       work-around to play streams from such encoders.
+               [/help]
+[subcommand oggdec]
+       purpose = decode an ogg/vorbis stream
+[subcommand opusdec]
+       purpose = decode an ogg/opus stream
+[subcommand prebuffer]
+       purpose = delay processing of an audio stream
+       [option duration]
+               short_opt = d
+               summary = length of the prebuffer period
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 200
+               [help]
+                       Wait this many milliseconds before letting data go through. The time
+                       interval starts when the first data byte is seen in the input queue.
+               [/help]
+       [option size]
+               short_opt = s
+               summary = amount of data to prebuffer
+               typestr = bytes
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 0
+               [help]
+                       Wait until this many data bytes are available in the input queue. The
+                       default value of zero means to not prebuffer by size. If both
+                       --duration and --size are given and non-zero, the prebuffer filter
+                       waits until both conditions are met.
+               [/help]
+[subcommand resample]
+       purpose = transform raw audio to a different sample rate
+       [option converter]
+               short_opt = C
+               summary = set conversion algorithm
+               typestr = type
+               arg_info = required_arg
+               arg_type = string
+               values = {
+                       # RCT: resample conversion type
+                       RCT_BEST = "best",
+                       RCT_MEDIUM = "medium",
+                       RCT_FASTEST = "fastest",
+                       RCT_ZERO_ORDER_HOLD = "zero_order_hold",
+                       RCT_LINEAR = "linear"
+               }
+               default_val = medium
+               [help]
+                       best: This is a bandlimited interpolator derived from the mathematical
+                       sinc function and this is the highest quality sinc based converter,
+                       providing a worst case Signal-to-Noise Ratio (SNR) of 97 decibels
+                       (dB) at a bandwidth of 97%.
+
+                       medium: This is another bandlimited interpolator much like the previous
+                       one. It has an SNR of 97dB and a bandwidth of 90%. The speed of the
+                       conversion is much faster than the previous one.
+
+                       fastest: This is the fastest bandlimited interpolator and has an SNR
+                       of 97dB and a bandwidth of 80%.
+
+                       zero_order_hold: A Zero Order Hold converter (interpolated value
+                       is equal to the last value). The quality is poor but the conversion
+                       speed is blindlingly fast.
+
+                       linear: A linear converter. Again the quality is poor, but the
+                       conversion speed is blindingly fast.
+               [/help]
+       [option dest-sample-rate]
+               short_opt = d
+               summary = sample rate to convert to
+               typestr = rate
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 44100
+       m4_include(channels.m4)
+       m4_include(sample-rate.m4)
+       m4_include(sample-format.m4)
+[subcommand spxdec]
+       purpose = decode an ogg/speex stream
+[subcommand sync]
+       purpose = synchronize playback between multiple clients
+       [option buddy]
+               short_opt = b
+               summary = client to synchronize with
+               typestr = url
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               [help]
+                       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.
+               [/help]
+       [option port]
+               short_opt = p
+               summary = UDP port for incoming synchronization packets
+               typestr = portnumber
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 29900
+               [help]
+                       The sync filter expects incoming synchronization packets on this
+                       UDP port.
+               [/help]
+       [option timeout]
+               short_opt = t
+               summary = how long to wait for other clients
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 2000
+               [help]
+                       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.
+               [/help]
+[subcommand wav]
+       purpose = insert a Microsoft wave header into a raw audio stream
+[subcommand wmadec]
+       purpose = decode a wma stream
diff --git a/m4/lls/gui.suite.m4 b/m4/lls/gui.suite.m4
new file mode 100644 (file)
index 0000000..e0e45ef
--- /dev/null
@@ -0,0 +1,58 @@
+m4_define(PROGRAM, para_gui)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/gui.conf)
+[suite gui]
+version-string = GIT_VERSION()
+[supercommand para_gui]
+       purpose = show para_audiod status in a curses window
+       [description]
+               para_gui is a curses program which displays status information
+               about para_server and para_audiod in a terminal. Keys may be
+               mapped to user-defined commands which are executed when the key is
+               pressed. Command output is shown in a simple pager.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option theme]
+               short_opt = T
+               summary = select startup theme
+               arg_info = required_arg
+               arg_type = string
+               typestr = name
+               [help]
+                       If this option is not given the default theme is used. If the given
+                       name is not a valid theme name, the list of available themes is
+                       printed and the program terminates.
+               [/help]
+       [option stat-cmd]
+               short_opt = s
+               summary = command to read status items from
+               arg_info = required_arg
+               arg_type = string
+               typestr = command
+               default_val = para_audioc -- stat -p
+               [help]
+                       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
+
+                       This command connects para_server instead of para_audiod. However,
+                       no timing information about the current audio file is printed.
+               [/help]
+       [option key-map]
+               short_opt = k
+               summary = map a key to a command using one of three possible modes
+               arg_info = required_arg
+               arg_type = string
+               typestr = k:m:c
+               flag multiple
+               [help]
+                       Mode may be d, x or p for display, external and paraslash commands,
+                       respectively. Of course, this option may be given multiple times,
+                       one for each key mapping. See the manual for more information.
+               [/help]
diff --git a/m4/lls/include/channels.m4 b/m4/lls/include/channels.m4
new file mode 100644 (file)
index 0000000..ae26b73
--- /dev/null
@@ -0,0 +1,13 @@
+[option channels]
+       short_opt = c
+       summary = specify number of channels
+       typestr = num
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 2
+       [help]
+               It is only necessary to specify this option for raw audio. If it is
+               not given, the channel count is queried from the parent buffer tree
+               nodes (e.g. the decoder) or the wav header. Only if this query fails,
+               the default value applies.
+       [/help]
diff --git a/m4/lls/include/color.m4 b/m4/lls/include/color.m4
new file mode 100644 (file)
index 0000000..a23b325
--- /dev/null
@@ -0,0 +1,29 @@
+[option color]
+       short_opt = C
+       summary = activate color output
+       typestr = when
+       arg_info = required_arg
+       arg_type = string
+       values = {COLOR_YES = "yes", COLOR_NO = "no", COLOR_AUTO = "auto"}
+       default_val = auto
+[option log-color]
+       summary = select a color for one type of log message
+       typestr = color_spec
+       arg_info = required_arg
+       arg_type = string
+       flag multiple
+       [help]
+               The format of <color_spec> is [<fg> [<bg>]] [<attr>].
+
+               Valid colors for <fg> and <bg> are "normal", "black", "red", "green",
+               "yellow", "blue", "magenta", "cyan", and "white".
+
+               The <attr> value must be one of "bold", "dim", "ul", "blink",
+               "reverse".
+
+       Examples:
+
+               --log-color "debug:green"
+               --log-color "info:yellow bold"
+               --log-color "notice:white red bold"
+       [/help]
diff --git a/m4/lls/include/common-option-section.m4 b/m4/lls/include/common-option-section.m4
new file mode 100644 (file)
index 0000000..2a3b3e6
--- /dev/null
@@ -0,0 +1,8 @@
+[option common-option-section]
+       summary = Common options
+       flag ignored
+       [help]
+               The following options are implemented generically and are available
+               for many of the commands.
+       [/help]
+
diff --git a/m4/lls/include/complete.m4 b/m4/lls/include/complete.m4
new file mode 100644 (file)
index 0000000..c9f135c
--- /dev/null
@@ -0,0 +1,8 @@
+[option complete]
+       summary = print possible command line completions
+       [help]
+               If this flag is given, the environment variables COMP_LINE and
+               COMP_POINT are examined to obtain the current command line and the
+               cursor position respectively. Possible completions are written to
+               stdout and the program exits.
+       [/help]
diff --git a/m4/lls/include/config-file.m4 b/m4/lls/include/config-file.m4
new file mode 100644 (file)
index 0000000..a5de133
--- /dev/null
@@ -0,0 +1,14 @@
+[option config-file]
+       short_opt = c
+       summary = path to alternative config file
+       arg_info = required_arg
+       arg_type = string
+       typestr = filename
+       [help]
+               default: DEFAULT_CONFIG_FILE()
+
+               PROGRAM() reads its config file right after parsing the options that
+               were given at the command line. If an option is given both at the
+               command line and in the config file, the value that was specified at
+               the command line takes precedence.
+       [/help]
diff --git a/m4/lls/include/daemon.m4 b/m4/lls/include/daemon.m4
new file mode 100644 (file)
index 0000000..5c04712
--- /dev/null
@@ -0,0 +1,7 @@
+[option daemon]
+       short_opt = d
+       summary = run as background daemon
+       [help]
+               If this option is given and no logfile was specified, all messages
+               go to /dev/null.
+       [/help]
diff --git a/m4/lls/include/detailed-help.m4 b/m4/lls/include/detailed-help.m4
new file mode 100644 (file)
index 0000000..33a10f8
--- /dev/null
@@ -0,0 +1,3 @@
+
+[option detailed-help]
+       summary = print help, including all details, and exit
diff --git a/m4/lls/include/group.m4 b/m4/lls/include/group.m4
new file mode 100644 (file)
index 0000000..8d49cc7
--- /dev/null
@@ -0,0 +1,12 @@
+[option group]
+       short_opt = g
+       summary = set group id
+       arg_info = required_arg
+       arg_type = string
+       typestr = groupname
+       [help]
+               This option sets the group id according to <group>. This option is
+               silently ignored if EUID != 0. Otherwise, real/effective GID and the
+               saved set-group ID are all set to the GID given by <group>. Must not
+               be given in the config file.
+       [/help]
diff --git a/m4/lls/include/help.m4 b/m4/lls/include/help.m4
new file mode 100644 (file)
index 0000000..ff6ce3e
--- /dev/null
@@ -0,0 +1,3 @@
+[option help]
+       summary = print help and exit
+       short_opt = h
diff --git a/m4/lls/include/history-file.m4 b/m4/lls/include/history-file.m4
new file mode 100644 (file)
index 0000000..deb3d7e
--- /dev/null
@@ -0,0 +1,13 @@
+[option history-file]
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = location of the file for the command history list
+[help]
+       If PROGRAM() runs in interactive mode, it reads the history file
+       on startup. Upon exit, the in-memory history is appended to the
+       history file.
+
+       If this option is not given, the history file is expected at
+       DEFAULT_HISTORY_FILE().
+[/help]
diff --git a/m4/lls/include/host.m4 b/m4/lls/include/host.m4
new file mode 100644 (file)
index 0000000..3c0f0eb
--- /dev/null
@@ -0,0 +1,10 @@
+[option host]
+       short_opt = i
+       summary = IP address or hostname
+       typestr = host
+       arg_info = required_arg
+       arg_type = string
+       default_val = localhost
+       [help]
+               Both IPv4 and IPv6 addresses are supported.
+       [/help]
diff --git a/m4/lls/include/log-timing.m4 b/m4/lls/include/log-timing.m4
new file mode 100644 (file)
index 0000000..a7364e1
--- /dev/null
@@ -0,0 +1,9 @@
+[option log-timing]
+       short_opt = T
+       summary = show milliseconds in log messages
+       [help]
+               Selecting this option causes milliseconds to be included in
+               the log message output. This allows to measure the interval
+               between log messages in milliseconds which is useful for
+               identifying timing problems.
+       [/help]
diff --git a/m4/lls/include/logfile.m4 b/m4/lls/include/logfile.m4
new file mode 100644 (file)
index 0000000..e3d40a1
--- /dev/null
@@ -0,0 +1,10 @@
+[option logfile]
+short_opt = L
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = where to write log output
+[help]
+       If this option is not given, PROGRAM() writes the log messages
+       to stderr.
+[/help]
diff --git a/m4/lls/include/loglevel.m4 b/m4/lls/include/loglevel.m4
new file mode 100644 (file)
index 0000000..06bea3d
--- /dev/null
@@ -0,0 +1,23 @@
+m4_define(`downcase', `m4_translit(`$*', `A-Z', `a-z')')
+m4_define(`SUITE_LOGLEVELS', `m4_patsubst(`$*', `LL_\([A-Z]+\)',
+       `LSGLL_\1 = "downcase(`\1')" ')')
+[option loglevel]
+       summary = control amount of logging
+       short_opt = l
+       arg_info = required_arg
+       arg_type = string
+       typestr = severity
+       values = {SUITE_LOGLEVELS(LOGLEVELS())}
+       default_val = warning
+       [help]
+               Log only messages with severity greater or equal than the given
+               value. Possible values:
+
+               debug: Produces really noisy output.
+               info: Still noisy, but won't fill up the disk quickly.
+               notice: Indicates normal, but significant event.
+               warning: Unexpected events that can be handled.
+               error: Unhandled error condition.
+               crit: System might be unreliable.
+               emerg: Last message before exit.
+       [/help]
diff --git a/m4/lls/include/per-command-options-section.m4 b/m4/lls/include/per-command-options-section.m4
new file mode 100644 (file)
index 0000000..a303808
--- /dev/null
@@ -0,0 +1,3 @@
+[option per-command-option-section]
+       summary = Options for PROGRAM()
+       flag ignored
diff --git a/m4/lls/include/port.m4 b/m4/lls/include/port.m4
new file mode 100644 (file)
index 0000000..8dd63b6
--- /dev/null
@@ -0,0 +1,7 @@
+[option port]
+       short_opt = p
+       summary = TCP port to connect to
+       typestr = portnumber
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 8000
diff --git a/m4/lls/include/priority.m4 b/m4/lls/include/priority.m4
new file mode 100644 (file)
index 0000000..70d44b4
--- /dev/null
@@ -0,0 +1,13 @@
+[option priority]
+       summary = adjust scheduling priority
+       arg_info = required_arg
+       arg_type = int32
+       typestr = prio
+       default_val = 0
+       [help]
+               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.
+       [/help]
diff --git a/m4/lls/include/sample-format.m4 b/m4/lls/include/sample-format.m4
new file mode 100644 (file)
index 0000000..0daad19
--- /dev/null
@@ -0,0 +1,20 @@
+[option sample-format]
+       short_opt = f
+       summary = specify sample format
+       typestr = format
+       arg_info = required_arg
+       arg_type = string
+       # TODO: dedup this from enum in para.h.
+       values = {
+               SAMPLE_FORMAT_S8 = "S8",
+               SAMPLE_FORMAT_U8 = "U8",
+               SAMPLE_FORMAT_S16_LE = "S16_LE",
+               SAMPLE_FORMAT_S16_BE = "S16_BE",
+               SAMPLE_FORMAT_U16_LE = "U16_LE",
+               SAMPLE_FORMAT_U16_BE = "U16_BE"
+       }
+       default_val = S16_LE
+       [help]
+               It is only necessary to specify this for raw audio. See the discussion
+               of the --channels option.
+       [/help]
diff --git a/m4/lls/include/sample-rate.m4 b/m4/lls/include/sample-rate.m4
new file mode 100644 (file)
index 0000000..1433521
--- /dev/null
@@ -0,0 +1,11 @@
+[option sample-rate]
+       short_opt = s
+       summary = do not guess the input sample rate
+       typestr = rate
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 44100
+       [help]
+               It is only necessary to specify this for raw audio. See the discussion
+               of the --channels option.
+       [/help]
diff --git a/m4/lls/include/user.m4 b/m4/lls/include/user.m4
new file mode 100644 (file)
index 0000000..6d4d31b
--- /dev/null
@@ -0,0 +1,21 @@
+[option user]
+       short_opt = u
+       summary = run as the given user
+       arg_info = required_arg
+       arg_type = string
+       typestr = username
+       [help]
+               PROGRAM() does not need any special privileges.
+
+               If started as root (EUID == 0) this option must be given at the
+               command line (not in the configuration file) so that PROGRAM()
+               can drop the root privileges right after parsing the command line
+               options, but before parsing the configuration file. In this case,
+               real/effective/saved UID are all set to the UID of <username>. As
+               the configuration file is read afterwards, those options that have
+               a default value depending on the UID (e.g. the directory for the
+               configuration file) are computed by using the uid of <username>. This
+               option has no effect if PROGRAM() is started as a non-root user (i.e.
+               EUID != 0).
+       [/help]
+
diff --git a/m4/lls/include/version.m4 b/m4/lls/include/version.m4
new file mode 100644 (file)
index 0000000..a84ab9d
--- /dev/null
@@ -0,0 +1,3 @@
+[option version]
+       summary = print version and exit
+       short_opt = V
diff --git a/m4/lls/makefile b/m4/lls/makefile
new file mode 100644 (file)
index 0000000..cf3965e
--- /dev/null
@@ -0,0 +1,30 @@
+.PRECIOUS: $(lls_suite_dir)/%.suite $(lsg_h)
+lls_m4_include_dir := $(lls_m4_dir)/include
+
+$(lls_suite_dir)/%.m4d: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+       @[ -z "$(Q)" ] || echo 'M4D $<'
+
+       $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -s $< \
+       | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
+       print "$(lls_suite_dir)/$(*F).suite: " $$3}}' | sort | uniq > $@
+
+$(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+       $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
+               -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+               $< > $@
+
+$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGC $<'
+       $(Q) lopsubgen --gen-c --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGH $<'
+       $(Q) lopsubgen --gen-header --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGM $<'
+       $(Q) lopsubgen --gen-man --output-dir $(lls_suite_dir) < $<
+
+$(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
diff --git a/m4/lls/mixer.suite.m4 b/m4/lls/mixer.suite.m4
new file mode 100644 (file)
index 0000000..37ecbd8
--- /dev/null
@@ -0,0 +1,238 @@
+m4_define(PROGRAM, para_mixer)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/mixer.conf)
+[suite mixer]
+version-string = GIT_VERSION()
+caption = List of subcommands
+[supercommand para_mixer]
+       purpose = alarm clock and volume-fader for OSS and ALSA
+       synopsis = [<options>] -- [<subcommand>] [<subcommand_options>]
+       [description]
+               para_mixer adjusts the settings of an audio mixing device. It can
+               set the level of a mixer channel, or fade the level from one value
+               to another in a given time period. The sleep and snooze subcommands
+               contact para_server to start or stop streaming.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [help]
+               These options apply to several subcommands.
+       [/help]
+       [option mixer-api]
+               short_opt = a
+               summary = select alternative mixer API
+               arg_info = required_arg
+               arg_type = string
+               typestr = api
+               [help]
+                       ALSA is preferred over OSS if both APIs are supported and this option
+                       is not given. To see the supported mixer APIs, use this option with
+                       an invalid string as the mixer API, e.g. --mixer-api help.
+               [/help]
+       [option mixer-device]
+               short_opt = m
+               summary = set mixer device
+               arg_info = required_arg
+               arg_type = string
+               typestr = device
+               [help]
+                       The default device (used if this option is not given) depends
+                       on the selected mixer API. For ALSA, the default is 'hw:0' which
+                       corresponds to the first sound device. For OSS, '/dev/mixer' is used
+                       as the default.
+               [/help]
+       [option mixer-channel]
+               short_opt = C
+               summary = select a mixer channel
+               arg_info = required_arg
+               arg_type = string
+               typestr = channel
+               [help]
+                       For the ALSA mixer API, the possible values are determined at runtime
+                       depending on the hardware and can be printed by specifying an invalid
+                       mixer channel, for example --mixer-channel help. The default channel is
+                       'Master'.
+
+                       For OSS the possible values are invariably 'volume', 'bass', 'treble',
+                       'synth', 'pcm', 'speaker', 'line', 'mic', 'cd', 'imix', 'altpcm',
+                       'reclev', 'igain', 'ogain'. However, not all listed channels might be
+                       supported on any particular hardware. The default channel is 'volume'.
+               [/help]
+       [option fade-exponent]
+               summary = set non-linear time scale for fading
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = value
+               default_val = 0
+               [help]
+                       This option affects the fade, snooze and sleep subcommands. It is
+                       ignored in set mode.
+
+                       The argument must be a number between 0 and 100. The default value
+                       0 corresponds to linear scaling. That is, the value of the mixer
+                       channel is increased or decreased in fixed time intervals until the
+                       destination value is reached. Exponents between 1 and 99 cause low
+                       channel values to be increased more quickly than high channel values.
+                       Large exponents cause greater differences. The special value 100 sets
+                       the destination value immediately. The command then sleeps for the
+                       configured fade time.
+               [/help]
+[subcommand help]
+       purpose = print subcommand help
+       non-opts-name = [<subcommand>]
+       [option long]
+               summary = print the long help text
+               short_opt = l
+
+[subcommand set]
+       purpose = set a channel to the given value and exit
+       [option val]
+               summary = mixer channel value to set
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = value
+               default_val = 10
+[subcommand fade]
+       purpose = fade a channel to the given value in the given time
+       [option fade-vol]
+               short_opt = f
+               summary = destination volume for fading
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 50
+       [option fade-time]
+               short_opt = t
+               summary = duration of fade period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 5
+[subcommand snooze]
+       purpose = fade out, pause, sleep, play, fade in
+       [option so-time]
+               summary = duration of fade-out period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 30
+       [option so-vol]
+               summary = destination volume for fade-out
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 20
+       [option snooze-time]
+               summary = delay between end of fade-out and begin of fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 600
+       [option si-time]
+               summary = duration of fade-in period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 180
+       [option si-vol]
+               summary = destination volume for fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 80
+[subcommand sleep]
+       purpose = stream, fade out, sleep, fade in
+       [description]
+               Change to the initial volume and select the initial mood/playlist.
+               Fade out to the given fade-out volume in the specified time. Switch
+               to the sleep mood/playlist and wait until wake time minus fade-in
+               time. Finally, switch to the wake mood/playlist and fade in to the
+               fade-in volume.
+       [/description]
+       [option ivol]
+               summary = set initial volume
+               arg_info = required_arg
+               arg_type = string
+               default_val = 60
+               flag multiple
+               typestr = [channel:]volume
+               [help]
+                       Used as the start volume, before fading out to the fade-out volume. The
+                       channel part may be omitted, in which case the default channel is
+                       used. This option may be given multiple times.
+               [/help]
+       [option fo-mood]
+               summary = mood or playlist for fade-out
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/fade
+               [help]
+                       This mood (or playlist) is selected right after setting the initial
+                       volume.
+               [/help]
+       [option fo-time]
+               summary = duration of fade-out period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 1800
+               [help]
+                       No fading if this is set to 0.
+               [/help]
+       [option fo-vol]
+               summary = destination volume for fade-out
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 20
+       [option sleep-mood]
+               summary = mood/playlist between fade-out and fade-in
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/sleep
+               [help]
+                       Select the given mood or playlist after the fade-out. If unset,
+                       playback is stopped until fade-in starts.
+               [/help]
+       [option wake-time]
+               short_opt = w
+               summary = when to start fade in
+               arg_info = required_arg
+               arg_type = string
+               typestr = [+][HH][:MM]
+               default_val = +9:00
+               [help]
+                       If the optional plus character is given, the wake time is computed as
+                       now + HH hours + MM minutes. Otherwise the HH:MM argument is considered
+                       an absolute time (referring to either the current or the next day).
+               [/help]
+       [option fi-mood]
+               summary = mood or playlist for fade-in
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/wake
+               [help]
+                       This mood or playlist is selected right before fade-in begins.
+               [/help]
+       [option fi-time]
+               summary = duration of fade-in period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 1200
+               [help]
+                       No fading if this is set to 0.
+               [/help]
+       [option fi-vol]
+               summary = destination volume for fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 80
diff --git a/m4/lls/play.suite.m4 b/m4/lls/play.suite.m4
new file mode 100644 (file)
index 0000000..0fbba0c
--- /dev/null
@@ -0,0 +1,40 @@
+m4_define(PROGRAM, para_play)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/play.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
+[suite play]
+version-string = GIT_VERSION()
+[supercommand para_play]
+       purpose = command line audio player
+       non-opts-name = <audio_file>...
+       [description]
+               para_play operates either in command mode or in insert mode. In
+               insert mode it presents a prompt and allows to enter commands like
+               stop, play, pause etc. In command mode the current audio file and the
+               playback position are shown and the program reads single key strokes
+               from stdin. Keys may be mapped to commands so that the configured
+               command is executed when a mapped key is pressed.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(config-file.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option randomize]
+               short_opt = z
+               summary = randomize playlist at startup
+       [option key-map]
+               short_opt = k
+               summary = map a key to a command
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = key:command [args]
+               [help]
+                       This option may be given multiple times, one for each key
+                       mapping. Example:
+
+                               --key-map 5:jmp 50
+               [/help]
diff --git a/m4/lls/play_cmd.suite.m4 b/m4/lls/play_cmd.suite.m4
new file mode 100644 (file)
index 0000000..195b47b
--- /dev/null
@@ -0,0 +1,92 @@
+[suite play_cmd]
+caption = list of commands
+[subcommand help]
+       purpose = list commands or keybindings, or print command-specific help
+       non-opts-name = [command]
+       [description]
+
+               This command acts differently depending on whether it is executed in
+               command mode or in insert mode.
+
+               In command mode, the help command prints the list of defined
+               keybindings. In insert mode, if no argument is given, it prints the
+               list of available commands. When called with the name of a command
+               as first argument, it prints the description of this command.
+       [/description]
+
+[subcommand fg]
+       purpose = enter command mode
+       [description]
+               In this mode, file name and play time of the current audio file are
+               displayed. Hit CTRL+C or : to switch to input mode.
+       [/description]
+[subcommand next]
+       purpose = load the next file of the playlist
+
+[subcommand prev]
+       purpose = load the previous file of the playlist
+
+[subcommand bg]
+       purpose = enter insert mode
+       [description]
+               Only useful if called in command mode via a key binding. The default
+               key bindings map the colon key to this command, so pressing : in
+               command mode activates insert mode.
+       [/description]
+
+[subcommand jmp]
+       purpose = change playback position
+       non-opts-name = percent
+       [description]
+               The percent argument must be an integer between 0 and 100, inclusively.
+       [/description]
+
+[subcommand ff]
+       purpose = set playback position relative to the current position
+       non-opts-name = seconds
+       [description]
+               Negative values mean to jump backwards the given amount of seconds.
+       [/description]
+
+[subcommand ls]
+       purpose = list the playlist
+       [description]
+               This prints all paths of the playlist. The currently
+               active file is marked with an asterisk.
+       [/description]
+
+[subcommand info]
+       purpose = print information about the current file
+       [description]
+               The output contains the playlist position, the path
+               and information provided by the audio format handler.
+       [/description]
+
+[subcommand play]
+       purpose = start or resume playback
+       non-opts-name = [num]
+       [description]
+               If no argument is given, playback starts at the current
+               position. Otherwise, the corresponding file is loaded
+               and playback is started at the beginning of the file.
+       [/description]
+
+[subcommand pause]
+       purpose = suspend playback
+       [description]
+               When paused, it is still possible to jump around in
+               the file via the jmp and ff commands.
+       [/description]
+
+[subcommand tasks]
+       purpose = print list of active tasks
+       [description]
+               Mainly useful for debugging.
+       [/description]
+
+[subcommand quit]
+       purpose = exit para_play
+       [description]
+               Pressing CTRL+D causes EOF on stdin which also exits
+               para_play.
+       [/description]
diff --git a/m4/lls/recv.suite.m4 b/m4/lls/recv.suite.m4
new file mode 100644 (file)
index 0000000..793a2f5
--- /dev/null
@@ -0,0 +1,22 @@
+m4_define(PROGRAM, para_recv)
+[suite recv]
+version-string = GIT_VERSION()
+[supercommand para_recv]
+       purpose = a command line HTTP/DCCP/UDP stream grabber
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option receiver]
+               short_opt = r
+               summary = select the receiver
+               arg_info = required_arg
+               arg_type = string
+               typestr = receiver_spec
+               [help]
+                       Any options for the selected receiver must be quoted. Example:
+
+                               -r 'http -i www.paraslash.org -p 8009'
+               [/help]
diff --git a/m4/lls/recv_cmd.suite.m4 b/m4/lls/recv_cmd.suite.m4
new file mode 100644 (file)
index 0000000..674a448
--- /dev/null
@@ -0,0 +1,102 @@
+[suite recv_cmd]
+caption = receivers
+[subcommand afh]
+       purpose = make an audio stream from a local file
+       [description]
+               The afh (audio format handler) receiver extracts selected parts of
+               the given audio file without decoding the file. Only complete chunks
+               with respect to the underlying audio format are extracted.
+       [/description]
+       [option filename]
+               short_opt = f
+               summary = file to open
+               typestr = filename
+               arg_info = required_arg
+               arg_type = string
+       [option begin-chunk]
+               short_opt = b
+               summary = skip the beginning of the file
+               typestr = chunk_num
+               arg_info = required_arg
+               arg_type = int32
+               [help]
+                       The argument must be an integer between -num_chunks and num_chunks -
+                       1, inclusively, where num_chunks is the total number of chunks. 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 at chunk num_chunks - 100. This is useful
+                       for cutting off the beginning of an audio file.
+               [/help]
+       [option end-chunk]
+               short_opt = e
+               summary = only write up to chunk chunk_num
+               typestr = chunk_num
+               arg_info = required_arg
+               arg_type = int32
+               [help]
+                       For the chunk_num argument the same rules as for --begin-chunk
+                       apply. The default is to write up to the last chunk.
+               [/help]
+       [option just-in-time]
+               short_opt = j
+               summary = use timed writes
+               [help]
+                       Write the specified data chunks 'just in time', i.e., delay the write
+                       until data is needed by the decoder/player for an uninterrupted audio
+                       stream. This may be useful for third-party software.
+               [/help]
+       [option no-header]
+               short_opt = h
+               summary = do not write an audio file header
+               [help]
+                       Some audio formats store information about the audio file in
+                       a format-specific header which is needed to decode any part of
+                       the file. For such formats the afh receiver generates a suitable
+                       header. This option changes the default behaviour, i.e. no header
+                       is written.
+               [/help]
+[subcommand http]
+       purpose = receive an audio stream over HTTP
+       m4_include(host.m4)
+       m4_include(port.m4)
+[subcommand dccp]
+       purpose = receive an audio stream over DCCP
+       m4_include(host.m4)
+       m4_include(port.m4)
+       [option ccid]
+               short_opt = c
+               summary = CCID preference(s) for this connection
+               typestr = id
+               arg_info = required_arg
+               arg_type = uint32
+               flag multiple
+               [help]
+                       When present exactly once, this option mandates the CCID for the
+                       sender-receiver connection. If it is passed more than once, it sets
+                       a preference list where the order of appearance signifies descending
+                       priority. For example, passing 4, 2, 3 creates the preference list
+                       (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
+
+                       The request is reconciled with the CCIDs on the server through the
+                       'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
+                       can be listed by calling 'para_client si'.
+               [/help]
+[subcommand udp]
+       purpose = receive an audio stream over UDP
+       [option host]
+               short_opt = i
+               summary = IP address or hostname
+               typestr = host
+               arg_info = required_arg
+               arg_type = string
+               default_val = 224.0.1.38
+               [help]
+                       The default address resolves to DANTZ.MCAST.NET and activates
+                       multicast.
+               [/help]
+       m4_include(port.m4)
+       [option iface]
+               summary = receiving udp multicast interface
+               typestr = iface-name
+               arg_info = required_arg
+               arg_type = string
diff --git a/m4/lls/server.suite.m4 b/m4/lls/server.suite.m4
new file mode 100644 (file)
index 0000000..5bba85d
--- /dev/null
@@ -0,0 +1,358 @@
+m4_define(PROGRAM, para_server)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/server.conf)
+[suite server]
+version-string = GIT_VERSION()
+[supercommand para_server]
+       purpose = manage and stream audio files
+       [description]
+               para_server streams audio files over a local or remote network. It
+               is controlled by para_client(1), which connects para_server through
+               the paraslash control service.
+
+               On startup the server spawns a second process, the audio file selector,
+               which maintains the database of all known audio files. This database
+               contains file format, duration and tag information of each known file
+               and statistics such as last-played time, and the number of times each
+               file was streamed. Lyrics and cover art may be added to the database
+               and associated with one or more audio files.
+
+               Besides ordinary playlists the audio file selector supports so-called
+               moods. Moods instruct the server to determine the files to be streamed
+               and their order in terms of properties stored in the database.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(priority.m4)
+       m4_include(daemon.m4)
+       m4_include(logfile.m4)
+       m4_include(user.m4)
+       m4_include(group.m4)
+       m4_include(loglevel.m4)
+       m4_include(log-timing.m4)
+       m4_include(color.m4)
+       m4_include(per-command-options-section.m4)
+       [option port]
+               short_opt = p
+               summary = listening port of the paraslash control service
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 2990
+               [help]
+                       para_server listens on this TCP port for incoming connections
+                       from clients such as para_client. If the default port is changed,
+                       the corresponding option of para_client must be used to connect
+                       to para_server.
+               [/help]
+       [option user-list]
+               summary = file which contains user names and credentials
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       This file contains one line per user of the form
+
+                               user <username> <key> <perms>
+
+                       See the manual for more information.
+               [/help]
+       [option vss]
+               summary = Options for the virtual streaming system
+               flag ignored
+       [option autoplay]
+               summary = start streaming on startup
+               short_opt = a
+               [help]
+                       The default is to defer streaming until para_client connects and
+                       executes the "play" command.
+               [/help]
+       [option autoplay-delay]
+               summary = time to wait before streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 0
+               [help]
+                       This option is ignored if --autplay is not given. Otherwise, its
+                       argument defines for how long streaming is delayed at startup.
+
+                       This is useful in init scripts to set the delay large enough to make
+                       sure para_audiod is up when para_server starts to stream.
+               [/help]
+       [option announce-time]
+               short_opt = A
+               summary = grace time for data connections
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 300
+               [help]
+                       para_server tells para_audiod through the control service connection
+                       whether an audio stream is currently available. This option defines
+                       the delay between announcing the stream and sending the first chunk
+                       of audio data.
+               [/help]
+       [option afs]
+               summary = Options for the audio file selector
+               flag ignored
+       [option afs-database-dir]
+               summary = location of the afs database
+               short_opt = D
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the database for the audio file
+                       selector. The default is ~/.paraslash/afs_database-0.4.
+
+                       If no database was found, the "init" command must be executed to
+                       initialize the database. Once initialized, audio files may added with
+                       the "add" command.
+               [/help]
+       [option afs-socket]
+               summary = socket for afs connections
+               short_opt = s
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               default_val = /var/paraslash/afs_command_socket-0.4
+               [help]
+                       Server commands communicate with the audio file selector, via a
+                       local socket. This option specifies the location of the socket in
+                       the file system.
+               [/help]
+       [option afs-initial-mode]
+               summary = mood or playlist to load on startup
+               short_opt = i
+               arg_info = required_arg
+               arg_type = string
+               typestr = specifier/name
+               [help]
+                       The argument of this option consists of a prefix, either 'm/' or
+                       'p/', to indicate whether a mood or a playlist should be loaded,
+                       followed by the name of the mood or playlist. Example:
+
+                               --afs-initial-mode p/foo
+
+                       loads the playlist named "foo".
+
+                       If this option is not given, the dummy mood is loaded at startup.
+               [/help]
+       [option http]
+               summary = Options for the http sender
+               flag ignored
+       [option http-port]
+               summary = TCP port for http streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+               [help]
+                       The http sender of para_server listens on this port for incoming
+                       connections. Clients are expected to send the usual http request
+                       message such as 'GET / HTTP/'.
+               [/help]
+       [option http-default-deny]
+               summary = make the http access control list a whitelist
+               [help]
+                       The default is to use blacklists, i.e. connections to the http sender
+                       are allowed unless the connecting host 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.
+               [/help]
+       [option http-access]
+               summary = add an entry to the http access control list
+               arg_info = required_arg
+               arg_type = string
+               typestr = a.b.c.d/n
+               flag multiple
+               [help]
+                       Add the given host/network to access control list (whitelist if
+                       http-default-deny was given, blacklist otherwise) before opening
+                       the tcp port. This option can be given multiple times. Example:
+
+                               --http-access 192.168.0.0/24
+
+                       whitelists/blacklists the 256 hosts 192.168.0.x.
+
+                       This option may be given multiple times to blacklist/whitelist any
+                       number of hosts or networks.
+               [/help]
+       [option http-no-autostart]
+               summary = do not open TCP port for http streaming on startup
+               [help]
+                       If this option is given, the http sender does not listen on its TCP
+                       port until the "sender" command is executed to open the port.
+               [/help]
+       [option http-max-clients]
+               summary = maximal number of simultaneous http connections
+               arg_info = required_arg
+               arg_type = int32
+               typestr = number
+               default_val = -1
+               [help]
+                       The http sender will refuse connections if already that number of
+                       clients are currently connected. A non-positive value (the default)
+                       allows for an unlimited number of simultaneous connections.
+               [/help]
+       [option dccp]
+               summary = Options for the dccp sender
+               flag ignored
+       [option dccp-port]
+               summary = port for dccp streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+               [help]
+                       See --http-port for details.
+               [/help]
+       [option dccp-default-deny]
+               summary = make the dccp access control list a whitelist
+               [help]
+                       See http-default-deny for details.
+               [/help]
+       [option dccp-access]
+               summary = add an entry to the dccp access control list
+               arg_info = required_arg
+               arg_type = string
+               typestr = a.b.c.d/n
+               flag multiple
+               [help]
+                       See --http-access for details.
+               [/help]
+       [option dccp-max-clients]
+               summary = maximal number of simultaneous dccp connections
+               arg_info = required_arg
+               arg_type = int32
+               typestr = number
+               default_val = -1
+               [help]
+                       See --http-max-clients for details.
+               [/help]
+       [option dccp-max-slice-size]
+               summary = upper bound for the FEC slice size
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = bytes
+               default_val = 0
+               [help]
+                       If this value is zero (the default) the dccp sender uses the maximum
+                       packet size (MPS) of the connection as the slice size. The MPS is a
+                       network parameter and depends on the path maximum transmission unit
+                       (path MTU) of an incoming connection, i.e. on the largest packet size
+                       that can be transmitted without causing fragmentation.
+
+                       This option allows to use a value less than the MPS in order to
+                       fine-tune application performance. Values greater than the MPS of an
+                       incoming connection can not be set.
+               [/help]
+       [option dccp-data-slices-per-group]
+               summary = the number of non-redundant slices per FEC group
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               default_val = 3
+               [help]
+                       This determines the number of slices in each FEC group which are
+                       necessary to decode the group. The given number must be smaller than
+                       the argument to 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.
+               [/help]
+       [option dccp-slices-per-group]
+               summary = the total number of slices per FEC group
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               default_val = 4
+               [help]
+                       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 you are on a lossy network.
+               [/help]
+       [option udp]
+               summary = Options for the udp sender
+               flag ignored
+       [option udp-target]
+               summary = add udp target with optional port
+               arg_info = required_arg
+               arg_type = string
+               typestr = host[:port]
+               flag multiple
+               [help]
+                       Add the 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 (see below) is used.
+
+                       The following examples are possible targets: "10.10.1.2:8000"
+                       (host:port); "10.10.1.2" (with default port); "224.0.1.38:1500"
+                       (IPv4 multicast); "localhost:8001" (hostname with port); "[::1]:8001"
+                       (IPv6 localhost); "[badc0de::1]" (IPv6 host with default port);
+                       "[FF00::beef]:1500" (IPv6 multicast).
+
+                       This option can be given multiple times, for multiple targets.
+               [/help]
+       [option udp-default-port]
+               summary = default port for udp targets
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+       [option udp-no-autostart]
+               summary = do not send the audio stream to UDP targets
+               [help]
+                       If this option is given, udp streaming may be activated at a later
+                       time by executing the "sender" command.
+               [/help]
+       [option udp-mcast-iface]
+               summary = outgoing udp multicast interface
+               arg_info = required_arg
+               arg_type = string
+               typestr = interface
+       [option udp-header-interval]
+               short_opt = H
+               summary = duration for sending header
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 2000
+               [help]
+                       As the udp sender has no idea about connected clients it sends the
+                       audio file header periodically if necessary. This option specifies the
+                       duration between subsequent headers are sent. Smaller values decrease
+                       the average time clients have to wait before starting playback,
+                       larger values decrease network traffic.
+
+                       Note that this affects only ogg/* and wma streams. Other audio formats,
+                       including mp3, don't need an audio file header.
+               [/help]
+       [option udp-ttl]
+               short_opt = t
+               summary = set time to live value
+               arg_info = required_arg
+               arg_type = int32
+               typestr = num
+               default_val = -1
+               [help]
+                       This option applies exclusively to multicast UDPv4/v6 streaming.
+
+                       For the sending UDPv4 socket it sets the multicast Time-To-Live value
+                       to "num".  Traditional TTL scope values are: 0=host, 1=network, 32=same
+                       site, 64=same region, 128=same continent, 255=unrestricted. Please
+                       note however that this scoping is not a good solution: RFC 2365
+                       e.g. presents a better alternative.
+
+                       When using UDPv6 multicasting, the option sets the number of multicast
+                       hops (as described in RFC 3493); a value of -1 allows the kernel to
+                       auto-select the hop value.
+               [/help]
diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4
new file mode 100644 (file)
index 0000000..5ac7f16
--- /dev/null
@@ -0,0 +1,682 @@
+[suite server_cmd]
+caption = list of server commands
+aux_info_prefix = Permissions:
+
+[introduction]
+       The server process listens on a network socket and accepts connections
+       from para_client or para_audiod. For the connection to succeed the
+       connecting peer must authenticate as one of the users stored in the
+       user table of para_server. Each entry of the user table contains the
+       set of permission bits that are granted to the user. Authenticated
+       users may execute one of the commands below if the set of permission
+       bits of the command is a subset of the permission bits that are
+       granted to the user.
+[/introduction]
+
+[subcommand add]
+       purpose = add or update audio files
+       non-opts-name = path...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Each path must be absolute and refer to either an audio file or a
+               directory. In case of a directory, all audio files in that directory
+               are added recursively. Note that the given paths refer to files or
+               directories on the host on which para_server is running.
+       [/description]
+       [option all]
+               short_opt = a
+               summary = add all files
+               [help]
+                       The default is to add only files ending in a known suffix for a
+                       supported audio format.
+               [/help]
+       [option lazy]
+               short_opt = l
+               summary = add files lazily
+               [help]
+                       If the path already exists in the database, skip this file. This
+                       operation is really cheap. Useful to update large directories after
+                       some files have been added.
+               [/help]
+       [option force]
+               short_opt = f
+               summary = force adding/updating
+               [help]
+                       Recompute the audio format handler data even if a file with the same
+                       path and the same hash value exists.
+               [/help]
+       [option verbose]
+               short_opt = v
+               summary = enable verbose mode
+               [help]
+                       Print what is being done.
+               [/help]
+
+[subcommand addatt]
+       purpose = add new attribute(s)
+       non-opts-name = attribute...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               This adds new attributes to the attribute table. At most 64 attributes
+               may be defined.
+       [/description]
+
+[subcommand check]
+       purpose = run integrity checks on database tables
+       aux_info = AFS_READ
+       [description]
+               If no options are given, all checks are run.
+       [/description]
+       [option aft]
+               short_opt = a
+               summary = run audio file table checks
+               [help]
+                       Report stale paths and invalid image and lyrics ids of the audio
+                       file table.
+               [/help]
+       [option attribute]
+               short_opt = A
+               summary = check for invalid attributes
+               [help]
+                       Report audio files whose attribute bitmask is invalid, i.e., has a bit
+                       set which does not correspond to any attribute of the attribute table.
+               [/help]
+       [option mood]
+               short_opt = m
+               summary = check for invalid mood definitions
+               [help]
+                       Run syntax checks on all moods of the mood table.
+               [/help]
+       [option playlist]
+               short_opt = p
+               summary = find invalid paths in playlists
+               [help]
+                       Check all playlists for paths not contained in the audio file table.
+               [/help]
+
+[subcommand cpsi]
+       purpose = copy selected parts of the audio file selector info
+       non-opts-name = source pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               If no option, or only --verbose is given, all fields of the audio
+               file selector info structure are copied to each row of the audio file
+               table whose path matches at least one of the given patterns. Otherwise,
+               only those fields which correspond to the given options are copied.
+       [/description]
+       [option attribute-bitmap]
+               short_opt = a
+               summary = copy the attribute bitmap
+       [option image-id]
+               short_opt = i
+               summary = copy the image id
+       [option lyrics-id]
+               short_opt = y
+               summary = copy the lyrics id
+       [option lastplayed]
+               short_opt = l
+               summary = copy the lastplayed timestamp
+       [option numplayed]
+               short_opt = n
+               summary = copy the numplayed counter
+       [option verbose]
+               short_opt = v
+               summary = enable verbose mode
+
+[subcommand ff]
+       purpose = jump N seconds forward or backward
+       synopsis = n[-]
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               This sets the 'R' (reposition request) bit of the vss status flags
+               which enqueues a request to jump n seconds forwards or backwards.
+
+               Example:
+
+                    para_client ff 30-
+
+               jumps 30 seconds backwards.
+
+       [/description]
+
+[subcommand help]
+       purpose = list available commands or print command-specific help
+       non-opts-name = [command]
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               Without any arguments, help prints a list of available commands. When
+               called with a command name as first argument, it prints the description
+               of this command.
+       [/description]
+
+[subcommand hup]
+       purpose = reload config file, log file and user list
+       aux_info = VSS_WRITE
+       [description]
+               Reread the config file and the user list file, close and reopen the log
+               file, and ask the afs process to do the same. Sending the HUP signal
+               to the server process has the same effect as running this command.
+       [/description]
+
+[subcommand init]
+       purpose = initialize the database tables for the audio file selector
+       synopsis = [table_name...]
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               When invoked without arguments, this command creates all
+               tables: audio_files, attributes, scores, moods, lyrics, images,
+               playlists. Otherwise only the given tables are created.
+       [/description]
+
+[subcommand jmp]
+       purpose = reposition the current stream
+       non-opts-name = n
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'R' (reposition request) bit of the vss status flags and
+               enqueue a request to jump to n% of the current audio file, where 0 <=
+               n <= 100.
+       [/description]
+
+[subcommand ls]
+       purpose = list audio files which match a pattern
+       non-opts-name = [pattern...]
+       aux_info = AFS_READ
+       [description]
+               If no pattern is given, all files are listed.  Otherwise, the command
+               lists all files of the audio file table whose path matches at least
+               one of the given patterns.
+       [/description]
+       [option listing-mode]
+               short_opt = l
+               summary = use alternative output format
+               arg_type = string
+               arg_info = optional_arg
+               typestr = mode
+               default_val = long
+               [help]
+                       The optional mode argument is either a single character or a word
+                       according to the following list.
+
+                       short (s). List only the path or basename (last component of the path),
+                       depending on whether -p is also given. This listing mode acts as if
+                       --listing-mode had not been given.
+
+                       long (l). Show detailed information. This is the default if no argument
+                       to --listing-mode is supplied.
+
+                       verbose (v). Multi-line output, one row per data field stored in the
+                       audio file table.
+
+                       parser-friendly (p). Like verbose listing mode, but use numerical
+                       values for the names of the output fields and prefix each line with
+                       a length field.
+
+                       mbox (m). Generate output suitable to be viewed with a mail
+                       program. One "mail" per matching audio file.
+
+                       chunk-table (c). Print path (or basename, depending on whether -p is
+                       also given), chunk time and chunk offsets.
+
+               [/help]
+       [option full-path]
+               short_opt = F
+               summary = list full paths, match full paths against patterns
+               [help]
+                       This option is the default, so it does nothing. Deprecated as of
+                       v0.6.0, scheduled for removal in v0.6.1.
+               [/help]
+       [option basename]
+               short_opt = b
+               summary = list and match basenames only
+               [help]
+                       Print only the basename of each matching file and match only the
+                       basenames of the paths stored in the audio file table against the
+                       given patterns. The default is to print and match the full path.
+               [/help]
+       [option admissible]
+               short_opt = a
+               summary = list only admissible files
+               [help]
+                       List only files which are admissible with respect to the current mood
+                       or playlist.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+       [option unix-date]
+               short_opt = d
+               summary = print dates as seconds after the epoch
+       [option sort]
+               short_opt = s
+               summary = change sort order
+               arg_type = string
+               arg_info = required_arg
+               typestr = order
+               default_val = path
+               [help]
+                       The sort order must be given as an required argument. Like for
+                       --listing-mode, this argument may either be a single character or a
+                       word, according to the following list.
+
+                       path (p). Sort alphabetically by path or basename, depending on
+                       whether -b is given. This is the default if --sort is not given.
+
+                       score (s). Iterate over the entries of the score table, rather than
+                       the audio file table. This sort order implies --admissible, since
+                       the score table contains only admissible files.
+
+                       lastplayed (l)
+
+                       numplayed (n)
+
+                       frequency (f)
+
+                       channels (c)
+
+                       image-id (i)
+
+                       lyrics-id (y)
+
+                       bitrate (b)
+
+                       duration (d)
+
+                       audio-format (a)
+
+                       hash (h)
+
+                       If --sort is not given, path sort is implied.
+               [/help]
+
+[subcommand lsatt]
+       purpose = list attributes
+       aux_info = AFS_READ
+       [description]
+               Print the list of all defined attributes which match the given
+               pattern. If no pattern is given, the full list is printed.
+       [/description]
+
+       [option id-sort]
+               short_opt = i
+               summary = sort attributes by id
+               [help]
+                       The default is to sort alphabetically by name.
+
+                       Attributes are internally represented as an 64 bit array. The attribute
+                       id is the bit number in this array.
+               [/help]
+       [option long]
+               short_opt = l
+               summary = print long listing
+               [help]
+                       The long listing prints the attribute id in addition to the name of
+                       the attribute. The id is printed as a decimal number and is separated
+                       from the name by a tab character.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+
+[subcommand mvatt]
+       purpose = rename an attribute
+       synopsis = source dest
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Rename the attribute given by the first argument to the destination
+               given by the second argument. It is an error if the destination
+               attribute exists.
+       [/description]
+
+[subcommand next]
+       purpose = close the stream and start to stream the next audio file
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'N' (next audio file) bit of the vss status flags. This
+               instructs the server to close the current stream, if any. The 'P'
+               (playing) bit is not modified by this command. If it is on, playing
+               continues with the next audio file.
+
+               This command is equivalent to stop if paused, and has no effect
+               if stopped.
+       [/description]
+
+[subcommand nomore]
+       purpose = stop playing after current audio file
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'O' (no more) bit of the vss status flags which asks
+               para_server to clear the 'P' (playing) bit after the 'N' (next audio
+               file) bit transitions from off to on (because the end of the current
+               audio file is reached). Use this command instead of stop if you don't
+               like sudden endings.
+       [/description]
+
+[subcommand pause]
+       purpose = suspend the current stream
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Clear the 'P' (playing) bit of the vss status flags.
+       [/description]
+
+[subcommand play]
+       purpose = start or resume playback
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'P' (playing) bit of the vss status flags.
+       [/description]
+
+[subcommand rm]
+       purpose = remove rows from the audio file table
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all rows of the audio file table which match any of the given
+               patterns. Note that this affects only the database table; the command
+               won't touch your audio files on disk.
+       [/description]
+       [option verbose]
+               short_opt = v
+               summary = print paths of deleted rows
+       [option force]
+               short_opt = f
+               summary = don't complain if nothing was removed
+       [option pathname-match]
+               short_opt = p
+               summary = modify matching behaviour
+               [help]
+                       Match a slash in the path only with a slash in pattern and not by an
+                       asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+                       expression ([]) containing a slash (see fnmatch(3)).
+               [/help]
+
+[subcommand rmatt]
+       purpose = remove attribute(s)
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all attributes which match any given pattern. All information
+               about the removed attributes in the audio file table is lost.
+       [/description]
+
+[subcommand select]
+       purpose = activate a mood or a playlist
+       non-opts-name = specifier/name
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               The specifier is either 'm' or 'p' to indicate whether a playlist or
+               a mood should be activated. Example:
+
+                       select m/foo
+
+               activates the mood named 'foo'.
+       [/description]
+
+[subcommand sender]
+       purpose = control paraslash senders
+       synopsis = [sender cmd [arguments]]
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Send a command to a specific sender. The following commands are
+               available, but not all senders support every command.
+
+                      help, on, off, add, delete, allow, deny, status.
+
+               The help command prints the help text of the given sender. If no
+               command is given the list of available senders is shown.
+
+               Example:
+
+                       para_client sender http help
+
+       [/description]
+
+[subcommand setatt]
+       purpose = set or unset attributes
+       synopsis = attribute{+|-}... pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Set ('+') or unset ('-') the given attributes for all audio files
+               matching the given pattern. Example:
+
+                       setatt rock+ punk+ pop- '*foo.mp3'
+
+               sets the 'rock' and the 'punk' attribute and unsets the 'pop' attribute
+               of all files ending with 'foo.mp3'.
+       [/description]
+
+[subcommand si]
+       purpose = print server info
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               Show server and afs PID, number of connections, uptime and more.
+       [/description]
+
+[subcommand stat]
+       purpose = print information about the current audio file
+       aux_info = VSS_READ
+       [option num]
+               short_opt = n
+               summary = number of times to show the status info
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = num
+               [help]
+                       Exit after the status information has been shown num times. If this
+                       option is not given, the command runs in an endless loop.
+               [/help]
+       [option parser-friendly]
+               short_opt = p
+               summary = enable parser-friendly output
+               [help]
+                       Show status item identifiers as numerical values and prefix each
+                       status item with its size in bytes.
+               [/help]
+
+[subcommand stop]
+       purpose = stop playback
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of
+               the vss status flags, effectively stopping playback.
+       [/description]
+
+[subcommand tasks]
+       purpose = list active server tasks
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               For each task, print ID, status and name. This is mostly useful
+               for debugging.
+       [/description]
+
+[subcommand term]
+       purpose = ask the server to terminate
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Shut down the server. Instead of this command, you can also send
+               SIGINT or SIGTERM to the para_server process. It should never be
+               necessary to send SIGKILL.
+       [/description]
+
+[subcommand touch]
+       purpose = manipulate the afs information of audio files
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               This command modifies the afs info structure of all rows of the audio
+               file table whose path matches at least one of the given patters.
+
+               If at least one option is given which takes a number as its argument,
+               only those fields of the afs info structure are updated which
+               correspond to the given options while all other fields stay unmodified.
+
+               If no such option is given, the lastplayed field is set to the current
+               time and the value of the numplayed field is increased by one while
+               all other fields are left unchanged. This mimics what happens when
+               the virtual streaming system selects the file for streaming.
+
+               If the file is admissible for the current mood (or contained in the
+               current playlist), its score is recomputed according to the changed
+               values.
+       [/description]
+       [option numplayed]
+               short_opt = n
+               summary = set the numplayed count manually
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The numplayed count of an audio file is the number of times the file
+                       was selected for streaming. It is one of the inputs to the scoring
+                       function which determines the order in which admissible files are
+                       streamed.
+
+                       The virtual streaming system increases this number automatically each
+                       time it opens the file for streaming.
+               [/help]
+       [option lastplayed]
+               short_opt = l
+               summary = set the lastplayed time manually
+               arg_type = uint64
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The lastplayed time of an audio file is the time when the file was
+                       last opened for streaming.
+
+                       Like the numplayed count, it is an input for the scoring function
+                       and is updated automatically by the virtual streaming system.
+
+                       The argument must be a number of seconds since the epoch. Example:
+
+                               touch -l=$(date +%s) file
+
+                       sets the lastplayed time of 'file' to the current time.
+               [/help]
+       [option image-id]
+               short_opt = i
+               summary = set the image id
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The afs info structure of each row of the audio file table contains
+                       a slot for the image id of the audio file that corresponds to the
+                       row. The image id stored in this slot refers to the key in the image
+                       table that identifies the blob.
+
+                       When a new audio file is added to the audio file table, its image
+                       id starts out as zero, indicating that there is no image associated
+                       with the file. Setting the image id to a non-zero number associates
+                       the file with a particular blob of the image table, for example the
+                       cover art of the album in jpg format.
+               [/help]
+       [option lyrics-id]
+               short_opt = y
+               summary = set the lyrics id
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       This option works just like --image-id, but sets the lyrics ID rather
+                       than the image id.
+               [/help]
+       [option amp]
+               short_opt = a
+               summary = set the amplification value (0-255)
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The amplification value of an audio file is a number which is stored
+                       in the afs info structure.
+
+                       The value determines the scaling factor by which the amplitude of
+                       the decoded samples should be multiplied in order to normalize the
+                       volume. A value of zero means no amplification, 64 means the amplitude
+                       should be multiplied by a factor of two, 128 by three and so on.
+
+                       The amp filter of para_audiod amplifies the volume according to
+                       this value.
+               [/help]
+       [option verbose]
+               short_opt = v
+               summary = explain what is being done
+       [option pathname-match]
+               short_opt = p
+               summary = modify matching behaviour
+               [help]
+                       Match a slash in the path only with a slash in pattern and not by an
+                       asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+                       expression ([]) containing a slash (see fnmatch(3)).
+               [/help]
+
+[subcommand version]
+       purpose = print the git version string of para_server
+       aux_info = NO_PERMISSION_REQUIRED
+       [option verbose]
+               short_opt = v
+               summary = print detailed (multi-line) version text
+
+m4_define(`BLOB_COMMANDS', `
+[subcommand rm`$2']
+       purpose = remove `$1' blob(s)
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all `$1' blobs which match any of the given patterns.
+       [/description]
+
+[subcommand mv`$2']
+       purpose = rename `$1' blob(s)
+       non-opts-name = source dest
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Rename `$1' source to dest. The command fails if the source `$1'
+               does not exist or if the destination `$1' already exists.
+       [/description]
+
+[subcommand add`$2']
+       purpose = add a blob to the `$1' table
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Read from stdin and ask the audio file selector to create a blob in
+               the `$1' table. If the named blob already exists, it gets replaced
+               with the new data.
+       [/description]
+
+[subcommand cat`$2']
+       purpose = dump a `$1' blob to stdout
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ
+
+[subcommand ls`$2']
+       purpose = list blobs of type `$1' which match a pattern
+       non-opts-name = [pattern...]
+       aux_info = AFS_READ
+       [description]
+               Print the list of all blobs which match the given pattern. If no
+               pattern is given, the full list is printed.
+       [/description]
+       [option id-sort]
+               short_opt = i
+               summary = sort by identifier
+               [help]
+                       The default is to sort alphabetically by name.
+               [/help]
+       [option long]
+               short_opt = l
+               summary = long listing
+               [help]
+                       Print identifier and name. The default is to print only the name.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+')
+
+BLOB_COMMANDS(`moods', `mood')
+BLOB_COMMANDS(`playlist', `pl')
+BLOB_COMMANDS(`image', `img')
+BLOB_COMMANDS(`lyrics', `lyr')
diff --git a/m4/lls/write.suite.m4 b/m4/lls/write.suite.m4
new file mode 100644 (file)
index 0000000..a2f50df
--- /dev/null
@@ -0,0 +1,35 @@
+m4_define(PROGRAM, para_write)
+[suite write]
+version-string = GIT_VERSION()
+[supercommand para_write]
+       purpose = play wav or raw audio
+       [description]
+               para_write reads audio data from stdin and starts one supported writer.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option writer]
+               short_opt = w
+               summary = select writer to start
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = 'name [options]'
+               [help]
+                       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.
+
+                       Options for a particular writer may be specified for each given
+                       '--writer' option separately. You will have to quote these options
+                       like this:
+
+                               --writer 'oss --device /dev/dsp2'
+               [/help]
+       m4_include(channels.m4)
+       m4_include(sample-rate.m4)
+       m4_include(sample-format.m4)
diff --git a/m4/lls/write_cmd.suite.m4 b/m4/lls/write_cmd.suite.m4
new file mode 100644 (file)
index 0000000..9d8ee75
--- /dev/null
@@ -0,0 +1,87 @@
+[suite write_cmd]
+caption = writers
+[subcommand alsa]
+       purpose = native ALSA output plugin
+       [option device]
+               short_opt = d
+               summary = set PCM device
+               typestr = device
+               arg_info = required_arg
+               arg_type = string
+               default_val = default
+               [help]
+                       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.
+               [/help]
+       [option buffer-time]
+               short_opt = B
+               summary = duration of the ALSA buffer
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 170
+               [help]
+                       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.
+               [/help]
+[subcommand ao]
+       purpose = output plugin for libao
+       [option driver]
+               short_opt = d
+               summary = select a output driver by name
+               typestr = name
+               arg_info = required_arg
+               arg_type = string
+               [help]
+                       If this is not given, the driver with the highest priority (see below)
+                       will be used.
+               [/help]
+       [option ao-option]
+               short_opt = o
+               summary = pass a key-value pair to the libao driver
+               typestr = key:value
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               [help]
+                       For each time this option is given, the supplied key-value pair is
+                       appended to the list of options for the driver. Invalid keys are
+                       silently ignored.
+               [/help]
+[subcommand oss]
+       purpose = output plugin for the Open Sound System
+       [option device]
+               short_opt = d
+               summary = set PCM device
+               typestr = path
+               arg_info = required_arg
+               arg_type = string
+               default_val = /dev/dsp
+[subcommand osx]
+       purpose = output plugin for Mac OS coreaudio
+       [option numbuffers]
+               short_opt = n
+               summary = number of audio buffers to allocate
+               typestr = num
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 20
+               [help]
+                       Increase if you get buffer underruns.
+               [/help]
+[subcommand file]
+       purpose = output plugin that writes to a local file
+       [option filename]
+               short_opt = f
+               summary = specify output file name
+               typestr = path
+               arg_info = required_arg
+               arg_type = string
+               [help]
+                       Defaults to a random filename in ~/.paraslash.
+               [/help]
diff --git a/man_util.bash b/man_util.bash
deleted file mode 100755 (executable)
index cb7519c..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/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 60c4392fabab76bb58ad1c2ee0ee211d52469ad7..5d68b2bb0b34df798e38efad7b0ceeff687982fc 100644 (file)
--- a/mix.h
+++ b/mix.h
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file mix.h Mixer API (used by para_fade). */
+/** \file mix.h Mixer API for para_mixer. */
 
 /**
  * Opaque structure which corresponds to an instance of a mixer.
@@ -16,10 +16,14 @@ struct mixer_handle;
 
 /**
  * Operations provided by each mixer plugin.
+ *
+ * Each mixer plugin must define a non-static instance of this structure, with
+ * all pointers initialized to non-NULL values. No other symbols need to be
+ * exported.
  */
 struct mixer {
-       /** Called on startup, must fill in all other members. */
-       void (*init)(struct mixer *self);
+       /** Used to identify the mixer. */
+       const char * const name;
        /** Return a handle that can be passed to other methods. */
        int (*open)(const char *dev, struct mixer_handle **handle);
        /** Returns a string of all valid mixer channels. */
@@ -34,3 +38,6 @@ struct mixer {
        /** Free all resources associated with the given handle. */
        void (*close)(struct mixer_handle **handle);
 };
+
+/** Declared even if unsupported because it does not hurt and avoids ifdefs. */
+extern const struct mixer alsa_mixer, oss_mixer;
diff --git a/mixer.c b/mixer.c
new file mode 100644 (file)
index 0000000..0c08e2f
--- /dev/null
+++ b/mixer.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mixer.c A volume fader and alarm clock for OSS. */
+
+#include <regex.h>
+#include <lopsub.h>
+#include <math.h>
+
+#include "mixer.lsg.h"
+#include "para.h"
+#include "fd.h"
+#include "string.h"
+#include "mix.h"
+#include "error.h"
+#include "version.h"
+
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+/* At least one of the two is defined if this file gets compiled. */
+static const struct mixer *mixers[] = {
+#ifdef HAVE_ALSA
+       &alsa_mixer,
+#endif
+#ifdef HAVE_OSS
+       &oss_mixer,
+#endif
+};
+
+#define NUM_SUPPORTED_MIXERS (ARRAY_SIZE(mixers))
+#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
+
+static struct lls_parse_result *lpr, *sub_lpr;
+
+#define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite))
+#define OPT_RESULT(_cmd, _opt) (lls_opt_result( \
+       LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr))
+#define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt)))
+#define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
+#define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
+
+typedef int (*mixer_subcommand_handler_t)(const struct mixer *, struct mixer_handle *);
+
+#define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
+       lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
+
+static int loglevel;
+static __printf_2_3 void date_log(int ll, const char *fmt, ...)
+{
+       va_list argp;
+       time_t t1;
+       struct tm *tm;
+
+       if (ll < loglevel)
+               return;
+       time(&t1);
+       tm = localtime(&t1);
+       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(const 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);
+}
+
+static void millisleep(int ms)
+{
+       struct timespec ts;
+
+       PARA_INFO_LOG("sleeping %dms\n", ms);
+       if (ms < 0)
+               return;
+       ts.tv_sec = ms / 1000,
+       ts.tv_nsec = (ms % 1000) * 1000 * 1000;
+       nanosleep(&ts, NULL);
+}
+
+/*
+ * This implements the inverse function of t -> t^alpha, scaled to the time
+ * interval [0,T] and the range given by old_vol and new_vol. It returns the
+ * amount of milliseconds until the given volume is reached.
+ */
+static unsigned volume_time(double vol, double old_vol, double new_vol,
+               double T, double alpha)
+{
+       double c, d, x;
+
+       if (old_vol < new_vol) {
+               c = old_vol;
+               d = new_vol;
+       } else {
+               c = new_vol;
+               d = old_vol;
+       }
+
+       x = T * exp(log(((vol - c) / (d - c))) / alpha);
+       assert(x <= T);
+       if (old_vol < new_vol)
+               return x;
+       else
+               return T - x;
+}
+
+/* Fade to new volume in fade_time seconds. */
+static int fade(const struct mixer *m, struct mixer_handle *h, uint32_t new_vol,
+               uint32_t fade_time)
+{
+       int i, T, old_vol, ret, slept, incr;
+       double ms, alpha;
+       uint32_t fe = OPT_UINT32_VAL(PARA_MIXER, FADE_EXPONENT);
+
+       if (fade_time <= 0 || fe >= 100) {
+               ret = m->set(h, new_vol);
+               if (ret < 0)
+                       return ret;
+               goto sleep;
+       }
+       alpha = (100 - fe) / 100.0;
+       ret = m->get(h);
+       if (ret < 0)
+               return ret;
+       old_vol = ret;
+       if (old_vol == new_vol)
+               goto sleep;
+       PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
+               OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), old_vol,
+               new_vol, fade_time);
+       incr = old_vol < new_vol? 1 : -1;
+       T = fade_time * 1000;
+       i = old_vol;
+       slept = 0;
+       do {
+               ms = volume_time(i + incr, old_vol, new_vol, T, alpha);
+               millisleep(ms - slept);
+               slept = ms;
+               i += incr;
+               ret = m->set(h, i);
+               if (ret < 0)
+                       return ret;
+       } while (i != new_vol);
+       return 1;
+sleep:
+       sleep(fade_time);
+       return ret;
+}
+
+static int com_fade(const struct mixer *m, struct mixer_handle *h)
+{
+       uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL);
+       uint32_t fade_time = OPT_UINT32_VAL(FADE, FADE_TIME);
+       return fade(m, h, new_vol, fade_time);
+}
+EXPORT_CMD(fade);
+
+static void client_cmd(const char *cmd)
+{
+       int ret, status, fds[3] = {0, 0, 0};
+       pid_t pid;
+       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+
+       PARA_NOTICE_LOG("%s\n", cmdline);
+       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
+       free(cmdline);
+       if (ret < 0) {
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               goto fail;
+       }
+       do
+               pid = waitpid(pid, &status, 0);
+       while (pid == -1 && errno == EINTR);
+       if (pid < 0) {
+               PARA_ERROR_LOG("%s\n", strerror(errno));
+               goto fail;
+       }
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               goto fail;
+       return;
+fail:
+       PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
+       exit(EXIT_FAILURE);
+}
+
+static void change_afs_mode(const char *afs_mode)
+{
+       char *cmd;
+
+       client_cmd("stop");
+       if (!afs_mode)
+               return;
+       cmd = make_message("select %s", afs_mode);
+       client_cmd(cmd);
+       free(cmd);
+}
+
+static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
+{
+       int i, ret;
+
+       for (i = 0; i < OPT_GIVEN(SLEEP, IVOL); i++) {
+               const char *val = lls_string_val(i, OPT_RESULT(SLEEP, IVOL));
+               char *p, *ch, *arg = para_strdup(val);
+               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 com_sleep(const struct mixer *m, struct mixer_handle *h)
+{
+       time_t t1, wake_time_epoch;
+       unsigned int delay;
+       struct tm *tm;
+       int ret;
+       const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+       const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
+       const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
+       const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
+       int fit = OPT_UINT32_VAL(SLEEP, FI_TIME);
+       int fot = OPT_UINT32_VAL(SLEEP, FO_TIME);
+       int fiv = OPT_UINT32_VAL(SLEEP, FI_VOL);
+       int fov = OPT_UINT32_VAL(SLEEP, FO_VOL);
+       int32_t hour, min = 0;
+       char *tmp;
+       char *wt = para_strdup(wake_time + (wake_time[0] == '+'));
+
+       /* calculate wake time */
+       time(&t1);
+       tmp = strchr(wt, ':');
+       if (tmp) {
+               *tmp = '\0';
+               tmp++;
+               ret = para_atoi32(tmp, &min);
+               if (ret < 0) {
+                       free(wt);
+                       return ret;
+               }
+       }
+       ret = para_atoi32(wt, &hour);
+       free(wt);
+       if (ret < 0)
+               return ret;
+       if (wake_time[0] == '+') { /* relative */
+               t1 += hour * 60 * 60 + min * 60;
+               tm = localtime(&t1);
+       } else {
+               tm = localtime(&t1);
+               if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
+                       t1 += 86400; /* wake time is tomorrow */
+                       tm = localtime(&t1);
+               }
+               tm->tm_hour = hour;
+               tm->tm_min = min;
+               tm->tm_sec = 0;
+       }
+       wake_time_epoch = mktime(tm);
+       PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
+       client_cmd("stop");
+       sleep(1);
+       if (fot) {
+               ret = set_initial_volume(m, h);
+               if (ret < 0)
+                       return ret;
+               change_afs_mode(fo_mood);
+               client_cmd("play");
+               ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+               if (ret < 0)
+                       return ret;
+               ret = fade(m, h, fov, fot);
+               if (ret < 0)
+                       return ret;
+       } else {
+               ret = m->set(h, fov);
+               if (ret < 0)
+                       return ret;
+       }
+       if (OPT_GIVEN(SLEEP, SLEEP_MOOD)) {
+               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)
+                       break;
+               delay = wake_time_epoch - t1 - fit;
+               PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
+                       delay, delay / 3600,
+                       (delay % 3600) / 60);
+               sleep(delay);
+       }
+       client_cmd("play");
+       ret = fade(m, h, fiv, fit);
+       PARA_INFO_LOG("fade complete, returning\n");
+       return ret;
+}
+EXPORT_CMD(sleep);
+
+static int com_snooze(const struct mixer *m, struct mixer_handle *h)
+{
+       int ret, val;
+
+       if (OPT_UINT32_VAL(SNOOZE, SO_TIME) == 0)
+               return 1;
+       ret = m->get(h);
+       if (ret < 0)
+               return ret;
+       val = ret;
+       if (val < OPT_UINT32_VAL(SNOOZE, SO_VOL))
+               ret = m->set(h, OPT_UINT32_VAL(SNOOZE, SO_VOL));
+       else
+               ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SO_VOL),
+                       OPT_UINT32_VAL(SNOOZE, SO_TIME));
+       if (ret < 0)
+               return ret;
+       client_cmd("pause");
+       PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n",
+               OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+       sleep(OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+       client_cmd("play");
+       return fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL),
+               OPT_UINT32_VAL(SNOOZE, SI_TIME));
+}
+EXPORT_CMD(snooze);
+
+static int com_set(const struct mixer *m, struct mixer_handle *h)
+{
+       return m->set(h, OPT_UINT32_VAL(SET, VAL));
+}
+EXPORT_CMD(set);
+
+static const struct mixer *get_mixer_or_die(void)
+{
+       int i;
+
+       if (!OPT_GIVEN(PARA_MIXER, MIXER_API))
+               i = 0; /* default: use first mixer */
+       else
+               FOR_EACH_MIXER(i)
+                       if (!strcmp(mixers[i]->name,
+                                       OPT_STRING_VAL(PARA_MIXER, MIXER_API)))
+                               break;
+       if (i < NUM_SUPPORTED_MIXERS) {
+               PARA_NOTICE_LOG("using %s mixer API\n", mixers[i]->name);
+               return mixers[i];
+       }
+       printf("available mixer APIs: ");
+       FOR_EACH_MIXER(i)
+               printf("%s%s%s ", i == 0? "[" : "", mixers[i]->name,
+                       i == 0? "]" : "");
+       printf("\n");
+       exit(EXIT_FAILURE);
+}
+
+static void show_subcommands(void)
+{
+       const struct lls_command *cmd;
+       int i;
+       printf("Subcommands:\n");
+       for (i = 1; (cmd = lls_cmd(i, mixer_suite)); i++) {
+               const char *name = lls_command_name(cmd);
+               const char *purpose = lls_purpose(cmd);
+               printf("%-20s%s\n", name, purpose);
+       }
+}
+
+
+static int com_help(__a_unused const struct mixer *m,
+               __a_unused struct mixer_handle *h)
+{
+       const struct lls_command *cmd;
+       const struct lls_opt_result *r_l = OPT_RESULT(HELP, LONG);
+       char *txt, *errctx;
+       const char *name;
+       int ret;
+
+       ret = lls_check_arg_count(sub_lpr, 0, 1, NULL);
+       if (ret < 0)
+               return ret;
+       if (lls_num_inputs(sub_lpr) == 0) {
+               show_subcommands();
+               return 0;
+       }
+       name = lls_input(0, sub_lpr);
+       ret = lls(lls_lookup_subcmd(name, mixer_suite, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       cmd = lls_cmd(ret, mixer_suite);
+       if (lls_opt_given(r_l))
+               txt = lls_long_help(cmd);
+       else
+               txt = lls_short_help(cmd);
+       printf("%s", txt);
+       free(txt);
+       return 0;
+}
+EXPORT_CMD(help);
+
+static void handle_help_flags(void)
+{
+       char *help;
+
+       if (OPT_GIVEN(PARA_MIXER, DETAILED_HELP))
+               help = lls_long_help(CMD_PTR(PARA_MIXER));
+       else if (OPT_GIVEN(PARA_MIXER, HELP))
+               help = lls_short_help(CMD_PTR(PARA_MIXER));
+       else
+               return;
+       printf("%s", help);
+       free(help);
+       show_subcommands();
+       exit(EXIT_SUCCESS);
+}
+
+static int parse_and_merge_config_file(const struct lls_command *cmd)
+{
+       int ret;
+       int cf_argc;
+       char **cf_argv;
+       char *cf, *errctx = NULL;
+       struct lls_parse_result **lprp, *cf_lpr, *merged_lpr;
+       void *map;
+       size_t sz;
+       const char *subcmd_name;
+
+       if (cmd == lls_cmd(0, mixer_suite)) {
+               lprp = &lpr;
+               subcmd_name = NULL;
+       } else {
+               lprp = &sub_lpr;
+               subcmd_name = lls_command_name(cmd);
+       }
+       if (OPT_GIVEN(PARA_MIXER, CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(PARA_MIXER, CONFIG_FILE));
+       else {
+               char *home = para_homedir();
+               cf = make_message("%s/.paraslash/mixer.conf", home);
+               free(home);
+       }
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) &&
+                               OPT_GIVEN(PARA_MIXER, CONFIG_FILE))
+                       goto free_cf;
+       } else {
+               ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx));
+               para_munmap(map, sz);
+               if (ret < 0)
+                       goto free_cf;
+               cf_argc = ret;
+               ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+               lls_free_argv(cf_argv);
+               if (ret < 0)
+                       goto free_cf;
+               ret = lls(lls_merge(*lprp, cf_lpr, cmd, &merged_lpr, &errctx));
+               lls_free_parse_result(cf_lpr, cmd);
+               if (ret < 0)
+                       goto free_cf;
+               lls_free_parse_result(*lprp, cmd);
+               *lprp = merged_lpr;
+               loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+       }
+       ret = 1;
+free_cf:
+       free(cf);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       return ret;
+}
+
+/**
+ * The main function of para_mixer.
+ *
+ * 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[])
+{
+       const struct lls_command *cmd = CMD_PTR(PARA_MIXER);
+       int ret;
+       char *errctx;
+       const char *subcmd;
+       const struct mixer *m;
+       struct mixer_handle *h;
+       unsigned n;
+
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+       version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER, VERSION));
+       handle_help_flags();
+
+       n = lls_num_inputs(lpr);
+       if (n == 0) {
+               show_subcommands();
+               ret = 0;
+               goto free_lpr;
+       }
+       ret = parse_and_merge_config_file(cmd);
+       if (ret < 0)
+               goto free_lpr;
+       subcmd = lls_input(0, lpr);
+       ret = lls(lls_lookup_subcmd(subcmd, mixer_suite, &errctx));
+       if (ret < 0)
+               goto fail;
+       cmd = lls_cmd(ret, mixer_suite);
+       ret = lls(lls_parse(n, argv + argc - n, cmd, &sub_lpr, &errctx));
+       if (ret < 0)
+               goto free_lpr;
+       ret = parse_and_merge_config_file(cmd);
+       if (ret < 0)
+               goto free_lpr;
+       m = get_mixer_or_die();
+       ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), &h);
+       if (ret < 0)
+               goto free_sub_lpr;
+       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret == -E_BAD_CHANNEL) {
+               char *channels = m->get_channels(h);
+               printf("Available channels: %s\n", channels);
+               free(channels);
+       }
+       if (ret < 0)
+               goto close_mixer;
+       ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h);
+close_mixer:
+       m->close(&h);
+free_sub_lpr:
+       lls_free_parse_result(sub_lpr, cmd);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR(PARA_MIXER));
+       if (ret >= 0)
+               return EXIT_SUCCESS;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       return EXIT_FAILURE;
+}
diff --git a/mm.c b/mm.c
index 92856ec3ef903eb905b0379c6eaff605bf6f8d06..47e88864c8e4b7cf514341d60eb844bd844b392e 100644 (file)
--- a/mm.c
+++ b/mm.c
@@ -9,6 +9,7 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
diff --git a/mm.h b/mm.h
index 2caace7f84220c23119ccb7d1bd7140212086a6a..e6c0d125d88594d78a9ad7b3a639375fd883cb6f 100644 (file)
--- a/mm.h
+++ b/mm.h
@@ -17,7 +17,7 @@
  * Mood score functions must return values between -100 and +100 inclusively.
  * Boolean score functions should always return either -100 or +100.
  *
- * \sa struct mood_method, mood_parser.
+ * \sa struct \ref mood_method, \ref mood_parser.
  */
 typedef int mood_score_function(const char *path, const struct afs_info *afsi,
                const struct afh_info *afhi, const void *data);
@@ -36,19 +36,15 @@ typedef int mood_score_function(const char *path, const struct afs_info *afsi,
  * later in the mood_score_function of the mood_method. The mood_parser may
  * store a pointer to its structure via the void** pointer.
  *
- * \sa mood_open(), mood_cleanup_function, mood_score_function.
+ * \sa \ref mood_cleanup_function, \ref mood_score_function.
  */
 typedef int mood_parser(int, char **, void **);
 
 /**
  * Deallocate resources which were allocated by the mood_parser.
  *
- * This optional function of a mood_method is used to free any resources
- * allocated in mood_open() by the mood_parser. The argument passed is a
- * pointer to the mood_method specific data structure that was returned by the
- * mood_parser.
- *
- * \sa mood_parser.
+ * Function to free the resources allocated in \ref mood_method::parser. The
+ * argument is a pointer to mood method specific data returned by ->parser().
  */
 typedef void mood_cleanup_function(void *);
 
diff --git a/mood.c b/mood.c
index 79f47e5a3d20801832d5ad396bbd5b3b98aeed08..315ef0cbdc61b7de1a3bbbd8f8683d7ab81d9c81 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -8,6 +8,7 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
@@ -58,15 +59,13 @@ struct mood_item {
        struct list_head mood_item_node;
 };
 
-/**
- * Created from the mood definition by mood_open().
+/*
+ * Created from the mood definition by \ref change_current_mood().
  *
  * When a mood is opened, each line of its definition is investigated, and a
- * corresponding mood item is produced. Each mood line starts with \p accept,
- * \p deny, or \p score which determines the type of the mood line.  For each
- * such type a linked list is maintained whose entries are the mood items.
- *
- * \sa mood_item, mood_open().
+ * corresponding mood item is produced. Each mood line starts with accept,
+ * deny, or score which determines the type of the mood line. For each such
+ * type a linked list is maintained whose entries are the mood items.
  */
 struct mood {
        /** The name of this mood. */
@@ -81,7 +80,7 @@ struct mood {
 
 /*
  * If current_mood is NULL then no mood is currently open. If
- * current_mood->name is NULL, the dummy mood is currently open
+ * current_mood->name is NULL, the dummy mood is currently open.
  */
 static struct mood *current_mood;
 
@@ -141,8 +140,8 @@ static bool get_item_score(struct mood_item *item, const struct afs_info *afsi,
 }
 
 /* returns 1 if row admissible, 0 if not, negative on errors */
-static int compute_mood_score(const struct osl_row *aft_row, struct mood *m,
-               long *result)
+static int row_is_admissible(const struct osl_row *aft_row, struct mood *m,
+               long *scorep)
 {
        struct mood_item *item;
        int ret;
@@ -155,13 +154,13 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m,
        if (!m)
                return -E_NO_MOOD;
        ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret< 0)
+       if (ret < 0)
                return ret;
        ret = get_afhi_of_row(aft_row, &afhi);
-       if (ret< 0)
+       if (ret < 0)
                return ret;
        ret = get_audio_file_path_of_row(aft_row, &path);
-       if (ret< 0)
+       if (ret < 0)
                return ret;
        /* reject audio file if it matches any entry in the deny list */
        list_for_each_entry(item, &m->deny_list, mood_item_node) {
@@ -191,7 +190,7 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m,
        }
        if (score_arg_sum)
                score /= score_arg_sum;
-       *result = score;
+       *scorep = score;
        return 1;
 }
 
@@ -258,7 +257,6 @@ struct mood_line_parser_data {
  * <score> is either an integer or "random" which assigns a random score to
  * all matching files
  */
-
 static int parse_mood_line(char *mood_line, void *data)
 {
        struct mood_line_parser_data *mlpd = data;
@@ -523,9 +521,7 @@ static int del_afs_statistics(const struct osl_row *row)
        return 1;
 }
 
-/**
- * Structure used during mood_open().
- *
+/*
  * 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
@@ -536,8 +532,6 @@ static int del_afs_statistics(const struct osl_row *row)
  * 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.
  */
 struct admissible_file_info
 {
@@ -571,7 +565,7 @@ static int add_if_admissible(struct osl_row *aft_row, void *data)
        int ret;
        long score = 0;
 
-       ret = compute_mood_score(aft_row, aa->m, &score);
+       ret = row_is_admissible(aft_row, aa->m, &score);
        if (ret <= 0)
                return ret;
        if (statistics.num >= aa->size) {
@@ -630,7 +624,8 @@ _static_inline_ int64_t update_quadratic_deviation(int64_t n, int64_t old_qd,
        return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
 }
 
-static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new_afsi)
+static int update_afs_statistics(struct afs_info *old_afsi,
+               struct afs_info *new_afsi)
 {
        unsigned n;
        int ret = get_num_admissible_files(&n);
@@ -678,7 +673,7 @@ static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
  *
  * \return Positive on success, negative on errors.
  *
- * \sa score_delete().
+ * \sa \ref score_delete().
  */
 static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
@@ -717,7 +712,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        if (ret < 0)
                return ret;
        was_admissible = ret;
-       ret = compute_mood_score(aft_row, current_mood, &score);
+       ret = row_is_admissible(aft_row, current_mood, &score);
        if (ret < 0)
                return ret;
        is_admissible = (ret > 0);
@@ -781,8 +776,7 @@ static void log_statistics(void)
 /**
  * Close the current mood.
  *
- * Free all resources of the current mood which were allocated during
- * mood_open().
+ * Frees all resources of the current mood.
  */
 void close_current_mood(void)
 {
@@ -804,8 +798,7 @@ void close_current_mood(void)
  * \return Positive on success, negative on errors. Loading the dummy mood
  * always succeeds.
  *
- * \sa struct admissible_file_info, struct admissible_array, struct
- * afs_info::last_played, mood_close().
+ * \sa struct \ref afs_info::last_played.
  */
 int change_current_mood(const char *mood_name)
 {
@@ -853,18 +846,15 @@ out:
        free(aa.array);
        return ret;
 }
-/**
+
+/*
  * Close and re-open the current mood.
  *
- * This function is used if changes to the audio file table or the
- * attribute table were made that render the current list of admissible
- * files useless. For example, if an attribute is removed from the
- * attribute table, this function is called.
- *
- * \return Positive on success, negative on errors. If no mood is currently
- * open, the function returns success.
+ * This function is called on events which render the current list of
+ * admissible files useless, for example if an attribute is removed from the
+ * attribute table.
  *
- * \sa mood_open(), mood_close().
+ * If no mood is currently open, the function returns success.
  */
 static int reload_current_mood(void)
 {
index 2115f71c77ce745b356b917058792d0c64db42ef..08837bec597918ed73fe27febbb0a42fbadf654e 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -657,6 +657,7 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
        tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv);
        PARA_DEBUG_LOG("%" PRIu32 "chunks, each %lums\n", afhi->chunks_total,
                tv2ms(&afhi->chunk_tv));
+       set_max_chunk_size(afhi);
        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]);
@@ -689,7 +690,7 @@ static const char * const mp3_suffixes[] = {"mp3", NULL};
  *
  * \param afh pointer to the struct to initialize
  */
-void mp3_init(struct audio_format_handler *afh)
+void mp3_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = mp3_get_file_info;
        afh->suffixes = mp3_suffixes;
index 5f6935d1a5be70c7fa89dd0368bca48e34664422..b053aaa8f3fd33171aca682fe53dd85906bcca11 100644 (file)
@@ -8,12 +8,12 @@
 
 #include <mad.h>
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "mp3dec_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -171,25 +171,15 @@ err:
 static void mp3dec_open(struct filter_node *fn)
 {
        struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
-       struct mp3dec_filter_args_info *mp3_conf = fn->conf;
 
        fn->private_data = pmd;
        mad_stream_init(&pmd->stream);
        mad_frame_init(&pmd->frame);
        mad_synth_init(&pmd->synth);
-       if (mp3_conf->ignore_crc_given)
+       if (FILTER_CMD_OPT_GIVEN(MP3DEC, IGNORE_CRC, fn->lpr))
                mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC);
 }
 
-static int mp3dec_parse_config(int argc, char **argv, void **config)
-{
-       struct mp3dec_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       mp3dec_filter_cmdline_parser(argc, argv, conf);
-       *config = conf;
-       return 1;
-}
-
 static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
 {
        struct filter_node *fn = btr_context(btrn);
@@ -198,28 +188,10 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
        return decoder_execute(cmd, pmd->sample_rate, pmd->channels, result);
 }
 
-static void mp3dec_free_config(void *conf)
-{
-       mp3dec_filter_cmdline_parser_free(conf);
-}
-/**
- * The init function of the mp3dec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void mp3dec_filter_init(struct filter *f)
-{
-       struct mp3dec_filter_args_info dummy;
-
-       mp3dec_filter_cmdline_parser_init(&dummy);
-       f->open = mp3dec_open;
-       f->close = mp3dec_close;
-       f->parse_config = mp3dec_parse_config;
-       f->free_config = mp3dec_free_config;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = mp3dec_post_select;
-       f->execute = mp3dec_execute;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter);
-}
+const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
+       .open = mp3dec_open,
+       .close = mp3dec_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = mp3dec_post_select,
+       .execute = mp3dec_execute,
+};
diff --git a/net.c b/net.c
index eaa1cfb81146b3dc4fd7bb11cd41df447dc0e825..a58d84ef1c6345dcef41ca5b3ba76d3ed602cb7b 100644 (file)
--- a/net.c
+++ b/net.c
@@ -44,7 +44,7 @@
  *               default of 32 if not specified.
  *
  * \return Pointer to \a addr if successful, NULL on error.
- * \sa RFC 4632
+ * \sa RFC 4632.
  */
 char *parse_cidr(const char *cidr,
                 char    *addr, ssize_t addrlen,
@@ -104,7 +104,7 @@ static bool is_v4_dot_quad(const char *address)
  * \param host The host string to check.
  * \return True if \a host passes the syntax checks.
  *
- * \sa RFC 3986, 3.2.2; RFC 1123, 2.1; RFC 1034, 3.5
+ * \sa RFC 3986, 3.2.2; RFC 1123, 2.1; RFC 1034, 3.5.
  */
 static bool host_string_ok(const char *host)
 {
@@ -146,7 +146,7 @@ static bool host_string_ok(const char *host)
  * \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
+ * \sa RFC 3986, 3.2.2/3.2.3.
  */
 char *parse_url(const char *url,
                char    *host, ssize_t hostlen,
@@ -191,7 +191,7 @@ failed:
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
  *
- * \sa getservent(3), services(5), nsswitch.conf(5)
+ * \sa getservent(3), services(5), nsswitch.conf(5).
  */
 const char *stringify_port(int port, const char *transport)
 {
@@ -215,7 +215,7 @@ const char *stringify_port(int port, const char *transport)
  *
  * \param l4type The symbolic name of the transport-layer protocol.
  *
- * \sa ip(7), socket(2)
+ * \sa ip(7), socket(2).
  */
 static inline int sock_type(const unsigned l4type)
 {
@@ -247,7 +247,7 @@ static const char *layer4_name(const unsigned l4type)
  * directly after makesock(). The 'pre_conn_opt' structure is for internal use
  * only and should not be visible elsewhere.
  *
- * \sa setsockopt(2), makesock()
+ * \sa setsockopt(2), \ref makesock().
  */
 struct pre_conn_opt {
        int             sock_level;     /**< Second argument to setsockopt() */
@@ -288,7 +288,7 @@ struct flowopts *flowopt_new(void)
  * \param val  The value to set \a opt to.
  * \param len  Length of \a val.
  *
- *  \sa setsockopt(2)
+ *  \sa setsockopt(2).
  */
 void flowopt_add(struct flowopts *fo, int lev, int opt,
                const char *name, const void *val, int len)
@@ -505,7 +505,7 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
  * \return Positive integer (socket descriptor) on success, negative value
  * otherwise.
  *
- * \sa makesock(), ip(7), ipv6(7), bind(2), listen(2).
+ * \sa \ref makesock(), ip(7), ipv6(7), bind(2), listen(2).
  */
 int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo)
 {
@@ -551,7 +551,7 @@ static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
  * \param ss Container of IPv4/6 address.
  * \return Pointer to normalized address (may be static storage).
  *
- * \sa RFC 3493
+ * \sa RFC 3493.
  */
 static const struct sockaddr *
 normalize_ip_address(const struct sockaddr_storage *ss)
@@ -603,7 +603,7 @@ static inline int estimated_header_overhead(const int af_type)
  */
 int generic_max_transport_msg_size(int sockfd)
 {
-       struct sockaddr_storage ss = {0};
+       struct sockaddr_storage ss = {.ss_family = 0};
        socklen_t sslen = sizeof(ss);
        int af_type = AF_INET;
 
@@ -624,12 +624,12 @@ int generic_max_transport_msg_size(int sockfd)
  * \return A static character string identifying hostname and port of the
  * chosen side in numeric host:port format.
  *
- * \sa getsockname(2), getpeername(2), parse_url(), getnameinfo(3),
+ * \sa getsockname(2), getpeername(2), \ref parse_url(), getnameinfo(3),
  * services(5), nsswitch.conf(5).
  */
 char *remote_name(int fd)
 {
-       struct sockaddr_storage ss = {0};
+       struct sockaddr_storage ss = {.ss_family = 0};
        const struct sockaddr *sa;
        socklen_t sslen = sizeof(ss);
        char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
@@ -735,7 +735,7 @@ __must_check int recv_bin_buffer(int fd, char *buf, size_t size)
  *
  * \return The return value of the underlying call to \a recv_bin_buffer().
  *
- * \sa recv_bin_buffer()
+ * \sa \ref recv_bin_buffer()
  */
 int recv_buffer(int fd, char *buf, size_t size)
 {
@@ -818,25 +818,37 @@ int dccp_available_ccids(uint8_t **ccid_array)
        return nccids;
 }
 
-/**
- * Prepare a structure for \p AF_UNIX socket addresses.
- *
- * \param u Pointer to the struct to be prepared.
- * \param name The socket pathname.
+/*
+ * Prepare a structure for AF_UNIX socket addresses.
  *
- * This just copies \a name to the sun_path component of \a u.
+ * This just copies name to the sun_path component of u, prepending a zero byte
+ * if abstract sockets are supported.
  *
- * \return Positive on success, \p -E_NAME_TOO_LONG if \a name is longer
- * than \p UNIX_PATH_MAX.
+ * The first call to this function tries to bind a socket to the abstract name
+ * space. The result of this test is stored in a static variable. Subsequent
+ * calls read this variable and create abstract sockets on systems that support
+ * them.
  */
-static int init_unix_addr(struct sockaddr_un *u, const char *name,
-               bool abstract)
+static int init_unix_addr(struct sockaddr_un *u, const char *name)
 {
-       if (strlen(name) + abstract >= UNIX_PATH_MAX)
+       static int use_abstract;
+
+       if (strlen(name) + 1 >= 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 + abstract, name);
+       if (use_abstract == 0) { /* executed only once */
+               int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+               memcpy(u->sun_path, "\0x\0", 3);
+               if (bind(fd, (struct sockaddr *)u, sizeof(*u)) == 0)
+                       use_abstract = 1; /* yes */
+               else
+                       use_abstract = -1; /* no */
+               close(fd);
+               PARA_NOTICE_LOG("%susing abstract socket namespace\n",
+                       use_abstract == 1? "" : "not ");
+       }
+       strcpy(u->sun_path + (use_abstract == 1? 1 : 0), name);
        return 1;
 }
 
@@ -844,28 +856,22 @@ static int init_unix_addr(struct sockaddr_un *u, const char *name,
  * Create a socket for local communication and listen on it.
  *
  * \param name The socket pathname.
- * \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.
  *
- * 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 error code on failure.
  *
  * \sa socket(2), \sa bind(2), \sa chmod(2), listen(2), unix(7).
  */
-int create_local_socket(const char *name, mode_t mode)
+int create_local_socket(const char *name)
 {
        struct sockaddr_un unix_addr;
        int fd, ret;
-       bool abstract = mode == 0;
 
-       ret = init_unix_addr(&unix_addr, name, abstract);
+       ret = init_unix_addr(&unix_addr, name);
        if (ret < 0)
                return ret;
        ret = socket(PF_UNIX, SOCK_STREAM, 0);
@@ -880,7 +886,9 @@ int create_local_socket(const char *name, mode_t mode)
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
-       if (!abstract) {
+       if (unix_addr.sun_path[0] != 0) { /* pathname socket */
+               mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
+                       | S_IROTH | S_IWOTH;
                ret = -E_CHMOD;
                if (chmod(name, mode) < 0)
                        goto err;
@@ -906,7 +914,7 @@ err:
  * \return The file descriptor of the connected socket on success, negative on
  * errors.
  *
- * \sa create_local_socket(), unix(7), connect(2).
+ * \sa \ref create_local_socket(), unix(7), connect(2).
  */
 int connect_local_socket(const char *name)
 {
@@ -917,14 +925,7 @@ int connect_local_socket(const char *name)
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
-       /* first try (linux-only) abstract socket */
-       ret = init_unix_addr(&unix_addr, name, true);
-       if (ret < 0)
-               goto err;
-       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);
+       ret = init_unix_addr(&unix_addr, name);
        if (ret < 0)
                goto err;
        if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
@@ -955,8 +956,7 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
  * \return On success, this call returns the number of bytes sent. On errors,
  * \p -E_SENDMSG is returned.
  *
- * \sa \ref recv_cred_buffer, sendmsg(2), socket(7), unix(7), okir's Black Hats
- * Manual.
+ * \sa \ref recv_cred_buffer, sendmsg(2), socket(7), unix(7).
  */
 ssize_t send_cred_buffer(int sock, char *buf)
 {
diff --git a/net.h b/net.h
index b2bb47c9b40b43fe2eb7c8813e6ffa7364b0853e..a8dd650b7cce698fa7f962a6e55719a561ac093a 100644 (file)
--- a/net.h
+++ b/net.h
@@ -93,7 +93,7 @@ _static_inline_ bool is_valid_ipv4_address(const char *address)
  * \param address The address string to check.
  *
  * \return 1 if string has a valid IPv6 address syntax, 0 if not.
- * \sa RFC 4291
+ * \sa RFC 4291.
  */
 _static_inline_ bool is_valid_ipv6_address(const char *address)
 {
@@ -147,7 +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, mode_t mode);
+int create_local_socket(const char *name);
 int connect_local_socket(const char *name);
 int recv_cred_buffer(int, char *, size_t);
 ssize_t send_cred_buffer(int, char*);
index 2ddf0ee35818fc7fe7aa59ccaf04ad75e7a07502..cb0611e01440a2a1e23e4c586a602c16cb7bbc16 100644 (file)
--- a/ogg_afh.c
+++ b/ogg_afh.c
@@ -212,7 +212,7 @@ static const char * const ogg_suffixes[] = {"ogg", NULL};
  *
  * \param afh Pointer to the struct to initialize.
  */
-void ogg_init(struct audio_format_handler *afh)
+void ogg_afh_init(struct audio_format_handler *afh)
 {
        afh->get_file_info = ogg_vorbis_get_file_info;
        afh->get_header = vorbis_get_header;
index adab7f481fbb0e7873c1d1e1ac4968834bd81fe3..734fd58680b1f944f6adf51c76aebb19d1b5b5c7 100644 (file)
@@ -127,19 +127,18 @@ int ogg_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
 {
        ogg_sync_state oss;
        ogg_page op;
-       long len = numbytes;
        char *buf;
        int ret, i, j, frames_per_chunk, ct_size;
        long long unsigned num_frames = 0;
 
        ogg_sync_init(&oss);
        ret = -E_OGG_SYNC;
-       buf = ogg_sync_buffer(&oss, len);
+       buf = ogg_sync_buffer(&oss, numbytes);
        if (!buf)
                goto out;
-       memcpy(buf, map, len);
+       memcpy(buf, map, numbytes);
        ret = -E_OGG_SYNC;
-       if (ogg_sync_wrote(&oss, len) < 0)
+       if (ogg_sync_wrote(&oss, numbytes) < 0)
                goto out;
        ret = process_ogg_packets(&oss, afhi, ci);
        if (ret < 0)
@@ -182,6 +181,7 @@ int ogg_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
                }
        }
        afhi->chunks_total = j;
+       set_max_chunk_size(afhi);
        set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
        ret = 0;
 out:
index 04be7020e57f29351b73501203db6b1bfa1dbb39..9fa80c5588fc8c8d08653a91a291b9f95ba8e183 100644 (file)
@@ -12,7 +12,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -264,16 +263,10 @@ out:
        return ret;
 }
 
-/**
- * The init function of the ogg vorbis decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void oggdec_filter_init(struct filter *f)
-{
-       f->open = ogg_open;
-       f->close = ogg_close;
-       f->pre_select = ogg_pre_select;
-       f->post_select = ogg_post_select;
-       f->execute = oggdec_execute;
-}
+const struct filter lsg_filter_cmd_com_oggdec_user_data = {
+       .open = ogg_open,
+       .close = ogg_close,
+       .pre_select = ogg_pre_select,
+       .post_select = ogg_post_select,
+       .execute = oggdec_execute
+};
index 282229855d0d97519f886483c249d8e3f4b00b4f..6f9d526c7d31f243b66679eb6555abdb0f2360d9 100644 (file)
@@ -50,7 +50,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -284,18 +283,10 @@ static void opusdec_pre_select(struct sched *s, void *context)
                return sched_min_delay(s);
 }
 
-/**
- * The init function of the opusdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void opusdec_filter_init(struct filter *f)
-{
-       f->open = opusdec_open;
-       f->close = opusdec_close;
-       f->pre_select = opusdec_pre_select;
-       f->post_select = opusdec_post_select;
-       f->execute = opusdec_execute;
-}
+const struct filter lsg_filter_cmd_com_opusdec_user_data = {
+       .open = opusdec_open,
+       .close = opusdec_close,
+       .pre_select = opusdec_pre_select,
+       .post_select = opusdec_post_select,
+       .execute = opusdec_execute,
+};
index 8e87452b816e68f03f0931e79efe007d7491b6c8..7fbdba5b770923988e646b96e74582f8ff4e8a1f 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -135,19 +135,13 @@ static void oss_mix_close(struct mixer_handle **handle)
        *handle = NULL;
 }
 
-/**
- * The init function of the OSS mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref alsa_mix_init().
- */
-void oss_mix_init(struct mixer *self)
-{
-       self->open = oss_mix_open;
-       self->get_channels = oss_mix_get_channels;
-       self->set_channel = oss_mix_set_channel;
-       self->get = oss_mix_get;
-       self->set = oss_mix_set;
-       self->close = oss_mix_close;
-}
+/** The mixer operations for the OSS mixer. */
+const struct mixer oss_mixer = {
+       .name = "oss",
+       .open = oss_mix_open,
+       .get_channels = oss_mix_get_channels,
+       .set_channel = oss_mix_set_channel,
+       .close = oss_mix_close,
+       .get = oss_mix_get,
+       .set = oss_mix_set
+};
index 20186667e2f383880538275d61d1607203ed2b8a..6f646c904016f92b13ea1b04f4a8cbb9f31243b8 100644 (file)
@@ -9,17 +9,16 @@
 #include <regex.h>
 #include <sys/ioctl.h>
 #include <sys/soundcard.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "fd.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
-#include "oss_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the oss writer. */
@@ -106,11 +105,11 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
 {
        int ret, format;
        unsigned ch, rate;
-       struct oss_write_args_info *conf = wn->conf;
        struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+       const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
 
-       PARA_INFO_LOG("opening %s\n", conf->device_arg);
-       ret = para_open(conf->device_arg, O_WRONLY, 0);
+       PARA_INFO_LOG("opening %s\n", dev);
+       ret = para_open(dev, O_WRONLY, 0);
        if (ret < 0)
                goto err_free;
        powd->fd = ret;
@@ -175,8 +174,7 @@ err:
        close(powd->fd);
 err_free:
        free(powd);
-       PARA_ERROR_LOG("failed to init %s: %s\n", conf->device_arg,
-               para_strerror(-ret));
+       PARA_ERROR_LOG("failed to init %s: %s\n", dev, para_strerror(-ret));
        return ret;
 }
 
@@ -240,37 +238,8 @@ out:
        return ret;
 }
 
-__malloc static void *oss_parse_config_or_die(int argc, char **argv)
-{
-       struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       oss_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void oss_free_config(void *conf)
-{
-       oss_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the oss writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void oss_write_init(struct writer *w)
-{
-       struct oss_write_args_info dummy;
-
-       oss_write_cmdline_parser_init(&dummy);
-       w->close = oss_close;
-       w->pre_select = oss_pre_select;
-       w->post_select = oss_post_select;
-       w->parse_config_or_die = oss_parse_config_or_die;
-       w->free_config = oss_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(oss_write);
-       oss_write_cmdline_parser_free(&dummy);
-}
+const struct writer lsg_write_cmd_com_oss_user_data = {
+       .pre_select = oss_pre_select,
+       .post_select = oss_post_select,
+       .close = oss_close,
+};
diff --git a/osx_write.c b/osx_write.c
deleted file mode 100644 (file)
index 18a2c08..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file osx_write.c paraslash's output plugin for MacOs */
-
-/*
- * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze
- * <skortze@sourceforge.net>
- */
-
-#include <regex.h>
-#include <sys/types.h>
-
-#include "para.h"
-#include "fd.h"
-#include "string.h"
-#include "list.h"
-#include "sched.h"
-#include "ggo.h"
-#include "buffer_tree.h"
-#include "write.h"
-#include "write_common.h"
-#include "osx_write.cmdline.h"
-#include "ipc.h"
-#include "error.h"
-
-#include <CoreServices/CoreServices.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-/** Data specific to the osx writer. */
-struct private_osx_write_data {
-       /** The main CoreAudio handle. */
-       AudioUnit audio_unit;
-       /** True if we wrote some audio data. */
-       bool playing;
-       /** Sample rate of the current audio stream. */
-       unsigned sample_rate;
-       /** Sample format of the current audio stream */
-       unsigned sample_format;
-       /** Number of channels of the current audio stream. */
-       unsigned channels;
-       /**
-        * Serializes access to buffer tree nodes between the writer and
-        * the callback which runs in a different thread.
-        */
-       int mutex;
-       /**
-        * The btr node of the callback.
-        *
-        * Although access to the btr node is serialized between the writer and
-        * the callback via the above mutex, this does not stop other buffer
-        * tree nodes, for example the decoder, to race against the osx
-        * callback.
-        *
-        * However, since all operations on buffer tree nodes are local in the
-        * sense that they only affect one level in the buffer tree (i.e.
-        * parent or child nodes, but not the grandparent or the
-        * grandchildren), we may work around this problem by using another
-        * buffer tree node for the callback.
-        *
-        * The writer grabs the mutex in its post_select method and pushes down
-        * the buffers to the callback node.
-        */
-       struct btr_node *callback_btrn;
-};
-
-/* This function writes the address and the number of bytes to one end of the socket.
- * The post_select() function then fills the buffer and notifies the callback also
- * through the socket.
- */
-static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags *af,
-               __a_unused const AudioTimeStamp *ts, __a_unused  UInt32 bus_number,
-               __a_unused UInt32 num_frames, AudioBufferList *abl)
-{
-       int i;
-       struct writer_node *wn = cb_arg;
-       struct private_osx_write_data *powd;
-       size_t samples_have, samples_want = 0;
-
-       powd = wn->private_data;
-       mutex_lock(powd->mutex);
-       powd = wn->private_data;
-       if (!powd || !wn->btrn)
-               goto out;
-       /*
-        * We fill with zeros if no data was yet written and we do not have
-        * enough to fill all buffers.
-        */
-       if (!powd->playing) {
-               size_t want = 0, have =
-                       btr_get_input_queue_size(powd->callback_btrn);
-               for (i = 0; i < abl->mNumberBuffers; i++)
-                       want += abl->mBuffers[i].mDataByteSize;
-               if (have < want) {
-                       PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
-                               have, want);
-                       for (i = 0; i < abl->mNumberBuffers; i++)
-                               memset(abl->mBuffers[i].mData, 0,
-                                       abl->mBuffers[i].mDataByteSize);
-                       goto out;
-               }
-               powd->playing = true;
-       }
-
-       for (i = 0; i < abl->mNumberBuffers; i++) {
-               /* what we have to fill */
-               void *dest = abl->mBuffers[i].mData;
-               size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes;
-
-               samples_want = sz / wn->min_iqs;
-               while (samples_want > 0) {
-                       char *buf;
-                       btr_merge(powd->callback_btrn, wn->min_iqs);
-                       samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs;
-                       //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want,
-                       //      dest, samples_have);
-                       samples = PARA_MIN(samples_have, samples_want);
-                       if (samples == 0)
-                               break;
-                       bytes = samples * wn->min_iqs;
-                       memcpy(dest, buf, bytes);
-                       btr_consume(powd->callback_btrn, bytes);
-                       samples_want -= samples;
-                       dest += bytes;
-               }
-               if (samples_want == 0)
-                       continue;
-               if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0)
-                       PARA_INFO_LOG("zero-padding (%zu samples)\n",
-                               samples_want);
-               memset(dest, 0, samples_want * wn->min_iqs);
-               break;
-       }
-out:
-       mutex_unlock(powd->mutex);
-       return noErr;
-}
-
-static int core_audio_init(struct writer_node *wn)
-{
-       struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
-       Component comp;
-       int ret;
-       int32_t val;
-       AURenderCallbackStruct input_callback;
-       ComponentDescription desc = {
-               .componentType = kAudioUnitType_Output,
-               .componentSubType = kAudioUnitSubType_DefaultOutput,
-               .componentManufacturer = kAudioUnitManufacturer_Apple,
-       };
-       AudioStreamBasicDescription format = {
-               .mFormatID = kAudioFormatLinearPCM,
-               .mFramesPerPacket = 1,
-       };
-       struct btr_node *btrn = wn->btrn;
-       struct btr_node_description bnd;
-
-       PARA_INFO_LOG("wn: %p\n", wn);
-       ret = -E_DEFAULT_COMP;
-       comp = FindNextComponent(NULL, &desc);
-       if (!comp)
-               goto e0;
-       ret = -E_OPEN_COMP;
-       if (OpenAComponent(comp, &powd->audio_unit))
-               goto e0;
-       ret = -E_UNIT_INIT;
-       if (AudioUnitInitialize(powd->audio_unit))
-               goto e1;
-       get_btr_sample_rate(btrn, &val);
-       powd->sample_rate = val;
-       get_btr_channels(btrn, &val);
-       powd->channels = val;
-       get_btr_sample_format(btrn, &val);
-       powd->sample_format = val;
-       /*
-        * Choose PCM format. We tell the Output Unit what format we're going
-        * to supply data to it. This is necessary if you're providing data
-        * through an input callback AND you want the DefaultOutputUnit to do
-        * any format conversions necessary from your format to the device's
-        * format.
-        */
-
-       format.mSampleRate = powd->sample_rate;
-       format.mChannelsPerFrame = powd->channels;
-
-       switch (powd->sample_format) {
-       case SF_S8:
-       case SF_U8:
-               wn->min_iqs = powd->channels;
-               format.mBitsPerChannel = 8;
-               format.mBytesPerPacket = powd->channels;
-               format.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
-               break;
-       default:
-               wn->min_iqs = powd->channels * 2;
-               format.mBytesPerPacket = powd->channels * 2;
-               format.mBitsPerChannel = 16;
-               format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
-       }
-       format.mBytesPerFrame = format.mBytesPerPacket;
-
-       if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE)
-               format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-
-       input_callback = (AURenderCallbackStruct){osx_callback, wn};
-       ret = -E_STREAM_FORMAT;
-       if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat,
-                       kAudioUnitScope_Input, 0, &format, sizeof(format)))
-               goto e2;
-       ret = -E_ADD_CALLBACK;
-       if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
-                       kAudioUnitScope_Input, 0, &input_callback,
-                       sizeof(input_callback)) < 0)
-               goto e2;
-
-       ret = mutex_new();
-       if (ret < 0)
-               goto e2;
-       powd->mutex = ret;
-       /* set up callback btr node */
-       bnd.name = "cb_node";
-       bnd.parent = btrn;
-       bnd.child = NULL;
-       bnd.handler = NULL;
-       bnd.context = powd;
-       powd->callback_btrn = btr_new_node(&bnd);
-       wn->private_data = powd;
-       return 1;
-e2:
-       AudioUnitUninitialize(powd->audio_unit);
-e1:
-       CloseComponent(powd->audio_unit);
-e0:
-       free(powd);
-       wn->private_data = NULL;
-       return ret;
-}
-
-__malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
-{
-       struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       osx_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void osx_free_config(void *conf)
-{
-       osx_write_cmdline_parser_free(conf);
-}
-
-static void osx_write_close(struct writer_node *wn)
-{
-       struct private_osx_write_data *powd = wn->private_data;
-
-       if (!powd)
-               return;
-       PARA_INFO_LOG("closing writer node %p\n", wn);
-       mutex_destroy(powd->mutex);
-       free(powd);
-       wn->private_data = NULL;
-}
-
-/* must be called with the mutex held */
-static inline bool need_drain_delay(struct private_osx_write_data *powd)
-{
-       if (!powd->playing)
-               return false;
-       return btr_get_input_queue_size(powd->callback_btrn) != 0;
-}
-
-static void osx_write_pre_select(struct sched *s, void *context)
-{
-       struct writer_node *wn = context;
-       struct private_osx_write_data *powd = wn->private_data;
-       int ret;
-       bool drain_delay_nec = false;
-
-       if (!powd) {
-               ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
-               if (ret != 0)
-                       sched_min_delay(s);
-               return;
-       }
-
-       mutex_lock(powd->mutex);
-       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL);
-       if (ret < 0)
-               drain_delay_nec = need_drain_delay(powd);
-       mutex_unlock(powd->mutex);
-
-       if (drain_delay_nec)
-               return sched_request_timeout_ms(50, s);
-       if (ret != 0)
-               return sched_min_delay(s);
-       sched_request_timeout_ms(50, s);
-}
-
-static int osx_write_post_select(__a_unused struct sched *s, void *context)
-{
-       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(wn->task);
-       if (ret < 0)
-               goto fail;
-       if (!powd) {
-               ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
-               if (ret == 0)
-                       return 0;
-               if (ret < 0)
-                       goto fail;
-               ret = core_audio_init(wn);
-               if (ret < 0)
-                       goto fail;
-               powd = wn->private_data;
-               ret = -E_UNIT_START;
-               if (AudioOutputUnitStart(powd->audio_unit) != noErr) {
-                       AudioUnitUninitialize(powd->audio_unit);
-                       CloseComponent(powd->audio_unit);
-                       btr_remove_node(&powd->callback_btrn);
-                       goto fail;
-               }
-       }
-       mutex_lock(powd->mutex);
-       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL);
-       if (ret > 0)
-               btr_pushdown(btrn);
-       if (ret < 0 && need_drain_delay(powd))
-               ret = 0;
-       mutex_unlock(powd->mutex);
-       if (ret >= 0)
-               return 0;
-fail:
-       assert(ret < 0);
-       if (powd && powd->callback_btrn) {
-               AudioOutputUnitStop(powd->audio_unit);
-               AudioUnitUninitialize(powd->audio_unit);
-               CloseComponent(powd->audio_unit);
-               btr_remove_node(&powd->callback_btrn);
-       }
-       btr_remove_node(&wn->btrn);
-       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-       return ret;
-}
-
-/**
- * The init function of the osx writer.
- *
- * \param w Filled in by the function.
- */
-void osx_write_init(struct writer *w)
-{
-       struct osx_write_args_info dummy;
-
-       osx_write_cmdline_parser_init(&dummy);
-       w->close = osx_write_close;
-       w->pre_select = osx_write_pre_select;
-       w->post_select = osx_write_post_select;
-       w->parse_config_or_die = osx_write_parse_config_or_die;
-       w->free_config = osx_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write);
-       osx_write_cmdline_parser_free(&dummy);
-}
diff --git a/para.h b/para.h
index 12d236391dcfb13e18d50450164517346e77398e..3cd1b1621509330f0df7d6d6f7f1b02095a09153 100644 (file)
--- a/para.h
+++ b/para.h
@@ -223,24 +223,9 @@ enum sample_format {SAMPLE_FORMATS};
 #define SAMPLE_FORMAT(a, b) b
 /** \endcond sample_format */
 
-/** Debug loglevel, gets really noisy. */
-#define LL_DEBUG 0
-/** Still noisy, but won't fill your disk. */
-#define LL_INFO  1
-/** Normal, but significant event. */
-#define LL_NOTICE 2
-/** Unexpected event that can be handled. */
-#define LL_WARNING 3
-/** Unhandled error condition. */
-#define LL_ERROR 4
-/** System might be unreliable. */
-#define LL_CRIT 5
-/** Last message before exit. */
-#define LL_EMERG 6
-/** Number of all loglevels. */
-#define NUM_LOGLEVELS 7
-
-/** \cond log */
+/** Debug, Info, etc. */
+enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
+
 #define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
@@ -248,4 +233,3 @@ enum sample_format {SAMPLE_FORMATS};
 #define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-/** \endcond log */
diff --git a/play.c b/play.c
index fac551aa161d14f258e1495a4530462ca1146357..2155ebf824d45bb578bcb0b782c565be42e349b2 100644 (file)
--- a/play.c
+++ b/play.c
@@ -7,14 +7,16 @@
 /** \file play.c Paraslash's standalone player. */
 
 #include <regex.h>
-#include <fnmatch.h>
 #include <signal.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
+#include "play_cmd.lsg.h"
+#include "write_cmd.lsg.h"
+#include "play.lsg.h"
 #include "para.h"
 #include "list.h"
-#include "play.cmdline.h"
 #include "error.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "version.h"
 #include "string.h"
@@ -23,7 +25,6 @@
 #include "afh.h"
 #include "recv.h"
 #include "write.h"
-#include "write_common.h"
 #include "fd.h"
 
 /**
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *play_lpr;
+
+#define CMD_PTR (lls_cmd(0, play_suite))
+#define OPT_RESULT(_name) \
+       (lls_opt_result(LSG_PLAY_PARA_PLAY_OPT_ ## _name, play_lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
 /**
  * Describes a request to change the state of para_play.
  *
@@ -75,7 +85,7 @@ struct play_task {
        struct filter_node fn;
        struct writer_node wn;
 
-       /* See comment to enum state_change_request_type above */
+       /* See comment to enum \ref state_change_request_type above. */
        enum state_change_request_type rq;
        /* only relevant if rq == CRT_FILE_CHANGE */
        unsigned next_file;
@@ -98,13 +108,14 @@ struct play_task {
        char *afhi_txt;
 };
 
-/* Activate the afh receiver. */
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-/** Initialization code for a receiver struct. */
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-/** This expands to the array of all receivers. */
-DEFINE_RECEIVER_ARRAY;
+typedef int (*play_cmd_handler_t)(struct lls_parse_result *lpr);
+struct play_command_info {
+       play_cmd_handler_t handler;
+};
+#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
+       const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 static int loglevel = LL_WARNING;
 
@@ -113,90 +124,109 @@ INIT_STDERR_LOGGING(loglevel);
 
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
-/** Iterate over all files in the playlist. */
-#define FOR_EACH_PLAYLIST_FILE(i) for (i = 0; i < conf.inputs_num; i++)
-static struct play_args_info conf;
-
 static struct sched sched = {.max_fileno = 0};
-static struct play_task play_task;
-static struct receiver *afh_recv;
+static struct play_task play_task, *pt = &play_task;
 
-static void check_afh_receiver_or_die(void)
-{
-       int i;
+#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
+#define AFH_RECV ((struct receiver *)lls_user_data(AFH_RECV_CMD))
 
-       FOR_EACH_RECEIVER(i) {
-               struct receiver *r = receivers + i;
-               if (strcmp(r->name, "afh"))
-                       continue;
-               afh_recv = r;
-               return;
-       }
-       PARA_EMERG_LOG("fatal: afh receiver not found\n");
-       exit(EXIT_FAILURE);
+static unsigned *shuffle_map;
+
+static const char *get_playlist_file(unsigned idx)
+{
+       return lls_input(shuffle_map[idx], play_lpr);
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help help = DEFINE_GGO_HELP(play);
-       unsigned flags = conf.detailed_help_given?
-               GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+       char *help;
 
-       ggo_print_help(&help, flags);
-       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
-       exit(0);
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 static void parse_config_or_die(int argc, char *argv[])
 {
-       int i, ret;
-       char *config_file;
-       struct play_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
+       const struct lls_command *cmd = CMD_PTR;
+       int i, ret, cf_argc;
+       char *cf, *errctx, **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
+       unsigned num_kmas;
+       void *map;
+       size_t sz;
 
-       play_cmdline_parser_ext(argc, argv, &conf, &params);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("play", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       ret = lls(lls_parse(argc, argv, cmd, &play_lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("play", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/play.conf", home);
+               cf = make_message("%s/.paraslash/play.conf", home);
                free(home);
        }
-       ret = file_exists(config_file);
-       if (conf.config_file_given && !ret) {
-               PARA_EMERG_LOG("can not read config file %s\n", config_file);
-               goto err;
-       }
-       if (ret) {
-               params.initialize = 0;
-               params.check_required = 1;
-               play_cmdline_parser_config_file(config_file, &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+                       goto free_cf;
+               ret = 0;
+               goto free_cf;
        }
-       for (i = 0; i < conf.key_map_given; i++) {
-               char *kma = conf.key_map_arg[i];
+       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+       para_munmap(map, sz);
+       if (ret < 0)
+               goto free_cf;
+       cf_argc = ret;
+       ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+       lls_free_argv(cf_argv);
+       if (ret < 0)
+               goto free_cf;
+       ret = lls(lls_merge(play_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
+       lls_free_parse_result(cf_lpr, cmd);
+       if (ret < 0)
+               goto free_cf;
+       lls_free_parse_result(play_lpr, cmd);
+       play_lpr = merged_lpr;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+
+       ret = lls(lls_check_arg_count(play_lpr, 1, INT_MAX, &errctx));
+       if (ret < 0)
+               goto free_cf;
+       num_kmas = OPT_GIVEN(KEY_MAP);
+       for (i = 0; i < num_kmas; i++) {
+               const char *kma = lls_string_val(i, OPT_RESULT(KEY_MAP));
                if (*kma && strchr(kma + 1, ':'))
                        continue;
                PARA_EMERG_LOG("invalid key map arg: %s\n", kma);
-               goto err;
+               goto free_cf;
        }
-       free(config_file);
-       return;
-err:
-       free(config_file);
+       ret = 1;
+free_cf:
+       free(cf);
+       if (ret >= 0)
+               return;
+       lls_free_parse_result(play_lpr, cmd);
+fail:
+       if (errctx)
+               PARA_EMERG_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
 }
 
-static char get_playback_state(struct play_task *pt)
+static char get_playback_state(void)
 {
        switch (pt->rq) {
        case CRT_NONE: return pt->playing? 'P' : 'U';
@@ -207,9 +237,9 @@ static char get_playback_state(struct play_task *pt)
        assert(false);
 };
 
-static long unsigned get_play_time(struct play_task *pt)
+static long unsigned get_play_time(void)
 {
-       char state = get_playback_state(pt);
+       char state = get_playback_state();
        long unsigned result;
 
        if (state != 'P' && state != 'U')
@@ -230,17 +260,18 @@ static long unsigned get_play_time(struct play_task *pt)
        return result;
 }
 
-static void wipe_receiver_node(struct play_task *pt)
+
+static void wipe_receiver_node(void)
 {
        PARA_NOTICE_LOG("cleaning up receiver node\n");
        btr_remove_node(&pt->rn.btrn);
-       afh_recv->close(&pt->rn);
-       afh_recv->free_config(pt->rn.conf);
+       AFH_RECV->close(&pt->rn);
+       lls_free_parse_result(pt->rn.lpr, AFH_RECV_CMD);
        memset(&pt->rn, 0, sizeof(struct receiver_node));
 }
 
 /* returns: 0 not eof, 1: eof, < 0: fatal error.  */
-static int get_playback_error(struct play_task *pt)
+static int get_playback_error(void)
 {
        int err;
 
@@ -259,22 +290,23 @@ static int get_playback_error(struct play_task *pt)
        return err;
 }
 
-static int eof_cleanup(struct play_task *pt)
+static int eof_cleanup(void)
 {
-       struct writer *w = writers + DEFAULT_WRITER;
-       const struct filter *decoder = filter_get(pt->fn.filter_num);
+       const struct filter *decoder;
+       const struct writer *w = writer_get(-1); /* default writer */
        int ret;
 
-       ret = get_playback_error(pt);
+       ret = get_playback_error();
        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);
+       lls_free_parse_result(pt->wn.lpr, WRITE_CMD(pt->wn.wid));
        memset(&pt->wn, 0, sizeof(struct writer_node));
 
+       decoder = filter_get(pt->fn.filter_num);
        task_reap(&pt->fn.task);
        if (decoder->close)
                decoder->close(&pt->fn);
@@ -290,7 +322,7 @@ static int eof_cleanup(struct play_task *pt)
         * paused.
         */
        if (ret < 0)
-               wipe_receiver_node(pt);
+               wipe_receiver_node();
        return ret;
 }
 
@@ -299,34 +331,42 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b)
        return para_random(100) - 50;
 }
 
-static void shuffle(char **base, size_t num)
+static void init_shuffle_map(void)
 {
+       unsigned n, num_inputs = lls_num_inputs(play_lpr);
+       shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+       for (n = 0; n < num_inputs; n++)
+               shuffle_map[n] = n;
+       if (!OPT_GIVEN(RANDOMIZE))
+               return;
        srandom(time(NULL));
-       qsort(base, num, sizeof(char *), shuffle_compare);
+       qsort(shuffle_map, num_inputs, sizeof(unsigned), shuffle_compare);
 }
 
 static struct btr_node *new_recv_btrn(struct receiver_node *rn)
 {
        return btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = afh_recv->name, .context = rn,
-                       .handler = afh_recv->execute));
+               EMBRACE(.name = lls_command_name(AFH_RECV_CMD), .context = rn,
+                       .handler = AFH_RECV->execute));
 }
 
-static int open_new_file(struct play_task *pt)
+static int open_new_file(void)
 {
        int ret;
-       char *tmp, *path = conf.inputs[pt->next_file], *afh_recv_conf[] =
-               {"play", "-f", path, "-b", "0", NULL};
+       const char *path = get_playlist_file(pt->next_file);
+       char *tmp = para_strdup(path), *errctx;
+       char *argv[] = {"play", "-f", tmp, "-b", "0", NULL};
 
        PARA_NOTICE_LOG("next file: %s\n", path);
-       wipe_receiver_node(pt);
+       wipe_receiver_node();
        pt->start_chunk = 0;
        pt->rn.btrn = new_recv_btrn(&pt->rn);
-       pt->rn.conf = afh_recv->parse_config(ARRAY_SIZE(afh_recv_conf) - 1,
-               afh_recv_conf);
-       assert(pt->rn.conf);
-       pt->rn.receiver = afh_recv;
-       ret = afh_recv->open(&pt->rn);
+       ret = lls(lls_parse(ARRAY_SIZE(argv) - 1, argv, AFH_RECV_CMD,
+               &pt->rn.lpr, &errctx));
+       free(tmp);
+       assert(ret >= 0);
+       pt->rn.receiver = AFH_RECV;
+       ret = AFH_RECV->open(&pt->rn);
        if (ret < 0) {
                PARA_ERROR_LOG("could not open %s\n", path);
                goto fail;
@@ -358,20 +398,21 @@ static int open_new_file(struct play_task *pt)
        }
        return 1;
 fail:
-       wipe_receiver_node(pt);
+       wipe_receiver_node();
        return ret;
 }
 
-static int load_file(struct play_task *pt)
+static int load_file(void)
 {
        const char *af;
        char *tmp, buf[20];
        int ret;
        const struct filter *decoder;
+       static struct lls_parse_result *filter_lpr, *writer_lpr;
 
        btr_remove_node(&pt->rn.btrn);
        if (!pt->rn.receiver || pt->next_file != pt->current_file) {
-               ret = open_new_file(pt);
+               ret = open_new_file();
                if (ret < 0)
                        return ret;
        } else {
@@ -387,30 +428,31 @@ 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);
+       ret = filter_setup(tmp, &pt->fn.conf, &filter_lpr);
        freep(&tmp);
        if (ret < 0)
                goto fail;
        pt->fn.filter_num = ret;
+       pt->fn.lpr = filter_lpr;
        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));
+               EMBRACE(.name = filter_name(pt->fn.filter_num),
+                       .parent = pt->rn.btrn, .handler = decoder->execute,
+                       .context = &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.wid = check_writer_arg_or_die(NULL, &writer_lpr);
+       pt->wn.lpr = writer_lpr;
        /* success, register tasks */
        pt->rn.task = task_register(
                &(struct task_info) {
-                       .name = afh_recv->name,
-                       .pre_select = afh_recv->pre_select,
-                       .post_select = afh_recv->post_select,
+                       .name = lls_command_name(AFH_RECV_CMD),
+                       .pre_select = AFH_RECV->pre_select,
+                       .post_select = AFH_RECV->post_select,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
@@ -424,36 +466,37 @@ static int load_file(struct play_task *pt)
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
        return 1;
 fail:
-       wipe_receiver_node(pt);
+       wipe_receiver_node();
        return ret;
 }
 
-static int next_valid_file(struct play_task *pt)
+static int next_valid_file(void)
 {
        int i, j = pt->current_file;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       FOR_EACH_PLAYLIST_FILE(i) {
-               j = (j + 1) % conf.inputs_num;
+       for (i = 0; i < num_inputs; i++) {
+               j = (j + 1) % num_inputs;
                if (!pt->invalid[j])
                        return j;
        }
        return -E_NO_VALID_FILES;
 }
 
-static int load_next_file(struct play_task *pt)
+static int load_next_file(void)
 {
        int ret;
 
 again:
        if (pt->rq == CRT_NONE) {
                pt->start_chunk = 0;
-               ret = next_valid_file(pt);
+               ret = next_valid_file();
                if (ret < 0)
                        return ret;
                pt->next_file = ret;
        } else if (pt->rq == CRT_REPOS)
                pt->next_file = pt->current_file;
-       ret = load_file(pt);
+       ret = load_file();
        if (ret < 0) {
                PARA_ERROR_LOG("%s: marking file as invalid\n",
                        para_strerror(-ret));
@@ -466,7 +509,7 @@ again:
        return ret;
 }
 
-static void kill_stream(struct play_task *pt)
+static void kill_stream(void)
 {
        if (pt->wn.task)
                task_notify(pt->wn.task, E_EOF);
@@ -475,14 +518,15 @@ static void kill_stream(struct play_task *pt)
 #ifdef HAVE_READLINE
 
 /* only called from com_prev(), nec. only if we have readline */
-static int previous_valid_file(struct play_task *pt)
+static int previous_valid_file(void)
 {
        int i, j = pt->current_file;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       FOR_EACH_PLAYLIST_FILE(i) {
+       for (i = 0; i < num_inputs; i++) {
                j--;
                if (j < 0)
-                       j = conf.inputs_num - 1;
+                       j = num_inputs - 1;
                if (!pt->invalid[j])
                        return j;
        }
@@ -533,7 +577,7 @@ static const char *default_keyseqs[] = {INTERNAL_KEYMAP_ENTRIES};
 static const char *default_commands[] = {INTERNAL_KEYMAP_ENTRIES};
 #undef KEYMAP_ENTRY
 #define NUM_INTERNALLY_MAPPED_KEYS ARRAY_SIZE(default_commands)
-#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + conf.key_map_given)
+#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + OPT_GIVEN(KEY_MAP))
 #define FOR_EACH_MAPPED_KEY(i) for (i = 0; i < NUM_MAPPED_KEYS; i++)
 
 static inline bool is_internal_key(int key)
@@ -564,9 +608,9 @@ static inline int get_key_map_idx(int key)
                get_internal_key_map_idx(key) : get_user_key_map_idx(key);
 }
 
-static inline char *get_user_key_map_arg(int key)
+static inline const char *get_user_key_map_arg(int key)
 {
-       return conf.key_map_arg[get_user_key_map_idx(key)];
+       return lls_string_val(get_user_key_map_idx(key), OPT_RESULT(KEY_MAP));
 }
 
 static inline char *get_internal_key_map_seq(int key)
@@ -653,24 +697,6 @@ static char **get_mapped_keyseqs(void)
        return result;
 }
 
-#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;
-       play_command_handler_t *handler;
-       const char *description;
-       const char *usage;
-       const char *help;
-};
-
-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"
 static struct i9e_completer pp_completers[];
 
 I9E_DUMMY_COMPLETER(jmp);
@@ -682,7 +708,6 @@ I9E_DUMMY_COMPLETER(ls);
 I9E_DUMMY_COMPLETER(info);
 I9E_DUMMY_COMPLETER(play);
 I9E_DUMMY_COMPLETER(pause);
-I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(tasks);
 I9E_DUMMY_COMPLETER(quit);
 I9E_DUMMY_COMPLETER(ff);
@@ -693,9 +718,15 @@ static void help_completer(struct i9e_completion_info *ci,
        result->matches = i9e_complete_commands(ci->word, pp_completers);
 }
 
-static struct i9e_completer pp_completers[] = {PLAY_COMPLETERS {.name = NULL}};
+static struct i9e_completer pp_completers[] = {
+#define LSG_PLAY_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_PLAY_CMD_SUBCOMMANDS
+#undef LSG_PLAY_CMD_CMD
+       {.name = NULL}
+};
 
-static void attach_stdout(struct play_task *pt, const char *name)
+static void attach_stdout(const char *name)
 {
        if (pt->btrn)
                return;
@@ -704,141 +735,137 @@ static void attach_stdout(struct play_task *pt, const char *name)
        i9e_attach_to_stdout(pt->btrn);
 }
 
-static void detach_stdout(struct play_task *pt)
+static void detach_stdout(void)
 {
        btr_remove_node(&pt->btrn);
 }
 
-static int com_quit(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_quit(__a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->rq = CRT_TERM_RQ;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(quit);
 
-static int com_help(struct play_task *pt, int argc, char **argv)
+static int com_help(struct lls_parse_result *lpr)
 {
-       int i;
-       char *buf;
+       int i, ret;
+       char *buf, *errctx;
        size_t sz;
+       const struct lls_command *cmd;
 
-       if (argc > 2)
-               return -E_PLAY_SYNTAX;
-       if (argc < 2) {
-               if (pt->background)
-                       FOR_EACH_COMMAND(i) {
-                               sz = xasprintf(&buf, "%s\t%s\n", pp_cmds[i].name,
-                                       pp_cmds[i].description);
-                               btr_add_output(buf, sz, pt->btrn);
-                       }
-               else {
-                       FOR_EACH_MAPPED_KEY(i) {
-                               bool internal = is_internal_key(i);
-                               int idx = get_key_map_idx(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",
-                                       internal? "internal" : "user-defined",
-                                       idx, seq, cmd);
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) {
+               if (pt->background) {
+                       for (i = 1; (cmd = lls_cmd(i, play_cmd_suite)); i++) {
+                               sz = xasprintf(&buf, "%s\t%s\n",
+                                       lls_command_name(cmd), lls_purpose(cmd));
                                btr_add_output(buf, sz, pt->btrn);
-                               free(seq);
-                               free(cmd);
                        }
+                       return 0;
+               }
+               FOR_EACH_MAPPED_KEY(i) {
+                       bool internal = is_internal_key(i);
+                       int idx = get_key_map_idx(i);
+                       char *seq = get_key_map_seq_safe(i);
+                       char *kmc = get_key_map_cmd(i);
+                       sz = xasprintf(&buf, "%s key #%d: %s -> %s\n",
+                               internal? "internal" : "user-defined",
+                               idx, seq, kmc);
+                       btr_add_output(buf, sz, pt->btrn);
+                       free(seq);
+                       free(kmc);
                }
                return 0;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(pp_cmds[i].name, argv[1]))
-                       continue;
-               sz = xasprintf(&buf,
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\t%s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       pp_cmds[i].description,
-                       pp_cmds[i].usage,
-                       pp_cmds[i].help
-               );
-               btr_add_output(buf, sz, pt->btrn);
-               return 0;
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), play_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
        }
-       return -E_BAD_PLAY_CMD;
+       cmd = lls_cmd(ret, play_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       btr_add_output(buf, strlen(buf), pt->btrn);
+       return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(help);
 
-static int com_info(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_info(__a_unused struct lls_parse_result *lpr)
 {
        char *buf;
        size_t sz;
        static char dflt[] = "[no information available]";
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        sz = xasprintf(&buf, "playlist_pos: %u\npath: %s\n",
-               pt->current_file, conf.inputs[pt->current_file]);
+               pt->current_file, get_playlist_file(pt->current_file));
        btr_add_output(buf, sz, pt->btrn);
        buf = pt->afhi_txt? pt->afhi_txt : dflt;
        btr_add_output_dont_free(buf, strlen(buf), pt->btrn);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(info);
 
-static void list_file(struct play_task *pt, int num)
+static void list_file(int num)
 {
        char *buf;
        size_t sz;
 
        sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
-               "*" : " ", num, conf.inputs[num]);
+               "*" : " ", num, get_playlist_file(num));
        btr_add_output(buf, sz, pt->btrn);
 }
 
-static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_tasks(__a_unused struct lls_parse_result *lpr)
 {
        static char state;
        char *buf;
        size_t sz;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
-
        buf = get_task_list(&sched);
        btr_add_output(buf, strlen(buf), pt->btrn);
-       state = get_playback_state(pt);
+       state = get_playback_state();
        sz = xasprintf(&buf, "state: %c\n", state);
        btr_add_output(buf, sz, pt->btrn);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(tasks);
 
-static int com_ls(struct play_task *pt, int argc, char **argv)
+static int com_ls(__a_unused struct lls_parse_result *lpr)
 {
-       int i, j, ret;
+       int i;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       if (argc == 1) {
-               FOR_EACH_PLAYLIST_FILE(i)
-                       list_file(pt, i);
-               return 0;
-       }
-       for (j = 1; j < argc; j++) {
-               FOR_EACH_PLAYLIST_FILE(i) {
-                       ret = fnmatch(argv[j], conf.inputs[i], 0);
-                       if (ret == 0) /* match */
-                               list_file(pt, i);
-               }
-       }
+       for (i = 0; i < num_inputs; i++)
+               list_file(i);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(ls);
 
-static int com_play(struct play_task *pt, int argc, char **argv)
+static int com_play(struct lls_parse_result *lpr)
 {
        int32_t x;
        int ret;
-       char state;
+       char state, *errctx;
 
-       if (argc > 2)
-               return -E_PLAY_SYNTAX;
-       state = get_playback_state(pt);
-       if (argc == 1) {
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       state = get_playback_state();
+       if (lls_num_inputs(lpr) == 0) {
                if (state == 'P')
                        return 0;
                pt->next_file = pt->current_file;
@@ -846,29 +873,28 @@ static int com_play(struct play_task *pt, int argc, char **argv)
                pt->playing = true;
                return 0;
        }
-       ret = para_atoi32(argv[1], &x);
+       ret = para_atoi32(lls_input(0, lpr), &x);
        if (ret < 0)
                return ret;
-       if (x < 0 || x >= conf.inputs_num)
+       if (x < 0 || x >= lls_num_inputs(play_lpr))
                return -ERRNO_TO_PARA_ERROR(EINVAL);
-       kill_stream(pt);
+       kill_stream();
        pt->next_file = x;
        pt->rq = CRT_FILE_CHANGE;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(play);
 
-static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_pause(__a_unused struct lls_parse_result *lpr)
 {
        char state;
        long unsigned seconds, ss;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
-       state = get_playback_state(pt);
+       state = get_playback_state();
        pt->playing = false;
        if (state != 'P')
                return 0;
-       seconds = get_play_time(pt);
+       seconds = get_play_time();
        pt->playing = false;
        ss = 0;
        if (pt->seconds > 0)
@@ -876,96 +902,105 @@ static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
        ss = PARA_MAX(ss, 0UL);
        ss = PARA_MIN(ss, pt->num_chunks);
        pt->start_chunk = ss;
-       kill_stream(pt);
+       kill_stream();
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(pause);
 
-static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
-
+static int com_prev(__a_unused struct lls_parse_result *lpr)
 {
        int ret;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
-       ret = previous_valid_file(pt);
+       ret = previous_valid_file();
        if (ret < 0)
                return ret;
-       kill_stream(pt);
+       kill_stream();
        pt->next_file = ret;
        pt->rq = CRT_FILE_CHANGE;
        pt->start_chunk = 0;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(prev);
 
-static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_next(__a_unused struct lls_parse_result *lpr)
 {
        int ret;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
-       ret = next_valid_file(pt);
+       ret = next_valid_file();
        if (ret < 0)
                return ret;
-       kill_stream(pt);
+       kill_stream();
        pt->next_file = ret;
        pt->rq = CRT_FILE_CHANGE;
        pt->start_chunk = 0;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(next);
 
-static int com_fg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_fg(__a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->background = false;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(fg);
 
-static int com_bg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_bg(__a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->background = true;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(bg);
 
-static int com_jmp(struct play_task *pt, int argc, char **argv)
+static int com_jmp(struct lls_parse_result *lpr)
 {
        int32_t percent;
        int ret;
+       char *errctx;
 
-       if (argc != 2)
-               return -E_PLAY_SYNTAX;
-       ret = para_atoi32(argv[1], &percent);
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       ret = para_atoi32(lls_input(0, lpr), &percent);
        if (ret < 0)
                return ret;
        if (percent < 0 || percent > 100)
                return -ERRNO_TO_PARA_ERROR(EINVAL);
        if (percent == 100)
-               return com_next(pt, 1, (char *[]){"next", NULL});
+               return com_next(NULL);
        if (pt->playing && !pt->fn.btrn)
                return 0;
        pt->start_chunk = percent * pt->num_chunks / 100;
        if (!pt->playing)
                return 0;
        pt->rq = CRT_REPOS;
-       kill_stream(pt);
+       kill_stream();
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(jmp);
 
-static int com_ff(struct play_task *pt, int argc, char **argv)
+static int com_ff(struct lls_parse_result *lpr)
 {
        int32_t seconds;
+       char *errctx;
        int ret;
 
-       if (argc != 2)
-               return -E_PLAY_SYNTAX;
-       ret = para_atoi32(argv[1], &seconds);
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       ret = para_atoi32(lls_input(0, lpr), &seconds);
        if (ret < 0)
                return ret;
        if (pt->playing && !pt->fn.btrn)
                return 0;
-       seconds += get_play_time(pt);
+       seconds += get_play_time();
        seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
        seconds = PARA_MAX(seconds, 0);
        pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
@@ -974,49 +1009,52 @@ static int com_ff(struct play_task *pt, int argc, char **argv)
        if (!pt->playing)
                return 0;
        pt->rq = CRT_REPOS;
-       kill_stream(pt);
+       kill_stream();
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(ff);
 
-static int run_command(char *line, struct play_task *pt)
+static int run_command(char *line)
 {
-       int i, ret, argc;
+       int ret, argc;
        char **argv = NULL;
+       char *errctx = NULL;
+       const struct play_command_info *pci;
+       struct lls_parse_result *lpr;
+       const struct lls_command *cmd;
 
-       attach_stdout(pt, __FUNCTION__);
+       attach_stdout(__FUNCTION__);
        ret = create_argv(line, " ", &argv);
-       if (ret < 0) {
-               PARA_ERROR_LOG("parse error: %s\n", para_strerror(-ret));
-               return 0;
-       }
+       if (ret < 0)
+               goto out;
        if (ret == 0)
                goto out;
        argc = ret;
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(pp_cmds[i].name, argv[0]))
-                       continue;
-               ret = pp_cmds[i].handler(pt, argc, argv);
-               if (ret < 0)
-                       PARA_WARNING_LOG("%s: %s\n", pt->background?
-                               "" : argv[0], para_strerror(-ret));
-               ret = 1;
+       ret = lls(lls_lookup_subcmd(argv[0], play_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       PARA_WARNING_LOG("invalid command: %s\n", argv[0]);
-       ret = 0;
+       cmd = lls_cmd(ret, play_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       pci = lls_user_data(cmd);
+       ret = pci->handler(lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
        free_argv(argv);
        return ret;
 }
 
 static int play_i9e_line_handler(char *line)
 {
-       return run_command(line, &play_task);
+       return run_command(line);
 }
 
 static int play_i9e_key_handler(int key)
 {
-       struct play_task *pt = &play_task;
        int idx = get_key_map_idx(key);
        char *seq = get_key_map_seq(key);
        char *cmd = get_key_map_cmd(key);
@@ -1025,7 +1063,7 @@ static int play_i9e_key_handler(int key)
        PARA_NOTICE_LOG("pressed %d: %s key #%d (%s -> %s)\n",
                key, internal? "internal" : "user-defined",
                idx, seq, cmd);
-       run_command(cmd, pt);
+       run_command(cmd);
        free(seq);
        free(cmd);
        pt->next_update = *now;
@@ -1042,7 +1080,7 @@ static struct i9e_client_info ici = {
 
 static void sigint_handler(int sig)
 {
-       play_task.background = true;
+       pt->background = true;
        i9e_signal_dispatch(sig);
 }
 
@@ -1051,15 +1089,15 @@ 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(struct play_task *pt)
+static void session_open(void)
 {
        int ret;
        char *history_file;
        struct sigaction act;
 
        PARA_NOTICE_LOG("\n%s\n", version_text("play"));
-       if (conf.history_file_given)
-               history_file = para_strdup(conf.history_file_arg);
+       if (OPT_GIVEN(HISTORY_FILE))
+               history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
        else {
                char *home = para_homedir();
                history_file = make_message("%s/.paraslash/play.history",
@@ -1095,7 +1133,7 @@ out:
        exit(EXIT_FAILURE);
 }
 
-static void session_update_time_string(struct play_task *pt, char *str, unsigned len)
+static void session_update_time_string(char *str, unsigned len)
 {
        if (pt->background)
                return;
@@ -1120,30 +1158,30 @@ 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 play_task *pt)
+static int session_post_select(__a_unused struct sched *s)
 {
        int ret;
 
        if (pt->background)
-               detach_stdout(pt);
+               detach_stdout();
        else
-               attach_stdout(pt, __FUNCTION__);
+               attach_stdout(__FUNCTION__);
        ret = i9e_get_error();
        if (ret < 0) {
-               kill_stream(pt);
+               kill_stream();
                i9e_close();
                para_log = stderr_log;
                free(ici.history_file);
                return ret;
        }
-       if (get_playback_state(pt) == 'X')
+       if (get_playback_state() == 'X')
                i9e_signal_dispatch(SIGTERM);
        return 0;
 }
 
 #else /* HAVE_READLINE */
 
-static int session_post_select(struct sched *s, struct play_task *pt)
+static int session_post_select(struct sched *s)
 {
        char c;
 
@@ -1151,38 +1189,36 @@ static int session_post_select(struct sched *s, struct play_task *pt)
                return 0;
        if (read(STDIN_FILENO, &c, 1))
                do_nothing;
-       kill_stream(pt);
+       kill_stream();
        return 1;
 }
 
-static void session_open(__a_unused struct play_task *pt)
+static void session_open(void)
 {
 }
 
-static void session_update_time_string(__a_unused struct play_task *pt,
-               char *str, __a_unused unsigned len)
+static void session_update_time_string(char *str, __a_unused unsigned len)
 {
        printf("\r%s     ", str);
        fflush(stdout);
 }
 #endif /* HAVE_READLINE */
 
-static void play_pre_select(struct sched *s, void *context)
+static void play_pre_select(struct sched *s, __a_unused void *context)
 {
-       struct play_task *pt = context;
        char state;
 
        para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
-       state = get_playback_state(pt);
+       state = get_playback_state();
        if (state == 'R' || state == 'F' || state == 'X')
                return sched_min_delay(s);
        sched_request_barrier_or_min_delay(&pt->next_update, s);
 }
 
-static unsigned get_time_string(struct play_task *pt, char **result)
+static unsigned get_time_string(char **result)
 {
        int seconds, length;
-       char state = get_playback_state(pt);
+       char state = get_playback_state();
 
        /* do not return anything if things are about to change */
        if (state != 'P' && state != 'U') {
@@ -1192,7 +1228,7 @@ static unsigned get_time_string(struct play_task *pt, char **result)
        length = pt->seconds;
        if (length == 0)
                return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
-       seconds = get_play_time(pt);
+       seconds = get_play_time();
        return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
                pt->current_file,
                seconds / 60,
@@ -1202,28 +1238,27 @@ static unsigned get_time_string(struct play_task *pt, char **result)
                length? (seconds * 100 + length / 2) / length : 0,
                length / 60,
                length % 60,
-               conf.inputs[pt->current_file]
+               get_playlist_file(pt->current_file)
        );
 }
 
-static int play_post_select(struct sched *s, void *context)
+static int play_post_select(struct sched *s, __a_unused void *context)
 {
-       struct play_task *pt = context;
        int ret;
 
-       ret = eof_cleanup(pt);
+       ret = eof_cleanup();
        if (ret < 0) {
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
-       ret = session_post_select(s, pt);
+       ret = session_post_select(s);
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
-               char state = get_playback_state(pt);
+               char state = get_playback_state();
                if (state == 'P' || state == 'R' || state == 'F') {
                        PARA_NOTICE_LOG("state: %c\n", state);
-                       ret = load_next_file(pt);
+                       ret = load_next_file();
                        if (ret < 0) {
                                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                                pt->rq = CRT_TERM_RQ;
@@ -1235,10 +1270,10 @@ static int play_post_select(struct sched *s, void *context)
        }
        if (tv_diff(now, &pt->next_update, NULL) >= 0) {
                char *str;
-               unsigned len = get_time_string(pt, &str);
+               unsigned len = get_time_string(&str);
                struct timeval delay = {.tv_sec = 0, .tv_usec = 100 * 1000};
                if (str && len > 0)
-                       session_update_time_string(pt, str, len);
+                       session_update_time_string(str, len);
                free(str);
                tv_add(now, &delay, &pt->next_update);
        }
@@ -1258,26 +1293,20 @@ out:
 int main(int argc, char *argv[])
 {
        int ret;
-       struct play_task *pt = &play_task;
+       unsigned num_inputs;
 
        /* needed this early to make help work */
        recv_init();
-       filter_init();
-       writer_init();
 
        sched.default_timeout.tv_sec = 5;
-
        parse_config_or_die(argc, argv);
-       if (conf.inputs_num == 0)
-               print_help_and_die();
-       check_afh_receiver_or_die();
-
-       session_open(pt);
-       if (conf.randomize_given)
-               shuffle(conf.inputs, conf.inputs_num);
-       pt->invalid = para_calloc(sizeof(*pt->invalid) * conf.inputs_num);
+       AFH_RECV->init();
+       session_open();
+       num_inputs = lls_num_inputs(play_lpr);
+       init_shuffle_map();
+       pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
        pt->rq = CRT_FILE_CHANGE;
-       pt->current_file = conf.inputs_num - 1;
+       pt->current_file = num_inputs - 1;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
diff --git a/play.cmd b/play.cmd
deleted file mode 100644 (file)
index 459ad8c..0000000
--- a/play.cmd
+++ /dev/null
@@ -1,77 +0,0 @@
-BN: play
-SF: play.c
-SN: list of commands
----
-N: help
-D: Display command list or help for given command.
-U: help [command]
-H: This command acts differently depending on whether it is executed in command
-H: mode or in insert mode.  In command mode, the list of keybindings is printed.
-H: In insert mode, if no command is given, the list of commands is shown.
-H: Otherwise, the help for the given command is printed.
----
-N: next
-D: Load next file.
-U: next
-H: Closes the current file and loads the next file of the playlist.
----
-N: prev
-D: Load previous file.
-U: prev
-H: Closes the current file and loads the previous file of the playlist.
----
-N: fg
-D: Enter command mode.
-U: fg
-H: In this mode, file name and play time are displayed.  Hit CTRL+C to switch to
-H: input mode.
----
-N: bg
-D: Enter input mode.
-U: bg
-H: Only useful if called in command mode via a key binding. The default key
-H: bindings map this command to the colon key, so pressing : in command mode
-H: activates insert mode.
----
-N: jmp
-D: Jump to position in current file.
-U: jmp <percent>
-H: The <percent> argument should be an integer between 0 and 100.
----
-N: ff
-D: Jump forwards or backwards.
-U: ff <seconds>
-H: Negative values mean to jmp backwards the given amount of seconds.
----
-N: ls
-D: List playlist.
-U: ls
-H: This prints all paths of the playlist. The currently active file is
-H: marked with an asterisk.
----
-N: info
-D: Print information about the current file.
-U: info
-H: This is the audio file selector info.
----
-N: play
-D: Start or resume playing.
-U: play [<num>]
-H: Without <num>, starts playing at the current position. Otherwise, the
-H: corresponding file is loaded and playback is started.
----
-N: pause
-D: Stop playing.
-U: pause
-H: When paused, it is still possible to jump around in the file via the jmp and ff
-H: comands.
----
-N: tasks
-D: Print list of active tasks.
-U: tasks
-H: Mainly useful for debugging.
----
-N: quit
-D: Exit para_play.
-U: quit
-H: Pressing CTRL+D causes EOF on stdin which also exits para_play.
index 8ea1854bddfc004db52d6d0afe577d6601960b07..55a83436bb366ccdb280df9bf90bc025640004c9 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
@@ -139,7 +140,7 @@ int playlist_check_callback(struct afs_callback_arg *aca)
 /**
  * Close the current playlist.
  *
- * \sa playlist_open().
+ * \sa \ref playlist_open().
  */
 void playlist_close(void)
 {
index 6078da0757f885eec2fec0dcb9fbda88cb99f454..8ca1630d8112242ee65b3feb63e2883ffdac7f86 100644 (file)
@@ -7,12 +7,12 @@
 /** \file prebuffer_filter.c Paraslash's prebuffering filter. */
 
 #include <regex.h>
+#include <lopsub.h>
 
 #include "para.h"
-#include "prebuffer_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -20,8 +20,6 @@
 
 /** Data specific to the prebuffer filter. */
 struct private_prebuffer_data {
-       /** The configuration data for this instance of the filter. */
-       struct prebuffer_filter_args_info *conf;
        /** Number of bytes prebuffered or -1 if no longer prebuffering. */
        int prebuffered;
        /** End of prebuffering period. */
@@ -34,16 +32,16 @@ static void prebuffer_pre_select(struct sched *s, void *context)
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
-       struct prebuffer_filter_args_info *conf = ppd->conf;
        struct timeval diff;
 
        if (iqs == 0)
                return;
        if (ppd->barrier.tv_sec == 0) {
+               uint32_t duration = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER,
+                       DURATION, fn->lpr);
                struct timeval tv;
-               PARA_INFO_LOG("prebuffer period %dms\n",
-                       conf->duration_arg);
-               ms2tv(conf->duration_arg, &tv);
+               PARA_INFO_LOG("prebuffer period %" PRIu32 "ms\n", duration);
+               ms2tv(duration, &tv);
                tv_add(&tv, now, &ppd->barrier);
        }
        if (tv_diff(&ppd->barrier, now, &diff) < 0)
@@ -62,66 +60,27 @@ static int prebuffer_post_select(__a_unused struct sched *s, void *context)
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
-       struct prebuffer_filter_args_info *conf = ppd->conf;
+       uint32_t size = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, SIZE, fn->lpr);
 
        if (ppd->barrier.tv_sec == 0)
                return 0;
        if (tv_diff(now, &ppd->barrier, NULL) < 0)
                return 0;
-       if (iqs < conf->size_arg)
+       if (iqs < size)
                return 0;
        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;
-
-       prebuffer_filter_cmdline_parser(argc, argv, conf);
-       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-       if (conf->duration_arg < 0)
-               goto err;
-       if (conf->size_arg < 0)
-               goto err;
-       PARA_NOTICE_LOG("prebuffering %ims, %i bytes\n", conf->duration_arg,
-               conf->size_arg);
-       *config = conf;
-       return 1;
-err:
-       free(conf);
-       return ret;
-}
-
 static void prebuffer_open(struct filter_node *fn)
 {
        struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
-
-       ppd->conf = fn->conf;
        fn->private_data = ppd;
 }
 
-static void prebuffer_free_config(void *conf)
-{
-       prebuffer_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the prebuffer filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void prebuffer_filter_init(struct filter *f)
-{
-       struct prebuffer_filter_args_info dummy;
-
-       prebuffer_filter_cmdline_parser_init(&dummy);
-       f->open = prebuffer_open;
-       f->close = prebuffer_close;
-       f->parse_config = prebuffer_parse_config;
-       f->free_config = prebuffer_free_config;
-       f->pre_select = prebuffer_pre_select;
-       f->post_select = prebuffer_post_select;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter);
-}
+const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
+       .open = prebuffer_open,
+       .close = prebuffer_close,
+       .pre_select = prebuffer_pre_select,
+       .post_select = prebuffer_post_select,
+};
diff --git a/recv.c b/recv.c
index 9de3033fee92925a4cd1446c8895f59055c82486..26aa7fd7b8a7f650f52082f2f7a1d9b9cef951bf 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -8,14 +8,15 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
+#include "recv.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "recv.cmdline.h"
 #include "fd.h"
 #include "string.h"
 #include "error.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},
-DEFINE_RECEIVER_ARRAY;
-
-/** The gengetopt args info struct. */
-static struct recv_args_info conf;
+#define CMD_PTR (lls_cmd(0, recv_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_RECV_PARA_RECV_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
+#define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
 
 static int loglevel;
 /** Always log to stderr. */
 INIT_STDERR_LOGGING(loglevel);
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(recv);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
-       exit(0);
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_receiver_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+       exit(EXIT_SUCCESS);
 }
 
 /**
@@ -60,41 +66,42 @@ __noreturn static void print_help_and_die(void)
  */
 int main(int argc, char *argv[])
 {
-       int ret, r_opened = 0, receiver_num;
-       struct receiver *r = NULL;
+       int ret;
+       const struct receiver *r = NULL;
        struct receiver_node rn;
        struct stdout_task sot = {.btrn = NULL};
        static struct sched s;
        struct task_info ti;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr; /* command line */
+       struct lls_parse_result *receiver_lpr; /* receiver specific options */
+       char *errctx;
 
-       recv_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("recv", conf.version_given);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
        recv_init();
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-
        memset(&rn, 0, sizeof(struct receiver_node));
-       rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num);
-       if (!rn.conf) {
-               PARA_EMERG_LOG("invalid receiver specifier\n");
-               ret = -E_RECV_SYNTAX;
-               goto out;
-       }
-       r = &receivers[receiver_num];
+       ret = check_receiver_arg(OPT_STRING_VAL(RECEIVER, lpr), &receiver_lpr);
+       if (ret < 0)
+               goto free_lpr;
+       cmd = lls_cmd(ret, recv_cmd_suite);
+       r = lls_user_data(cmd);
        rn.receiver = r;
+       rn.lpr = receiver_lpr;
        rn.btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = r->name));
+               EMBRACE(.name = lls_command_name(cmd)));
        ret = r->open(&rn);
        if (ret < 0)
-               goto out;
-       r_opened = 1;
-
+               goto remove_btrn;
        sot.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.parent = rn.btrn, .name = "stdout"));
        stdout_task_register(&sot, &s);
 
-       ti.name = r->name;
+       ti.name = lls_command_name(cmd);
        ti.pre_select = r->pre_select;
        ti.post_select = r->post_select;
        ti.context = &rn;
@@ -104,15 +111,19 @@ int main(int argc, char *argv[])
        s.default_timeout.tv_usec = 0;
        ret = schedule(&s);
        sched_shutdown(&s);
-out:
-       if (r_opened)
-               r->close(&rn);
-       btr_remove_node(&rn.btrn);
+       r->close(&rn);
        btr_remove_node(&sot.btrn);
-       if (rn.conf)
-               r->free_config(rn.conf);
-
-       if (ret < 0)
+remove_btrn:
+       btr_remove_node(&rn.btrn);
+       lls_free_parse_result(receiver_lpr, cmd);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR);
+out:
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       }
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/recv.h b/recv.h
index 1a0de659e92be1d6f68838c3408b8859c4278a43..6fb70b9a70897e65232732a71194f666236b5a1c 100644 (file)
--- a/recv.h
+++ b/recv.h
  */
 struct receiver_node {
        /** Points to the corresponding receiver. */
-       struct receiver *receiver;
+       const struct receiver *receiver;
        /** Receiver-specific data. */
        void *private_data;
-       /** Pointer to the configuration data for this instance. */
-       void *conf;
+       /** The parsed command line options for this instance. */
+       struct lls_parse_result *lpr;
        /** The task associated with this instance. */
        struct task *task;
        /** The receiver node is always the root of the buffer tree. */
@@ -40,48 +40,23 @@ struct receiver_node {
 /**
  * Describes one supported paraslash receiver.
  *
- * \sa http_recv.c, udp_recv.c
+ * \sa \ref http_recv.c, \ref udp_recv.c.
  */
 struct receiver {
        /**
-        * The name of the receiver.
-        */
-       const char *name;
-       /**
-        * The receiver init function.
-        *
-        * It must fill in all other function pointers and is assumed to succeed.
-        *
-        * \sa http_recv_init udp_recv_init.
-        */
-       void (*init)(struct receiver *r);
-       /**
-        * The command line parser of the receiver.
-        *
-        * It should check whether the command line options given by \a argc
-        * and \a argv are valid.  On success, it should return a pointer to
-        * the receiver-specific configuration data determined by \a argc and
-        * \a argv.  Note that this might be called more than once with
-        * different values of \a argc and \a argv.
-        */
-       void *(*parse_config)(int argc, char **argv);
-       /**
-        * Deallocate the configuration structure of a receiver node.
+        * The optional receiver init function.
         *
-        * This calls the receiver-specific cleanup function generated by
-        * gengetopt.
+        * Performs any initialization needed before the receiver can be opened.
         */
-       void (*free_config)(void *conf);
+       void (*init)(void);
        /**
         * Open one instance of the receiver.
         *
-        * This should allocate the output buffer of \a rn. and may also
-        * perform any other work necessary for retrieving the stream according
-        * to the configuration stored in the \a conf member of \a rn which is
-        * guaranteed to point to valid configuration data (as previously
-        * obtained from the config parser).
+        * This should allocate the output buffer of the given receiver node
+        * and prepare it for retrieving the audio stream according to the
+        * configuration stored in rn->lpr.
         *
-        * \sa receiver_node::conf, receiver_node::buf.
+        * \sa struct \ref receiver_node.
         */
        int (*open)(struct receiver_node *rn);
        /**
@@ -90,69 +65,60 @@ struct receiver {
         * It should free all resources associated with given receiver node
         * that were allocated during the corresponding open call.
         *
-        * \sa receiver_node.
+        * \sa \ref receiver_node.
         */
        void (*close)(struct receiver_node *rn);
        /**
         * Add file descriptors to fd_sets and compute timeout for select(2).
         *
-        * The pre_select function gets called from the driving application
-        * before entering its select loop. The receiver may use this hook to
-        * add any file descriptors to the sets of file descriptors given by \a
-        * s.
+        * If this is not NULL, the function is called in each iteration of the
+        * scheduler's select loop. The receiver may define it to add file
+        * descriptors to the file descriptor sets given by s. Those will be
+        * monitored in the subsequent call to select(2). The function may also
+        * lower the timeout value of s to make select(2) return earlier if no
+        * file descriptors are ready for I/O.
         *
-        * \sa select(2), time.c struct task, struct sched.
+        * \sa select(2), \ref time.c, struct \ref sched.
         */
        void (*pre_select)(struct sched *s, void *context);
        /**
-        * Evaluate the result from select().
+        * Evaluate the result from select(2).
         *
-        * This hook gets called after the call to select(). It should check
-        * all file descriptors which were added to any of the fd sets during
-        * the previous call to pre_select. According to the result, it may
-        * then use any non-blocking I/O to establish a connection or to
-        * receive the audio data.
+        * This is called after the call to select(2). It should check all file
+        * descriptors which were added to any of the fd sets in the previous
+        * call to ->pre_select() and perform (non-blocking) I/O operations on
+        * those fds which are ready for I/O, for example in order to establish
+        * a connection or to receive a part of the audio stream.
         *
-        * \sa select(2), struct receiver.
+        * \sa select(2), struct \ref receiver.
         */
        int (*post_select)(struct sched *s, void *context);
-
-       /** The two help texts of this receiver. */
-       struct ggo_help help;
        /**
         * Answer a buffer tree query.
         *
-        * This optional function pointer is used for inter node communications
+        * This optional function pointer allows for inter node communication
         * of the buffer tree nodes. See \ref btr_command_handler for details.
         */
        btr_command_handler execute;
 };
 
-/** Define an array of all available receivers. */
-#define DEFINE_RECEIVER_ARRAY struct receiver receivers[] = { \
-       HTTP_RECEIVER \
-       DCCP_RECEIVER \
-       UDP_RECEIVER \
-       AFH_RECEIVER \
-       {.name = NULL}};
+#define RECV_CMD(_num) (lls_cmd(_num, recv_cmd_suite))
+
+#define RECV_CMD_OPT_RESULT(_recv, _opt, _lpr) \
+       (lls_opt_result(LSG_RECV_CMD_ ## _recv ## _OPT_ ## _opt, _lpr))
+#define RECV_CMD_OPT_GIVEN(_recv, _opt, _lpr) \
+       (lls_opt_given(RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_STRING_VAL(_recv, _opt, _lpr) \
+       (lls_string_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_UINT32_VAL(_recv, _opt, _lpr) \
+       (lls_uint32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_INT32_VAL(_recv, _opt, _lpr) \
+       (lls_int32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
 
 /** Iterate over all available receivers. */
-#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++)
+#define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++)
 
 void recv_init(void);
-void *check_receiver_arg(char *ra, int *receiver_num);
-void print_receiver_helps(unsigned flags);
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
+void print_receiver_helps(bool detailed);
 int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
-
-/** \cond receiver */
-extern void http_recv_init(struct receiver *r);
-#define HTTP_RECEIVER {.name = "http", .init = http_recv_init},
-extern void dccp_recv_init(struct receiver *r);
-#define DCCP_RECEIVER {.name = "dccp", .init = dccp_recv_init},
-extern void udp_recv_init(struct receiver *r);
-#define UDP_RECEIVER {.name = "udp", .init = udp_recv_init},
-#define AFH_RECEIVER /* not active by default */
-
-extern struct receiver receivers[];
-/** \endcond receiver */
-
index 59630dfcc5f5f6fc5cfa973fd25c4476366b6b92..1ad84438026ce8f724ddf8d1fba8547a96276fb6 100644 (file)
 /** \file recv_common.c common functions of para_recv and para_audiod */
 
 #include <regex.h>
+#include <inttypes.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
+#include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
 #include "string.h"
 
 /**
  * Call the init function of each paraslash receiver.
+ *
+ * Receivers employ the user_data feature of the lopsub library: Each receiver
+ * of the recv_cmd suite defines a struct receiver as its user data.
+ * recv_init() obtains a pointer to this structure by calling lls_user_data().
+ * If the receiver has an init function (i.e., if ->init is not NULL), ->init()
+ * is called to initialize the receiver.
  */
 void recv_init(void)
 {
        int i;
 
-       FOR_EACH_RECEIVER(i)
-               receivers[i].init(&receivers[i]);
-}
-
-static void *parse_receiver_args(int receiver_num, char *options)
-{
-       struct receiver *r = &receivers[receiver_num];
-       char **argv;
-       int argc;
-       void *conf;
-
-       if (options) {
-               argc = create_shifted_argv(options, " \t", &argv);
-               if (argc < 0)
-                       return NULL;
-       } else {
-               argc = 1;
-               argv = para_malloc(2 * sizeof(char*));
-               argv[1] = NULL;
+       FOR_EACH_RECEIVER(i) {
+               const struct lls_command *cmd = RECV_CMD(i);
+               const struct receiver *r = lls_user_data(cmd);
+               if (r && r->init)
+                       r->init();
        }
-       argv[0] = make_message("%s_recv", r->name);
-       conf = r->parse_config(argc, argv);
-       free_argv(argv);
-       return conf;
 }
 
 /**
  * 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
+ * \param ra string of the form receiver_name [options...]
+ * \param lprp Filled in on success, undefined else.
  *
  * 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 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.
+ * If a NULL pointer or an empty string is passed as the first argument, the
+ * hhtp receiver with no options is assumed.
+ *
+ * \return On success the number of the receiver is returned. On errors, the
+ * function calls exit(EXIT_FAILURE).
  */
-void *check_receiver_arg(char *ra, int *receiver_num)
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp)
 {
-       int j;
+       int ret, argc, receiver_num;
+       char *errctx = NULL, **argv;
+       const struct lls_command *cmd;
 
-       PARA_DEBUG_LOG("checking %s\n", ra);
-       for (j = 0; receivers[j].name; j++) {
-               const char *name = receivers[j].name;
-               size_t len = strlen(name);
-               char c;
-               if (strlen(ra) < len)
-                       continue;
-               if (strncmp(name, ra, len))
-                       continue;
-               c = ra[len];
-               if (c && c != ' ')
-                       continue;
-               if (c && !receivers[j].parse_config)
-                       return NULL;
-               *receiver_num = j;
-               return parse_receiver_args(j, c? ra + len + 1: NULL);
+       *lprp = NULL;
+       if (!ra || !*ra) {
+               argc = 1;
+               argv = para_malloc(2 * sizeof(char*));
+               argv[0] = para_strdup("http");
+               argv[1] = NULL;
+       } else {
+               ret = create_argv(ra, " \t\n", &argv);
+               if (ret < 0) {
+                       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+                       exit(EXIT_FAILURE);
+               }
+               argc = ret;
        }
-       PARA_ERROR_LOG("receiver not found\n");
-       return NULL;
+       ret = lls(lls_lookup_subcmd(argv[0], recv_cmd_suite, &errctx));
+       if (ret < 0) {
+               PARA_EMERG_LOG("%s: %s\n", errctx? errctx : argv[0],
+                       para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       receiver_num = ret;
+       cmd = RECV_CMD(receiver_num);
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       ret = receiver_num;
+       free_argv(argv);
+       return ret;
 }
 
 /**
  * Print out the help texts to all receivers.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
  */
-void print_receiver_helps(unsigned flags)
+void print_receiver_helps(bool detailed)
 {
        int i;
 
-       printf_or_die("\nAvailable receivers: ");
-       FOR_EACH_RECEIVER(i)
-               printf_or_die("%s%s", i? " " : "", receivers[i].name);
-       printf_or_die("\n");
+       printf("\nAvailable receivers: ");
+       FOR_EACH_RECEIVER(i) {
+               const struct lls_command *cmd = RECV_CMD(i);
+               printf("%s%s", i? " " : "", lls_command_name(cmd));
+       }
+       printf("\n\n");
        FOR_EACH_RECEIVER(i) {
-               struct receiver *r = receivers + i;
-               if (!r->help.short_help)
+               const struct lls_command *cmd = RECV_CMD(i);
+               char *help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
                        continue;
-               printf_or_die("\n%s: %s", r->name,
-                       r->help.purpose);
-               ggo_print_help(&r->help, flags);
+               printf("%s\n", help);
+               free(help);
        }
 }
 
index 1699ed2c03afcc84cc7b8b3e2e8a7f91aca13cc5..72a1864eeafb69982cc1f0e49a1013eb81872341 100644 (file)
@@ -8,20 +8,24 @@
 
 #include <regex.h>
 #include <samplerate.h>
+#include <lopsub.h>
 
-#include "resample_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "check_wav.h"
 
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(RESAMPLE, _opt, _lpr))
+#define OPT_GIVEN(_opt, _lpr) (FILTER_CMD_OPT_GIVEN(RESAMPLE, _opt, _lpr))
+
+/* effective values, may differ from config arg */
 struct resample_context {
-       int channels;
+       uint32_t channels;
        int source_sample_rate;
        float ratio;
        SRC_STATE *src_state;
@@ -32,10 +36,8 @@ static int resample_execute(struct btr_node *btrn, const char *cmd, char **resul
 {
        struct filter_node *fn = btr_context(btrn);
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
-
-       return decoder_execute(cmd, conf->dest_sample_rate_arg, ctx->channels,
-               result);
+       uint32_t dsr = U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr);
+       return decoder_execute(cmd, dsr, ctx->channels, result);
 }
 
 static void resample_close(struct filter_node *fn)
@@ -54,13 +56,12 @@ static void resample_close(struct filter_node *fn)
 static void resample_open(struct filter_node *fn)
 {
        struct resample_context *ctx = para_calloc(sizeof(*ctx));
-       struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
        struct wav_params wp;
 
        fn->private_data = ctx;
        fn->min_iqs = 2;
-       COPY_WAV_PARMS(&wp, conf);
+       LLS_COPY_WAV_PARMS(&wp, LSG_FILTER_CMD_RESAMPLE, fn->lpr);
        ctx->cwc = check_wav_init(btr_parent(btrn), btrn, &wp, NULL);
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
@@ -95,18 +96,17 @@ static int resample_set_params(struct filter_node *fn)
 {
        int ret;
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
+       struct lls_parse_result *lpr = fn->lpr;
 
-       ctx->channels = conf->channels_arg;
-       if (!conf->channels_given) {
+       ctx->channels = U32_OPTVAL(CHANNELS, lpr);
+       if (!OPT_GIVEN(CHANNELS, lpr)) {
                ret = get_btr_val("channels", btrn);
                if (ret >= 0)
                        ctx->channels = ret;
        }
-
-       ctx->source_sample_rate = conf->sample_rate_arg;
-       if (!conf->sample_rate_given) {
+       ctx->source_sample_rate = U32_OPTVAL(SAMPLE_RATE, lpr);
+       if (!OPT_GIVEN(SAMPLE_RATE, lpr)) {
                ret = get_btr_val("sample_rate", btrn);
                if (ret >= 0)
                        ctx->source_sample_rate = ret;
@@ -114,44 +114,37 @@ static int resample_set_params(struct filter_node *fn)
        /* reject all sample formats except 16 bit signed, little endian */
        ret = get_btr_val("sample_format", btrn);
        if (ret >= 0 && ret != SF_S16_LE) {
-               const char *sample_formats[] = {SAMPLE_FORMATS};
+               const char * const sample_formats[] = {SAMPLE_FORMATS};
                PARA_ERROR_LOG("unsupported sample format: %s\n",
                        sample_formats[ret]);
                return -ERRNO_TO_PARA_ERROR(EINVAL);
        }
-       ctx->ratio = (float)conf->dest_sample_rate_arg / ctx->source_sample_rate;
+       ctx->ratio = U32_OPTVAL(DEST_SAMPLE_RATE, lpr)
+               / (float)ctx->source_sample_rate;
        return 1;
 }
 
 static int resample_init(struct filter_node *fn)
 {
-       int ret, converter;
+       int ret;
+       const uint32_t trafo[] = {
+               [RCT_BEST] = SRC_SINC_BEST_QUALITY,
+               [RCT_MEDIUM] = SRC_SINC_MEDIUM_QUALITY,
+               [RCT_FASTEST] = SRC_SINC_FASTEST,
+               [RCT_ZERO_ORDER_HOLD] = SRC_ZERO_ORDER_HOLD,
+               [RCT_LINEAR] = SRC_LINEAR
+       };
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
+       const struct lls_option *o_c = FILTER_CMD_OPT(RESAMPLE, CONVERTER);
+       uint32_t converter = U32_OPTVAL(CONVERTER, fn->lpr);
 
+       PARA_INFO_LOG("converter type: %s\n",
+               lls_enum_string_val(converter, o_c));
        ret = resample_set_params(fn);
        if (ret < 0)
                return ret;
-       switch (conf->converter_arg) {
-       case converter_arg_best:
-               converter = SRC_SINC_BEST_QUALITY;
-               break;
-       case converter_arg_medium:
-               converter = SRC_SINC_MEDIUM_QUALITY;
-               break;
-       case converter_arg_fastest:
-               converter = SRC_SINC_FASTEST;
-               break;
-       case converter_arg_zero_order_hold:
-               converter = SRC_ZERO_ORDER_HOLD;
-               break;
-       case converter_arg_linear:
-               converter = SRC_LINEAR;
-               break;
-       default:
-               assert(0);
-       }
-       ctx->src_state = src_new(converter, conf->channels_arg, &ret);
+       ctx->src_state = src_new(trafo[converter],
+               U32_OPTVAL(CHANNELS, fn->lpr), &ret);
        if (!ctx->src_state) {
                PARA_ERROR_LOG("%s\n", src_strerror(ret));
                return -E_LIBSAMPLERATE;
@@ -201,7 +194,6 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
        int ret;
        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;
        int16_t *in, *out;
        size_t in_bytes, num_frames;
@@ -218,7 +210,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
                if (ret <= 0)
                        goto out;
        }
-       if (ctx->source_sample_rate == conf->dest_sample_rate_arg) {
+       if (ctx->source_sample_rate == U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr)) {
                /*
                 * No resampling necessary. We do not splice ourselves out
                 * though, since our children might want to ask us through the
@@ -251,58 +243,45 @@ out:
        return ret;
 }
 
-static int resample_parse_config(int argc, char **argv, void **config)
+static void *resample_setup(const struct lls_parse_result *lpr)
 {
-       int ret, val, given;
-       struct resample_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       resample_filter_cmdline_parser(argc, argv, conf);
+       int given;
+       uint32_t u32;
 
        /* sanity checks */
-       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-       val = conf->channels_arg;
-       given = conf->channels_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       val = conf->sample_rate_arg;
-       given = conf->sample_rate_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       val = conf->dest_sample_rate_arg;
-       given = conf->dest_sample_rate_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       *config = conf;
-       return 1;
-err:
-       free(conf);
-       return ret;
+       u32 = U32_OPTVAL(CHANNELS, lpr);
+       given = OPT_GIVEN(CHANNELS, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: zero channels?!\n");
+               exit(EXIT_FAILURE);
+       }
+       u32 = U32_OPTVAL(SAMPLE_RATE, lpr);
+       given = OPT_GIVEN(SAMPLE_RATE, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: input sample rate can not be 0\n");
+               exit(EXIT_FAILURE);
+       }
+       u32 = U32_OPTVAL(DEST_SAMPLE_RATE, lpr);
+       given = OPT_GIVEN(DEST_SAMPLE_RATE, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: destination sample rate can not be 0\n");
+               exit(EXIT_FAILURE);
+       }
+       return NULL;
 }
 
-static void resample_free_config(void *conf)
+static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
+               void *conf)
 {
-       if (!conf)
-               return;
-       resample_filter_cmdline_parser_free(conf);
        free(conf);
 }
 
-/**
- * The init function of the resample filter.
- *
- * \param f Structure to initialize.
- */
-void resample_filter_init(struct filter *f)
-{
-       struct resample_filter_args_info dummy;
-
-       resample_filter_cmdline_parser_init(&dummy);
-       f->close = resample_close;
-       f->open = resample_open;
-       f->pre_select = resample_pre_select;
-       f->post_select = resample_post_select;
-       f->parse_config = resample_parse_config;
-       f->free_config = resample_free_config;
-       f->execute = resample_execute;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter);
-}
+const struct filter lsg_filter_cmd_com_resample_user_data = {
+       .setup = resample_setup,
+       .open = resample_open,
+       .pre_select = resample_pre_select,
+       .post_select = resample_post_select,
+       .close = resample_close,
+       .teardown = resample_teardown,
+       .execute = resample_execute
+};
diff --git a/sched.c b/sched.c
index bc301778f9a8c20d567db31b1b19f4c413fc0ed3..297348a16e0c3e85ea683dccf681c2ecbf278a4b 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -387,7 +387,7 @@ void sched_min_delay(struct sched *s)
  * function does nothing. Otherwise the timeout for the next select() call is
  * set to the given value.
  *
- * \sa sched_request_timeout_ms().
+ * \sa \ref sched_request_timeout_ms().
  */
 void sched_request_timeout(struct timeval *to, struct sched *s)
 {
@@ -420,7 +420,7 @@ void sched_request_timeout_ms(long unsigned ms, struct sched *s)
  * \return If \a barrier is in the past, this function does nothing and returns
  * zero. Otherwise it returns one.
  *
- * \sa sched_request_barrier_or_min_delay().
+ * \sa \ref sched_request_barrier_or_min_delay().
  */
 int sched_request_barrier(struct timeval *barrier, struct sched *s)
 {
@@ -441,7 +441,7 @@ int sched_request_barrier(struct timeval *barrier, struct sched *s)
  * \return If \a barrier is in the past, this function requests a minimal
  * timeout and returns zero. Otherwise it returns one.
  *
- * \sa sched_min_delay(), sched_request_barrier().
+ * \sa \ref sched_min_delay(), \ref sched_request_barrier().
  */
 int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s)
 {
diff --git a/score.c b/score.c
index 81b3ded0021f41115dcc78a8f9605d722195c389..30761e7556274effe0867335822ab2dc1916e065 100644 (file)
--- a/score.c
+++ b/score.c
@@ -7,6 +7,7 @@
 /** \file score.c Scoring functions to determine the audio file streaming order. */
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
@@ -33,8 +34,6 @@ static int ptr_compare(const struct osl_object *obj1, const struct osl_object *o
  * This function first compares the score values as usual integers. If they compare as
  * equal, the address of \a obj1 and \a obj2 are compared. So this compare function
  * returns zero if and only if \a obj1 and \a obj2 point to the same memory area.
- *
- * \sa osl_compare_function.
  */
 static int score_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 {
@@ -91,8 +90,6 @@ static struct osl_table_description score_table_desc = {
  * \param num Result is returned here.
  *
  * \return Positive on success, negative on errors.
- *
- * \sa osl_get_num_rows().
  */
 int get_num_admissible_files(unsigned *num)
 {
@@ -283,7 +280,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
  * \return Positive on success, negative on errors. Possible errors:
  * Errors returned by osl_get_row() and osl_del_row().
  *
- * \sa score_add(), score_shutdown().
+ * \sa \ref score_add().
  */
 int score_delete(const struct osl_row *aft_row)
 {
diff --git a/send.h b/send.h
index 0c74f0ea93208ec6b24878c963e671a4da8adb8d..b70ba09452a5cf9abff66d8a0c6b40472fd5a502 100644 (file)
--- a/send.h
+++ b/send.h
@@ -25,7 +25,7 @@ enum sender_subcommand {
 /**
  * Describes one supported sender of para_server.
  *
- * \sa http_send.c udp_send.c, dccp_send.c.
+ * \sa \ref http_send.c \ref udp_send.c, \ref dccp_send.c.
  */
 struct sender {
        /** The name of the sender. */
@@ -182,10 +182,10 @@ struct sender_status {
 
 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);
+void init_sender_status(struct sender_status *ss,
+               const struct lls_opt_result *acl_opt_result, int port,
+               int max_clients, int default_deny);
 char *generic_sender_status(struct sender_status *ss, const char *name);
-
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
 void generic_com_deny(struct sender_command_data *scd,
index ec7ab67108334c85085b46b84ada1db45eb91a82..b03be8775d9f4e12841cb8353639dd9f70725a1f 100644 (file)
@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
@@ -71,7 +72,7 @@ static int open_sender(unsigned l4type, int port)
  * list, destroy the chunk queue of this client, delete the client from the
  * list of connected clients and free the sender_client struct.
  *
- * \sa shutdown_clients().
+ * \sa \ref shutdown_clients().
  */
 void shutdown_client(struct sender_client *sc, struct sender_status *ss)
 {
@@ -133,19 +134,33 @@ int send_queued_chunks(int fd, struct chunk_queue *cq)
  * Initialize a struct sender status.
  *
  * \param ss The struct to initialize.
- * \param access_arg The array of access arguments given at the command line.
- * \param num_access_args The number of elements in \a access_arg.
+ * \param acl_opt_result Contains array of --{http|dccp}-access arguments.
  * \param port The tcp or dccp port to listen on.
  * \param max_clients The maximal number of simultaneous connections.
  * \param default_deny Whether a blacklist should be used for access control.
  */
-void init_sender_status(struct sender_status *ss, char **access_arg,
-       int num_access_args, int port, int max_clients, int default_deny)
+void init_sender_status(struct sender_status *ss,
+               const struct lls_opt_result *acl_opt_result, int port,
+               int max_clients, int default_deny)
 {
+       int i;
+
        ss->listen_fd = -1;
        INIT_LIST_HEAD(&ss->client_list);
        ss->port = port;
-       acl_init(&ss->acl, access_arg, num_access_args);
+
+       /* Initialize an access control list */
+       INIT_LIST_HEAD(&ss->acl);
+       for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
+               const char *arg = lls_string_val(i, acl_opt_result);
+               char addr[16];
+               int mask;
+               if (!parse_cidr(arg, addr, sizeof(addr), &mask))
+                       PARA_WARNING_LOG("ACL syntax error: %s, ignoring\n",
+                               arg);
+               else
+                       acl_add_entry(&ss->acl, addr, mask);
+       }
        ss->num_clients = 0;
        ss->max_clients = max_clients;
        ss->default_deny = default_deny;
@@ -197,7 +212,7 @@ char *generic_sender_status(struct sender_status *ss, const char *name)
  * \param scd Contains the IP and the netmask.
  * \param ss The sender.
  *
- * \sa generic_com_deny().
+ * \sa \ref generic_com_deny().
  */
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss)
@@ -211,7 +226,7 @@ void generic_com_allow(struct sender_command_data *scd,
  * \param scd see \ref generic_com_allow().
  * \param ss see \ref generic_com_allow().
  *
- * \sa generic_com_allow().
+ * \sa \ref generic_com_allow().
  */
 void generic_com_deny(struct sender_command_data *scd,
                struct sender_status *ss)
@@ -247,7 +262,7 @@ int generic_com_on(struct sender_status *ss, unsigned protocol)
  *
  * \param ss The sender to deactivate.
  *
- * \sa \ref del_close_on_fork_list(), shutdown_clients().
+ * \sa \ref del_close_on_fork_list(), \ref shutdown_clients().
  */
 void generic_com_off(struct sender_status *ss)
 {
index a023b1526931c1eeeb3fcec7be3a6b6c72662c69..43ede2a91b752667ad4090e2742c4a844bf66250 100644 (file)
--- a/server.c
+++ b/server.c
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
-#include "server.cmdline.h"
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
@@ -62,7 +63,6 @@
 #include "signal.h"
 #include "user_list.h"
 #include "color.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
@@ -80,12 +80,15 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 struct misc_meta_data *mmd;
 
 /**
- * The configuration of para_server
+ * The active value for all config options of para_server.
  *
- * It also contains the options for the audio file selector, audio format
- * handler and all supported senders.
+ * It is computed by merging the parse result of the command line options with
+ * the parse result of the config file.
  */
-struct server_args_info conf;
+struct lls_parse_result *server_lpr = NULL;
+
+/* Command line options (no config file options). Used in handle_sighup(). */
+static struct lls_parse_result *cmdline_lpr;
 
 /** A random value used in child context for authentication. */
 uint32_t afs_socket_cookie;
@@ -93,9 +96,6 @@ uint32_t afs_socket_cookie;
 /** The mutex protecting the shared memory area containing the mmd struct. */
 int mmd_mutex;
 
-/** The file containing user information (public key, permissions). */
-static char *user_list_file = NULL;
-
 static struct sched sched;
 static struct signal_task *signal_task;
 
@@ -162,71 +162,97 @@ err_out:
 /**
  * (Re-)read the server configuration files.
  *
- * \param override Passed to gengetopt to activate the override feature.
+ * \param reload Whether config file overrides command line.
  *
- * This function also re-opens the logfile and sets the global \a
- * user_list_file variable.
+ * This function also re-opens the logfile and the user list. On SIGHUP it is
+ * called from both server and afs context.
  */
-void parse_config_or_die(int override)
+void parse_config_or_die(bool reload)
 {
-       char *home = para_homedir();
        int ret;
-       char *cf;
+       char *cf = NULL, *errctx = NULL, *user_list_file = NULL;
+       void *map;
+       size_t sz;
+       int cf_argc;
+       char **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
+       char *home = para_homedir();
 
        daemon_close_log();
-       if (conf.config_file_given)
-               cf = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else
                cf = make_message("%s/.paraslash/server.conf", home);
-       free(user_list_file);
-       if (!conf.user_list_given)
-               user_list_file = make_message("%s/.paraslash/server.users", home);
-       else
-               user_list_file = para_strdup(conf.user_list_arg);
-       ret = file_exists(cf);
-       if (conf.config_file_given && !ret)  {
-               ret = -1;
-               PARA_EMERG_LOG("can not read config file %s\n", cf);
-               goto out;
+       if (!mmd || getpid() != mmd->afs_pid) {
+               if (OPT_GIVEN(USER_LIST))
+                       user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST));
+               else
+                       user_list_file = make_message("%s/.paraslash/server.users", home);
        }
-       if (ret) {
-               int tmp = conf.daemon_given;
-               struct server_cmdline_parser_params params = {
-                       .override = override,
-                       .initialize = 0,
-                       .check_required = 1,
-                       .check_ambiguity = 0,
-                       .print_errors = !conf.daemon_given
-               };
-               server_cmdline_parser_config_file(cf, &conf, &params);
-               daemon_set_loglevel(conf.loglevel_arg);
-               conf.daemon_given = tmp;
+       free(home);
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+                       goto free_cf;
+               ret = 0;
+               server_lpr = cmdline_lpr;
+               goto success;
        }
-       if (conf.logfile_given) {
-               daemon_set_logfile(conf.logfile_arg);
+       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+       para_munmap(map, sz);
+       if (ret < 0)
+               goto free_cf;
+       cf_argc = ret;
+       ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+       lls_free_argv(cf_argv);
+       if (ret < 0)
+               goto free_cf;
+       if (reload) /* config file overrides command line */
+               ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       else /* command line options overrride config file options */
+               ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       lls_free_parse_result(cf_lpr, CMD_PTR);
+       if (ret < 0)
+               goto free_cf;
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(server_lpr, CMD_PTR);
+       server_lpr = merged_lpr;
+success:
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       if (OPT_GIVEN(LOGFILE)) {
+               daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        }
-
-       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
-                       conf.logfile_given)) {
+       if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+                       COLOR_NO, OPT_GIVEN(LOGFILE))) {
                int i;
-               for (i = 0; i < conf.log_color_given; i++)
-                       daemon_set_log_color_or_die(conf.log_color_arg[i]);
+               for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+                       daemon_set_log_color_or_die(lls_string_val(i,
+                               OPT_RESULT(LOG_COLOR)));
        }
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
        daemon_set_flag(DF_LOG_TIME);
-       if (conf.log_timing_given)
+       if (OPT_GIVEN(LOG_TIMING))
                daemon_set_flag(DF_LOG_TIMING);
+       daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+       if (user_list_file)
+               init_user_list(user_list_file);
        ret = 1;
-out:
+free_cf:
        free(cf);
-       free(home);
-       if (ret > 0)
-               return;
        free(user_list_file);
-       user_list_file = NULL;
-       exit(EXIT_FAILURE);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
 }
 
 /*
@@ -234,9 +260,9 @@ out:
  */
 static void handle_sighup(void)
 {
+
        PARA_NOTICE_LOG("SIGHUP\n");
-       parse_config_or_die(1); /* reopens log */
-       init_user_list(user_list_file); /* reload user list */
+       parse_config_or_die(true);
        if (mmd->afs_pid)
                kill(mmd->afs_pid, SIGHUP);
 }
@@ -369,7 +395,8 @@ static int command_post_select(struct sched *s, void *context)
         */
        for (i = sct->argc - 1; i >= 0; i--)
                memset(sct->argv[i], 0, strlen(sct->argv[i]));
-       sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
+       i = sct->argc - 1 - lls_num_inputs(cmdline_lpr);
+       sprintf(sct->argv[i], "para_server (serving %s)", peer_name);
        handle_connect(new_fd, peer_name);
        /* never reached*/
 out:
@@ -387,7 +414,7 @@ static void init_server_command_task(int argc, char **argv)
        PARA_NOTICE_LOG("initializing tcp command socket\n");
        sct->argc = argc;
        sct->argv = argv;
-       ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
+       ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT));
        if (ret < 0)
                goto err;
        sct->listen_fd = ret;
@@ -426,7 +453,8 @@ static int init_afs(int argc, char **argv)
 
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
-               sprintf(argv[0], "para_server (afs)");
+               i = argc - lls_num_inputs(cmdline_lpr) - 1;
+               sprintf(argv[i], "para_server (afs)");
                close(afs_server_socket[0]);
                afs_init(afs_socket_cookie, afs_server_socket[1]);
        }
@@ -445,45 +473,46 @@ static int init_afs(int argc, char **argv)
        return afs_server_socket[0];
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(server);
-       bool d = conf.detailed_help_given;
+       char *help;
+       bool d = OPT_GIVEN(DETAILED_HELP);
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (d)
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 static void server_init(int argc, char **argv)
 {
-       struct server_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
-       int afs_socket, daemon_pipe = -1;
+       int ret, afs_socket, daemon_pipe = -1;
+       char *errctx;
 
        valid_fd_012();
-       init_random_seed_or_die();
        /* parse command line options */
-       server_cmdline_parser_ext(argc, argv, &conf, &params);
-       daemon_set_loglevel(conf.loglevel_arg);
-       version_handle_flag("server", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       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);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       server_lpr = cmdline_lpr;
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+               OPT_STRING_VAL(GROUP));
+       version_handle_flag("server", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_or_die(false);
+       /* become daemon */
+       if (OPT_GIVEN(DAEMON))
+               daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
+       init_random_seed_or_die();
        daemon_log_welcome("server");
        init_ipc_or_die(); /* init mmd struct and mmd->lock */
        daemon_set_start_time();
-       init_user_list(user_list_file);
-       /* become daemon */
-       if (conf.daemon_given)
-               daemon_pipe = daemonize(true /* parent waits for us */);
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
 
@@ -491,16 +520,14 @@ static void server_init(int argc, char **argv)
         * Although afs uses its own signal handling we must ignore SIGUSR1
         * _before_ the afs child process gets born by init_afs() below.  It's
         * racy to do this in the child because the parent might send SIGUSR1
-        * before the child gets a chance to ignore this signal -- only the
-        * good die young.
-        */
-       para_sigaction(SIGUSR1, SIG_IGN);
-       /*
-        * We have to block SIGCHLD before the afs process is being forked off.
-        * Otherwise, para_server does not notice if afs dies before the
+        * before the child gets a chance to ignore this signal.
+        *
+        * We also have to block SIGCHLD before the afs process is created
+        * because otherwise para_server does not notice if afs dies before the
         * SIGCHLD handler has been installed for the parent process by
         * init_signal_task() below.
         */
+       para_sigaction(SIGUSR1, SIG_IGN);
        para_block_signal(SIGCHLD);
        PARA_NOTICE_LOG("initializing the audio file selector\n");
        afs_socket = init_afs(argc, argv);
@@ -517,6 +544,13 @@ static void server_init(int argc, char **argv)
                close(daemon_pipe);
        }
        PARA_NOTICE_LOG("server init complete\n");
+       return;
+fail:
+       assert(ret < 0);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 static void status_refresh(void)
@@ -572,9 +606,10 @@ int main(int argc, char *argv[])
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        sched_shutdown(&sched);
-       if (ret < 0) {
+       lls_free_parse_result(server_lpr, CMD_PTR);
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-               exit(EXIT_FAILURE);
-       }
-       exit(EXIT_SUCCESS);
+       exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
diff --git a/server.cmd b/server.cmd
deleted file mode 100644 (file)
index 5f46ba1..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-BN: server
-SF: command.c
-SN: list of server commands
----
-N: ff
-P: VSS_READ | VSS_WRITE
-D: Jump N seconds forward or backward.
-U: ff n[-]
-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:
-H:     para_client ff 30-
-H:
-H: jumps 30 seconds backwards.
----
-N: help
-P: 0
-D: Print online help.
-U: help [command]
-H: Without any arguments, help prints a list of available commands. When
-H: called with a command name as first argument, it prints the description
-H: of that command.
----
-N: hup
-P: VSS_WRITE
-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 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 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: Close the current audio file.
-U: next
-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 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
-D: Pause current audio file.
-U: pause
-H: Clear the 'P' (playing) bit of the vss status flags.
----
-N: play
-P: VSS_READ | VSS_WRITE
-D: Start or resume playing.
-U: play
-H: Set the 'P' (playing) bit of the vss status flags.
----
-N: sender
-P: VSS_READ | VSS_WRITE
-D: Control paraslash senders.
-U: sender [s cmd [arguments]]
-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: Show server and afs PID, number of connections, uptime and more.
----
-N: stat
-P: VSS_READ
-D: Print information about the current audio file.
-U: stat [-n=num] [-p]
-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 item identifiers are
-H: printed as numerical values.
----
-N: stop
-P: VSS_READ | VSS_WRITE
-D: Stop playing.
-U: stop
-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: Ask the server to terminate.
-U: term
-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 the git version string of para_server.
-U: version
-H: Show version and other info.
index 8de691ca321bd49db2d7c25398537f31b7d03a0c..0bfca305fc5b6465aec22db6b5b0c155a682a141 100644 (file)
--- a/server.h
+++ b/server.h
@@ -48,8 +48,6 @@ struct sender_command_data {
  * propagate to the stat command handlers.
  */
 struct misc_meta_data {
-       /** The size of the current audio file in bytes. */
-       size_t size;
        /** The "old" status flags -- commands may only read them. */
        unsigned int vss_status_flags;
        /** The new status flags -- commands may set them. */
@@ -89,9 +87,37 @@ struct misc_meta_data {
        struct audio_file_data afd;
 };
 
-/** Command line options for para_server. */
-extern struct server_args_info conf;
+extern struct lls_parse_result *server_lpr;
+
+/**
+ * Get a reference to the supercommand of para_server.
+ *
+ * This is needed for parsing the command line and for the ENUM_STRING_VAL()
+ * macro below. The latter macro is used in command.c, so CMD_PTR() can not
+ * be made local to server.c.
+ */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
+/** Get the parse result of an option to para_server. */
+#define OPT_RESULT(_name) (lls_opt_result( \
+               LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
+
+/** How many times a server option was given. */
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type string. */
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type uint32. */
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type int32. */
+#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
+
+/** Get the string which corresponds to an enum constant. */
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+       lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
 
 __noreturn void handle_connect(int fd, const char *peername);
-void parse_config_or_die(int override);
+void parse_config_or_die(bool reload);
 char *server_get_tasks(void);
index 644d287aaf6a4daba32dd023c9770691aa3f4d24..3266d090a8a8d24674963422b63883718cf3cfe8 100644 (file)
@@ -50,7 +50,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -308,16 +307,10 @@ fail:
        return ret;
 }
 
-/**
- * The init function of the ogg/speex decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void spxdec_filter_init(struct filter *f)
-{
-       f->open = spxdec_open;
-       f->close = speexdec_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = speexdec_post_select;
-       f->execute = speexdec_execute;
-}
+const struct filter lsg_filter_cmd_com_spxdec_user_data = {
+       .open = spxdec_open,
+       .close = speexdec_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = speexdec_post_select,
+       .execute = speexdec_execute,
+};
index 29db2b7e3e28f050d72577a6d8b533c18ed54be8..43e194dbca5d672936997ac97b94820892ca5a12 100644 (file)
--- a/stdout.c
+++ b/stdout.c
@@ -89,7 +89,7 @@ void stdout_task_register(struct stdout_task *sot, struct sched *s)
                .name = "stdout",
        };
 
-       /* See stdin.c for details. */
+       /* See \ref stdin.c for details. */
        ret = fcntl(STDOUT_FILENO, F_GETFL);
        if (ret < 0) {
                PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
index 6033a008dbf154953de673f85048eb78812d1722..e675502cf6707240a61be523c6fb510692991acd 100644 (file)
--- a/string.c
+++ b/string.c
@@ -10,7 +10,6 @@
 
 #include <pwd.h>
 #include <sys/utsname.h> /* uname() */
-#include <string.h>
 #include <regex.h>
 #include <langinfo.h>
 #include <wchar.h>
@@ -190,7 +189,7 @@ __printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...)
  * \return This function either returns a pointer to a string that must be
  * freed by the caller or aborts without returning.
  *
- * \sa printf(3), xasprintf().
+ * \sa printf(3), \ref xasprintf().
  */
 __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...)
 {
@@ -230,7 +229,7 @@ void freep(void *arg)
  * return \a a without making a copy of \a a.  Otherwise, construct the
  * concatenation \a c, free \a a (but not \a b) and return \a c.
  *
- * \sa strcat(3)
+ * \sa strcat(3).
  */
 __must_check __malloc char *para_strcat(char *a, const char *b)
 {
@@ -548,7 +547,7 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...)
  *
  * \return Standard.
  *
- * \sa para_atoi32(), strtol(3), atoi(3).
+ * \sa \ref para_atoi32(), strtol(3), atoi(3).
  */
 int para_atoi64(const char *str, int64_t *value)
 {
@@ -585,7 +584,7 @@ int para_atoi64(const char *str, int64_t *value)
  *
  * \return Standard.
  *
- * \sa para_atoi64().
+ * \sa \ref para_atoi64().
 */
 int para_atoi32(const char *str, int32_t *value)
 {
index 52f989417d8071a1f2ed33d99ce7fd8ba18d66c1..93bb6cba57ca6d1bee17358281ff774538793e39 100644 (file)
--- a/string.h
+++ b/string.h
@@ -20,7 +20,7 @@ struct para_buffer {
        size_t size;
        /** The maximal size this buffer may grow. Zero means unlimited. */
        size_t max_size;
-       /** \sa para_buffer_flags. */
+       /** \sa \ref para_buffer_flags. */
        unsigned flags;
        /** The next para_printf() will write at this offset. */
        size_t offset;
@@ -37,7 +37,7 @@ struct para_buffer {
 /**
  * Controls the behavior of for_each_line().
  *
- * \sa for_each_line().
+ * \sa \ref for_each_line().
  */
 enum for_each_line_flags {
        /** Activate read-only mode. */
index 82e86e90cf50c4bf77faac44183286c536caab26..efc10116f788a79bee5e2e75dfdfc0e036d06f1a 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.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"
@@ -42,7 +42,7 @@ struct sync_buddy {
        struct list_head node;
 };
 
-/* Allocated in ->open() */
+/* Allocated in ->open(), stored in fn->private_data */
 struct sync_filter_context {
        int listen_fd;
        struct list_head buddies;
@@ -50,12 +50,6 @@ struct sync_filter_context {
        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) \
@@ -90,59 +84,59 @@ static void sync_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static void sync_free_config(void *conf)
+static void sync_teardown(const struct lls_parse_result *lpr, void *conf)
 {
-       struct sync_filter_config *sfc = conf;
-       int i;
+       struct sync_buddy_info *sbi = conf;
+       int i, num_buddies = FILTER_CMD_OPT_GIVEN(SYNC, BUDDY, lpr);
 
-       for (i = 0; i < sfc->conf->buddy_given; i++) {
-               free(sfc->buddy_info[i].host);
-               freeaddrinfo(sfc->buddy_info[i].ai);
+       for (i = 0; i < num_buddies; i++) {
+               free(sbi[i].host);
+               freeaddrinfo(sbi[i].ai);
        }
-       sync_filter_cmdline_parser_free(sfc->conf);
-       free(sfc);
+       free(sbi);
 }
 
 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);
+       struct sync_buddy_info *sbi = fn->conf;
+       uint32_t port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, fn->lpr);
+       unsigned buddy_given;
+       const struct lls_opt_result *r_b;
 
        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,
+               port,
                NULL /* no flowopts */
        );
        if (ret < 0) {
-               PARA_ERROR_LOG("could not create UDP listening socket %d\n",
-                       sfc->conf->port_arg);
+               PARA_ERROR_LOG("could not create UDP listening socket %u\n",
+                       port);
                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];
+       r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, fn->lpr);
+       buddy_given = lls_opt_given(r_b);
+       for (i = 0; i < buddy_given; i++) {
                int fd;
+               const char *url = lls_string_val(i, r_b);
 
                /* make buddy udp socket from address info */
                assert(sbi->ai);
                ret = makesock_addrinfo(
                        IPPROTO_UDP,
                        false /* not passive */,
-                       sbi->ai,
+                       sbi[i].ai,
                        NULL /* no flowopts */
                );
                if (ret < 0) {
@@ -160,7 +154,7 @@ static void sync_open(struct filter_node *fn)
                }
                buddy = para_malloc(sizeof(*buddy));
                buddy->fd = fd;
-               buddy->sbi = sbi;
+               buddy->sbi = sbi + i;
                buddy->ping_received = false;
                para_list_add(&buddy->node, &ctx->buddies);
 
@@ -172,61 +166,50 @@ fail:
 }
 
 /*
- * 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.
+ * 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)
+static void *sync_setup(const struct lls_parse_result *lpr)
 {
-       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);
+       int i, ret;
+       unsigned n;
+       struct sync_buddy_info *sbi;
+       const struct lls_opt_result *r_b;
+
+       r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
+       n = lls_opt_given(r_b);
+       sbi = para_malloc(n * sizeof(*sbi));
+       PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
        for (i = 0; i < n; i++) {
-               const char *url = conf->buddy_arg[i];
+               const char *url = lls_string_val(i, r_b);
                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;
+                       exit(EXIT_FAILURE);
                }
                if (port < 0)
-                       port = conf->port_arg;
+                       port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, lpr);
                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;
+                       PARA_ERROR_LOG("host lookup failure for %s: %s\n",
+                               url, para_strerror(-ret));
+                       exit(EXIT_FAILURE);
                }
-               sbi->url = url;
-               sbi->host = host;
-               sbi->port = port;
-               sbi->ai = ai;
-               sbi->disabled = false;
+               sbi[i].url = url;
+               sbi[i].host = host;
+               sbi[i].port = port;
+               sbi[i].ai = ai;
+               sbi[i].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;
+       return sbi;
 }
 
 /*
@@ -260,11 +243,12 @@ static void sync_disable_active_buddies(struct sync_filter_context *ctx)
 }
 
 static void sync_set_timeout(struct sync_filter_context *ctx,
-               struct sync_filter_config *sfc)
+               struct lls_parse_result *lpr)
 {
+       uint32_t ms = FILTER_CMD_OPT_UINT32_VAL(SYNC, TIMEOUT, lpr);
        struct timeval to;
 
-       ms2tv(sfc->conf->timeout_arg, &to);
+       ms2tv(ms, &to);
        tv_add(now, &to, &ctx->timeout);
 }
 
@@ -273,7 +257,6 @@ 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);
@@ -286,7 +269,7 @@ static void sync_pre_select(struct sched *s, void *context)
        if (ret == 0)
                return;
        if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
-               sync_set_timeout(ctx, sfc);
+               sync_set_timeout(ctx, fn->lpr);
                return sched_min_delay(s);
        }
        if (sync_complete(ctx)) /* push down what we have */
@@ -310,7 +293,6 @@ 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))
@@ -324,7 +306,7 @@ static int sync_post_select(__a_unused struct sched *s, void *context)
        if (ret == 0)
                return 0;
        if (ctx->timeout.tv_sec == 0)
-               sync_set_timeout(ctx, sfc);
+               sync_set_timeout(ctx, fn->lpr);
        else {
                if (tv_diff(&ctx->timeout, now, NULL) < 0) {
                        sync_disable_active_buddies(ctx);
@@ -395,21 +377,11 @@ out:
        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);
-}
+const struct filter lsg_filter_cmd_com_sync_user_data = {
+       .setup = sync_setup,
+       .open = sync_open,
+       .pre_select = sync_pre_select,
+       .post_select = sync_post_select,
+       .close = sync_close,
+       .teardown = sync_teardown
+};
index 15bb6859b1c2e668245bf0663bc57d7593710f83..a71963c9bbda192f047f144e970d92dcfd764a77 100644 (file)
@@ -18,6 +18,7 @@ endif
 
 tests := $(sort $(wildcard $(test_dir)/t[0-9][0-9][0-9][0-9]-*.sh))
 
+check: $(tests)
 test: $(tests)
 
 $(tests): all
@@ -30,4 +31,4 @@ test-clean:
        $(RM) -r $(results_dir)
        $(RM) -r $(trash_dir)
 
-.PHONY: $(tests) test-help
+.PHONY: $(tests) test-help test-clean test check
index 554dfdf85dd654ef81c3114a847102ede377e6bc..0b30539acbbfd527a46c586e47818ac5f49d02fd 100755 (executable)
@@ -11,7 +11,7 @@ implementation.'
 test_require_objects "oggdec_filter"
 missing_objects="$result"
 
-test_require_executables oggdec sha1sum
+test_require_executables oggdec shasum
 missing_executables="$result"
 
 get_audio_file_paths ogg
@@ -28,9 +28,9 @@ for ogg in $oggs; do
                continue
        fi
        test_expect_success "${ogg##*/}" "
-               $PARA_FILTER -f oggdec < $ogg | sha1sum > filter.sha1 &&
-               oggdec --quiet --raw --output - -  < $ogg | sha1sum > oggdec.sha1 &&
-               diff -u filter.sha1 oggdec.sha1
+               $PARA_FILTER -f oggdec < $ogg | shasum > filter.sha &&
+               oggdec --quiet --raw --output - -  < $ogg | shasum > oggdec.sha &&
+               diff -u filter.sha oggdec.sha
        "
 done
 test_done
index 1963748f81547ac5706b992597cc41955abffa6d..5329ef0f3aaf85b7f50f0b460e28026ca4067378 100755 (executable)
@@ -28,7 +28,7 @@ declare -a commands=() cmdline=() required_objects=() good=() bad=()
 i=0
 commands[$i]="help"
 cmdline[$i]="help"
-good[$i]='help server  ----'
+good[$i]='help ----'
 
 let i++
 commands[$i]="init"
index fa32d4112e0ee80592b04c40214ffc7a868c9d89..ee1949c05a04d4fd4b81f2b9c847a403a753e2f5 100755 (executable)
@@ -13,8 +13,6 @@ 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"
@@ -24,20 +22,19 @@ grep_man()
 # 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_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv"
+test_expect_success 'para_filter: filter options' "grep_man 'FILTERS' filter"
+test_expect_success 'para_write: writer options' "grep_man 'WRITERS' 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"
+               "grep_man 'RECEIVERS' audiod"
        test_expect_success 'para_audiod: filters' \
-               "grep_man 'Options for the compress filter' audiod"
+               "grep_man 'FILTERS' audiod"
        test_expect_success 'para_audiod: writers' \
-               "grep_man 'Options for the file writer' audiod"
+               "grep_man 'WRITERS' audiod"
 fi
 
 # check various command lists
@@ -56,7 +53,7 @@ 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'
+       regex='LIST OF SERVER COMMANDS.\{100,\}'
        test_expect_success 'para_server: server/afs commands' \
                "grep_man '$regex' server"
 fi
index 1f9913e3cf9ba49b4cb76cc5b985d914cb527b69..9c6516e2abdd9500848263f324dd7afbe8246829 100644 (file)
@@ -311,7 +311,7 @@ fi
 fixup_dirs
 
 [[ -z "$o_executables" ]] && o_executables="para_afh para_audioc para_audiod
-       para_client para_fade para_filter para_gui para_recv para_server
+       para_client para_mixer para_filter para_gui para_recv para_server
        para_write"
 for exe in $o_executables; do
        export $(tr 'a-z' 'A-Z' <<< $exe)="$o_executables_dir/$exe"
diff --git a/time.c b/time.c
index 6bb9a50f5b32f7441943817dc22f868d0f0131cd..90c6735644540f1431afcc1b376da0662823e80c 100644 (file)
--- a/time.c
+++ b/time.c
@@ -198,22 +198,14 @@ void compute_chunk_time(long unsigned chunk_num,
 struct timeval *clock_get_realtime(struct timeval *tv)
 {
        static struct timeval user_friendly;
+       struct timespec t;
+       int ret;
 
        if (!tv)
                tv = &user_friendly;
-#ifdef HAVE_CLOCK_GETTIME
-       {
-               struct timespec t;
-               int ret;
-
-               ret = clock_gettime(CLOCK_REALTIME, &t);
-               assert(ret == 0);
-               tv->tv_sec = t.tv_sec;
-               tv->tv_usec = t.tv_nsec / 1000;
-       }
-#else
-       #include <sys/time.h>
-       gettimeofday(tv, NULL);
-#endif /* HAVE_CLOCK_GETTIME */
+       ret = clock_gettime(CLOCK_REALTIME, &t);
+       assert(ret == 0);
+       tv->tv_sec = t.tv_sec;
+       tv->tv_usec = t.tv_nsec / 1000;
        return tv;
 }
index b803b4976b52d0e89fdae2c3ee7edbdb8a0a311b..4620d61a4603e46561881a3b7c0976a09c7788d9 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "portable_io.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "udp_recv.cmdline.h"
 #include "string.h"
 #include "net.h"
 #include "fd.h"
@@ -103,13 +103,6 @@ static void udp_recv_close(struct receiver_node *rn)
        btr_pool_free(rn->btrp);
 }
 
-static void *udp_recv_parse_config(int argc, char **argv)
-{
-       struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-       udp_recv_cmdline_parser(argc, argv, tmp);
-       return tmp;
-}
-
 /*
  * Perform AF-independent joining of multicast receive addresses.
  *
@@ -173,58 +166,34 @@ err:
 
 static int udp_recv_open(struct receiver_node *rn)
 {
-       struct udp_recv_args_info *c = rn->conf;
-       char  *iface = c->iface_given ? c->iface_arg : NULL;
+       struct lls_parse_result *lpr = rn->lpr;
+       const char *iface = RECV_CMD_OPT_STRING_VAL(UDP, IFACE, lpr);
+       const char *host = RECV_CMD_OPT_STRING_VAL(UDP, HOST, lpr);
+       uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL);
+       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
        if (ret < 0)
-               goto err;
+               return ret;
        rn->fd = ret;
-
        ret = mcast_receiver_setup(rn->fd, iface);
-       if (ret < 0) {
-               close(rn->fd);
+       if (ret < 0)
                goto err;
-       }
-
        ret = mark_fd_nonblocking(rn->fd);
-       if (ret < 0) {
-               close(rn->fd);
+       if (ret < 0)
                goto err;
-       }
-       PARA_INFO_LOG("receiving from %s:%d, fd=%d\n", c->host_arg,
-               c->port_arg, rn->fd);
+       PARA_INFO_LOG("receiving from %s:%u, fd=%d\n", host, port, rn->fd);
        rn->btrp = btr_pool_new("udp_recv", 320 * 1024);
        return rn->fd;
 err:
+       close(rn->fd);
        return ret;
 }
 
-static void udp_recv_free_config(void *conf)
-{
-       udp_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the udp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void udp_recv_init(struct receiver *r)
-{
-       struct udp_recv_args_info dummy;
-
-       udp_recv_cmdline_parser_init(&dummy);
-       r->open = udp_recv_open;
-       r->close = udp_recv_close;
-       r->pre_select = udp_recv_pre_select;
-       r->post_select = udp_recv_post_select;
-       r->parse_config = udp_recv_parse_config;
-       r->free_config = udp_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv);
-       udp_recv_cmdline_parser_free(&dummy);
-}
+/** See \ref recv_init(). */
+const struct receiver lsg_recv_cmd_com_udp_user_data = {
+       .open = udp_recv_open,
+       .close = udp_recv_close,
+       .pre_select = udp_recv_pre_select,
+       .post_select = udp_recv_post_select,
+};
index 425118a135edfee7224f40256dc087a803115aaf..8c9eebc94c3a5ef684ef86340b70d3140cd6fcda 100644 (file)
@@ -15,8 +15,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
-#include "server.cmdline.h"
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
@@ -94,11 +95,11 @@ static int mcast_sender_setup(struct sender_client *sc)
 {
        struct sockaddr_storage ss;
        socklen_t sslen = sizeof(ss);
-       int ttl = conf.udp_ttl_arg, id = 0;
+       int ttl = OPT_INT32_VAL(UDP_TTL), id = 0;
        const int on = 1;
 
-       if (conf.udp_mcast_iface_given) {
-               char *iface = conf.udp_mcast_iface_arg;
+       if (OPT_GIVEN(UDP_MCAST_IFACE)) {
+               const char *iface = OPT_STRING_VAL(UDP_MCAST_IFACE);
 
                id = if_nametoindex(iface);
                if (id == 0)
@@ -174,7 +175,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
        ret = parse_fec_url(url, scd);
        if (ret)
                return ret;
-       port = scd->port > 0 ? scd->port : conf.udp_default_port_arg;
+       port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
        ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
@@ -372,7 +373,7 @@ static char *udp_status(void)
                "port: %s\n"
                "targets: %s\n",
                (sender_status == SENDER_on)? "on" : "off",
-               stringify_port(conf.udp_default_port_arg, "udp"),
+               stringify_port(OPT_UINT32_VAL(UDP_DEFAULT_PORT), "udp"),
                tgts? tgts : "(none)"
        );
        free(tgts);
@@ -385,10 +386,11 @@ static void udp_init_target_list(void)
        int i;
 
        INIT_LIST_HEAD(&targets);
-       for (i = 0; i < conf.udp_target_given; i++) {
-               if (udp_resolve_target(conf.udp_target_arg[i], &scd) < 0)
+       for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
+               const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
+               if (udp_resolve_target(arg, &scd) < 0)
                        PARA_CRIT_LOG("not adding requested target '%s'\n",
-                                       conf.udp_target_arg[i]);
+                                       arg);
                else
                        udp_com_add(&scd);
        }
@@ -438,7 +440,7 @@ void udp_send_init(struct sender *s)
        s->client_cmds[SENDER_delete] = udp_com_delete;
        sender_status = SENDER_off;
        udp_init_target_list();
-       if (!conf.udp_no_autostart_given)
+       if (!OPT_GIVEN(UDP_NO_AUTOSTART))
                sender_status = SENDER_on;
        PARA_DEBUG_LOG("udp sender init complete\n");
 }
index 9c751244339c34197d52229b75b4d55e5a58a43f..23fe086f189e89b3a729aa5afe145006446371bb 100644 (file)
@@ -48,7 +48,7 @@ static void populate_user_list(char *user_list_file)
                if (strcmp(w, "user"))
                        continue;
                PARA_DEBUG_LOG("found entry for user %s\n", n);
-               ret = get_asymmetric_key(k, LOAD_PUBLIC_KEY, &pubkey);
+               ret = get_public_key(k, &pubkey);
                if (ret < 0) {
                        PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
                                para_strerror(-ret));
@@ -63,7 +63,7 @@ static void populate_user_list(char *user_list_file)
                if (ret <= CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
                        PARA_WARNING_LOG("public key %s too short (%d)\n",
                                k, ret);
-                       free_asymmetric_key(pubkey);
+                       free_public_key(pubkey);
                        continue;
                }
                u = para_malloc(sizeof(*u));
@@ -114,7 +114,7 @@ void init_user_list(char *user_list_file)
                list_for_each_entry_safe(u, tmp, &user_list, node) {
                        list_del(&u->node);
                        free(u->name);
-                       free_asymmetric_key(u->pubkey);
+                       free_public_key(u->pubkey);
                        free(u);
                }
        } else
index 3a77e98fa8c4636406035ce66f7842235832db4d..948ed503093e53d1df0e29430619c22092489235 100644 (file)
@@ -15,6 +15,7 @@
  * is read at server startup.
  */
 enum server_command_permissions {
+       NO_PERMISSION_REQUIRED = 0, /** None of the below. */
        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. */
diff --git a/vss.c b/vss.c
index 4d73a95cd142908f26ec9c2108139f1561a4c995..4a1db40d716c9022c7a38e0653ee50a515a53e4f 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -19,7 +19,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "portable_io.h"
@@ -29,7 +31,6 @@
 #include "afs.h"
 #include "server.h"
 #include "net.h"
-#include "server.cmdline.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
@@ -88,6 +89,8 @@ struct vss_task {
        enum afs_socket_status afsss;
        /** The memory mapped audio file. */
        char *map;
+       /** The size of the memory mapping. */
+       size_t mapsize;
        /** Used by the scheduler. */
        struct task *task;
        /** Pointer to the header of the mapped audio file. */
@@ -96,6 +99,8 @@ struct vss_task {
        size_t header_len;
        /** Time between audio file headers are sent. */
        struct timeval header_interval;
+       /* Only used if afh supports dynamic chunks. */
+       void *afh_context;
 };
 
 /**
@@ -347,6 +352,8 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
 static void vss_get_chunk(int chunk_num, struct vss_task *vsst,
                char **buf, size_t *sz)
 {
+       int ret;
+
        /*
         * Chunk zero is special for header streams: It is the first portion of
         * the audio file which consists of the audio file header. It may be
@@ -356,12 +363,20 @@ static void vss_get_chunk(int chunk_num, struct vss_task *vsst,
         * rather than the unmodified header (chunk zero).
         */
        if (chunk_num == 0 && vsst->header_len > 0) {
+               assert(vsst->header_buf);
                *buf = vsst->header_buf; /* stripped header */
                *sz = vsst->header_len;
                return;
        }
-       afh_get_chunk(chunk_num, &mmd->afd.afhi, vsst->map, (const char **)buf,
-               sz);
+       ret = afh_get_chunk(chunk_num, &mmd->afd.afhi,
+               mmd->afd.audio_format_id, vsst->map, vsst->mapsize,
+               (const char **)buf, sz, &vsst->afh_context);
+       if (ret < 0) {
+               PARA_WARNING_LOG("could not get chunk %d: %s\n",
+                       chunk_num, para_strerror(-ret));
+               *buf = NULL;
+               *sz = 0;
+       }
 }
 
 static void compute_group_size(struct vss_task *vsst, struct fec_group *g,
@@ -846,7 +861,7 @@ static void vss_eof(struct vss_task *vsst)
        set_eof_barrier(vsst);
        afh_free_header(vsst->header_buf, mmd->afd.audio_format_id);
        vsst->header_buf = NULL;
-       para_munmap(vsst->map, mmd->size);
+       para_munmap(vsst->map, vsst->mapsize);
        vsst->map = NULL;
        mmd->chunks_sent = 0;
        //mmd->offset = 0;
@@ -855,7 +870,9 @@ 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->size = 0;
+       vsst->mapsize = 0;
+       afh_close(vsst->afh_context, mmd->afd.audio_format_id);
+       vsst->afh_context = NULL;
        mmd->events++;
 }
 
@@ -973,11 +990,11 @@ static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
-       mmd->size = statbuf.st_size;
-       ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
+       ret = para_mmap(statbuf.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
                passed_fd, 0, &vsst->map);
        if (ret < 0)
                goto err;
+       vsst->mapsize = statbuf.st_size;
        close(passed_fd);
        mmd->chunks_sent = 0;
        mmd->current_chunk = 0;
@@ -986,7 +1003,7 @@ static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
        mmd->num_played++;
        mmd->new_vss_status_flags &= (~VSS_NEXT);
        afh_get_header(&mmd->afd.afhi, mmd->afd.audio_format_id,
-               vsst->map, mmd->size, &vsst->header_buf, &vsst->header_len);
+               vsst->map, vsst->mapsize, &vsst->header_buf, &vsst->header_len);
        return;
 err:
        free(mmd->afd.afhi.chunk_table);
@@ -1076,7 +1093,7 @@ static void vss_send(struct vss_task *vsst)
                 */
                if (mmd->current_chunk > 0) { /* chunk 0 might be on the heap */
                        buf += len;
-                       for (i = 0; i < 5 && buf < vsst->map + mmd->size; i++) {
+                       for (i = 0; i < 5 && buf < vsst->map + vsst->mapsize; i++) {
                                __a_unused volatile char x = *buf;
                                buf += 4096;
                        }
@@ -1113,7 +1130,7 @@ static int vss_post_select(struct sched *s, void *context)
                set_eof_barrier(vsst);
                mmd->chunks_sent = 0;
                mmd->current_chunk = afh_get_start_chunk(mmd->repos_request,
-                       &mmd->afd.afhi);
+                       &mmd->afd.afhi, mmd->afd.audio_format_id);
                mmd->new_vss_status_flags &= ~VSS_REPOS;
                set_mmd_offset();
        }
@@ -1166,10 +1183,8 @@ void init_vss_task(int afs_socket, struct sched *s)
        static struct vss_task vss_task_struct, *vsst = &vss_task_struct;
        int i;
        char *hn = para_hostname(), *home = para_homedir();
-       long unsigned announce_time = conf.announce_time_arg > 0?
-                       conf.announce_time_arg : 300,
-               autoplay_delay = conf.autoplay_delay_arg > 0?
-                       conf.autoplay_delay_arg : 0;
+       long unsigned announce_time = OPT_UINT32_VAL(ANNOUNCE_TIME),
+               autoplay_delay = OPT_UINT32_VAL(AUTOPLAY_DELAY);
        vsst->header_interval.tv_sec = 5; /* should this be configurable? */
        vsst->afs_socket = afs_socket;
        ms2tv(announce_time, &vsst->announce_tv);
@@ -1182,7 +1197,7 @@ void init_vss_task(int afs_socket, struct sched *s)
        free(hn);
        free(home);
        mmd->sender_cmd_data.cmd_num = -1;
-       if (conf.autoplay_given) {
+       if (OPT_GIVEN(AUTOPLAY)) {
                struct timeval tmp;
                mmd->vss_status_flags |= VSS_PLAYING;
                mmd->new_vss_status_flags |= VSS_PLAYING;
index 88047adbb71c2becef6afe13f45ab96ddd3b1774..eb38cd7d9626cc9be41957e2f6a04619503bb891 100644 (file)
@@ -12,7 +12,6 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -120,15 +119,9 @@ err:
        return ret;
 }
 
-/**
- * The init function of the wav filter.
- *
- * \param f Structure to initialize.
- */
-void wav_filter_init(struct filter *f)
-{
-       f->close = wav_close;
-       f->open = wav_open;
-       f->pre_select = wav_pre_select;
-       f->post_select = wav_post_select;
-}
+const struct filter lsg_filter_cmd_com_wav_user_data = {
+       .close = wav_close,
+       .open = wav_open,
+       .pre_select = wav_pre_select,
+       .post_select = wav_post_select,
+};
index 425c2190d1798557138f605a9de43b94412f88cd..2b7fe2217b51b1d54fefc2a005b5e3ad509a65d9 100644 (file)
@@ -6,7 +6,7 @@
        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
+       AddIcon ../tar-icon.png *.tgz *.tar.bz2 *.tar.xz
+       AddDescription "current master snapshot" -git.tar.xz .g*.tar.xz .g*.dirty.tar.xz
+       AddDescription "release tarball" *.tgz *.tar.bz2 *.tar.xz
 </ifmodule>
index 2c72f28126d2ba083ac0de69f643c6b501820987..d91a2ad4d5626c137551ae31ba9bb13b2bfcc7b0 100644 (file)
@@ -5,7 +5,7 @@ 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> Runs on Linux, 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>
diff --git a/web/dia/overview.dia b/web/dia/overview.dia
deleted file mode 100644 (file)
index f9e0158..0000000
+++ /dev/null
@@ -1,3817 +0,0 @@
-<?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 b226943b199a224e6b38e4685235707982e23f11..d6d690a16bd5299a6fdb8d304cfe48fc13dbc6c3 100644 (file)
@@ -5,12 +5,6 @@
 <h2> General information </h2>
 
 <ul>
-       <li> <a href="overview.pdf">overview.pdf</a>,
-
-               a pdf file containing a sketch which illustrates how
-               the pieces of paraslash work together.
-
-       </li>
        <li> <a href="manual.html">user manual</a>,
                Installation, Configuration and Usage.
        </li>
@@ -29,7 +23,7 @@
        [<a href="para_filter.man.html">para_filter</a>]
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
-       [<a href="para_fade.man.html">para_fade</a>]
+       [<a href="para_mixer.man.html">para_mixere</a>]
        [<a href="para_play.man.html">para_play</a>]
 </p>
 
index a09fd1c868f5c9aa1eabd9b004d00dc30754d256..1ca8a4d20c4624fe8e67dcbacd4e1f16c6f5931d 100644 (file)
@@ -9,9 +9,9 @@ provided at this point. There are several ways to download the source:
 
                Clone the git repository by executing
 
-               <p> <pre> <kbd>
+               <pre> <kbd>
                        git clone git://git.tuebingen.mpg.de/paraslash.git
-               </kbd> </pre> </p>
+               </kbd> </pre>
 
                <p> The repository contains the full history of the
                project since 2006, all work in progress and the source
@@ -52,7 +52,7 @@ provided at this point. There are several ways to download the source:
 
                Whenever significant changes are incorporated a
 
-               <a href="releases/paraslash-git.tar.bz2">tarball</a>
+               <a href="releases/paraslash-git.tar.xz">tarball</a>
 
                of the current master branch is created. All changes in
                this tarball will be included in the next release. Like
index fd17a649265020637a64e568160f83098c42dafb..06ff03362e8a33dfe2b02bd2c214892dd3ba59c4 100644 (file)
Binary files a/web/images/paraslash.ico and b/web/images/paraslash.ico differ
index a06d6a1a9d67f4d4bf62fc81fcee3348c0d9993b..804752cf5b6568cbb687c784fede125f3d1d24ab 100644 (file)
Binary files a/web/images/paraslash.png and b/web/images/paraslash.png differ
index 12454ee2ae55ba131165ae2d442041879dad33b1..96d724c939ee13f1dd640ee98a47f1421bc1d85f 100644 (file)
@@ -12,81 +12,143 @@ 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
+In this chapter we give an [overview](#Overview) of the interactions
+of the 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.
+applications, para_server and para_audiod. para_server maintains
+the audio file database and acts as the streaming source, while
+para_audiod is the streaming client. Usually, both run in the
+background 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|
-                                                    +-----------+
+                                               .____________________.
+                                               |       ______       |
+       .-----------------------.               |    .d########b.    |
+       |.---------------------.|               |  .d############b   |
+       ||                     ||               | .d######""####//b. |
+       ||                     ||               | 9######(  )######P |
+       ||                     ||               | 'b######++######d' |
+       ||       Screen        ||               |  "9############P"  |
+       ||                     ||               |   "9a########P"    |
+       ||                     ||               |      `""""''       |
+       |`---------------------'|               |  ________________  |
+       `-----------------------'               | |________________| |
+             ___)     (___                     |____________________|
+             `-._______.-'                           loudspeaker
+                   |                                     |
+                   |                                     |
+                   |                                     |
+             .____/ \___.     ._____________.     ._____/ \_____.
+             |          |     |             |     |             |
+             | para_gui |-----| para_audioc |-----| para_audiod |
+             |____   ___|     |_____________|     |_____   _____|
+                  \ /                                   \ /
+                   |                                     |
+                   |                                     |
+                   |                                     |
+            ._____/ \_____.                       ._____/ \_____.
+            |             |                       |             |
+            | para_client |-----------------------| para_server |
+            |_____________|                       |_____   _____|
+                                                        \ /
+                                                         |
+                                                         |
+                                                     .-'"""`-.
+                                                    (         )
+                                                    |`-.___.-'|
+                                                    |         |
+                                                    |. ' " ` .|
+                                                    |         |
+                                                     `-.___.-'
+                                                      Database
+
+The two client programs, para_client and para_audioc communicate with
+para_server and para_audiod, respectively.
+
+para_gui controls para_server and para_audiod by executing para_client
+and para_audioc. In particular, it runs a command to obtain the state
+of para_audiod and para_server, and the metadata of the current audio
+file. This information is pretty-printed in a curses window.
+
 The paraslash executables
 -------------------------
 
-### para_server ###
+<h3> para_server </h3>
 
 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
+as play, stop, pause, next from authenticated clients. The components
+of para_server are illustrated in the following diagram:
+
+       ______________________________________________________________________ network
+             |                                  |      |      |         |
+             |           .-'""""`-.             |      |      |         |
+             |          (          )            |      |      |         |
+       .____/ \_____.   |`-.____.-'|      .____/ \____/ \____/ \____.   |
+       |            |   |          |      |                         |   |
+       | dispatcher |   | database |      | senders (http/udp/dccp) |   |
+       |____   _____|   |          |      |___________   ___________|   |
+            \ /         |. ' "" ` .|                  \ /               |
+             |          |          |                   |                |
+             |           `-.____.-'                    |                |
+             |               |                         |                |
+             |               |                         |                |
+             |               |                         |                |
+             |        ._____/ \_____.        .________/ \________.      |
+             |        |             |        |                   |      |
+             |        | audio  file |________| virtual streaming |      |
+             |        |  selector   |        |      system       |      |
+             |        |_____   _____|        |________   ________|      |
+             |              \ /                       \ /               |
+             |               |                         |                |
+             |               |                         |                |
+             |               |   ._________________.   |                |
+             |               |   |                 |   |                |
+             |               `---| command handler |---'                |
+             |                   |____   ___   ____|                    |
+             |                        \ /   \ /                         |
+             |                         |     |                          |
+             |                         |     |                          |
+             |                         |     |                          |
+             `-------------------------'     `--------------------------'
+
+
+Incoming connections arrive at the dispatcher which creates a process
+dedicated to the connection. Its task is to authenticate the client
+and to run the command handler which forwards the client request to
+either the audio file selector or the virtual streaming system. Results
+(if any) are sent back to the client.
+
+The audio file selector manages audio files using various database
+tables. It maintains statistics on the usage of all audio files such as
+last-played time and the number of times each file was selected. It
+is also responsible for selecting and loading audio files for
+streaming. 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.
-
+including information found in (ID3) tags. Simple playlists are also
+supported. It is 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.
+discusses this topic in more detail.
 
+Another component of para_server is the virtual streaming system,
+which controls the paraslash senders. During streaming it requests
+small chunks of data (e.g., mp3 frames) from the audio file selector
+and feeds them to the senders which forward the chunks to connected
+clients.
 
-### para_client ###
+The three senders of para_server correspond to network streaming
+protocols based on HTTP, DCCP, or UDP. This is explained in the
+section on [networking](#Networking).
+
+<h3> para_client </h3>
 
 The client program to connect to para_server. paraslash commands
 are sent to para_server and the response is dumped to STDOUT. This
@@ -101,29 +163,67 @@ 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 ###
+<h3> para_audiod </h3>
+
+The purpose of para_audiod is to download, decode and play an audio
+stream received from para_server. A typical setup looks as follows.
+
+
+              .----------------------------.
+              |                            |
+              |                            |
+       ._____/ \_____.                .___/ \____.
+       |             |     .----------|          |
+       | para_server |     |   .______| receiver |
+       |_____    ____|     |   |      |___   ____|
+             \ /           |   |          \ /
+              |            |   |           |
+              |            |   |           |
+              |            |   |           |
+       ._____/ \_____.     |   |      .___/ \____.
+       |             |     |   |      |          |
+       | status task |-----+   |      | filter 1 |
+       |_____________|         |      |___   ____|
+                               |          \ /
+                               |           |            .____________________.
+                               |           |            |       ______       |
+       .____________.          |      .___/ \____.      |    .d########b.    |
+       |            |          |      |          |      |  .d############b   |
+       | dispatcher |----------'      | filter 2 |      | .d######""####//b. |
+       |_____   ____|                 |___   ____|      | 9######(  )######P |
+             \ /                          \ /           | 'b######++######d' |
+              |                            |            |  "9############P"  |
+              |                            |            |   "9a########P"    |
+       ._____/ \_____.                .___/ \____.      |      `""""''       |
+       |             |                |          |      |  ________________  |
+       | para_audioc |                |  writer  |------| |________________| |
+       |_____________|                |__________|      |____________________|
+
+
+The status task of para_audiod connects to para_server and runs the
+"stat" command to retrieve the current server status. If an audio
+stream is available, para_audiod starts a so-called buffer tree to
+play the stream.
+
+The buffer tree 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 of para_audiod listens on a local socket and runs
+audiod commands on behalf of para_audioc. For example, para_gui runs
+para_audioc to obtain status information about para_audiod and the
+current audio file. Access to the local socket may be restricted by
+means of Unix socket credentials.
+
+<h3> para_audioc </h3>
 
 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 ###
+<h3> para_recv </h3>
 
 A command line HTTP/DCCP/UDP stream grabber. The http mode is
 compatible with arbitrary HTTP streaming sources (e.g. icecast).
@@ -134,7 +234,7 @@ 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 ###
+<h3> para_filter </h3>
 
 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
@@ -143,31 +243,33 @@ 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 ###
+<h3> para_afh </h3>
 
 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 ###
+<h3> para_write </h3>
 
 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.
+output plug-in and optional WAV/raw players for ALSA (Linux) and OSS.
+para_write can also be used as a stand-alone WAV or raw audio player.
 
-### para_play ###
+<h3> para_play </h3>
 
-A command line audio player.
+A command line audio player which supports the same audio formats as
+para_server. It differs from other players in that it has an insert
+and a command mode, like the vi editor. Line editing is based on
+libreadline, and tab completion and command history are supported.
 
-### para_gui ###
+<h3> para_gui </h3>
 
 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 ###
+<h3> para_mixer </h3>
 
 An alarm clock and volume-fader for OSS and ALSA.
 
@@ -183,25 +285,28 @@ source code and the steps that have to be performed in order to
 
 Requirements
 ------------
-### For the impatient ###
+<h3> For the impatient </h3>
 
+       git clone git://git.tuebingen.mpg.de/lopsub
+       cd lopsub && make && sudo make install
        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 \
+       sudo apt-get install autoconf libssl-dev 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 ###
+<h3> Detailed description </h3>
 
 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
+- [lopsub](http://people.tuebingen.mpg.de/maan/lopsub/). The long
+option parser for subcommands generates the command line and config
+file parsers for all paraslash executables. Clone the source code
+repository with
 
-               git clone git://git.tuebingen.mpg.de/osl
+               git clone git://git.tuebingen.mpg.de/lopsub
 
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
 [clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
@@ -214,18 +319,17 @@ disto. On BSD systems the gnu make executable is often called gmake.
 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:
 
+- [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
+
 - [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
@@ -249,8 +353,11 @@ recognized. The mp3 tagger also needs this library for modifying
 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`).
+- [libfaad and mp4ff](http://www.audiocoding.com/). For aac files
+(m4a) you need libfaad and libmp4ff (package: `libfaad-dev`). Note
+that for some distributions, e.g. Ubuntu, mp4ff is not part of the
+libfaad package. Install the faad library from sources (available
+through the above link) to get the mp4ff library and header files.
 
 - [speex](http://www.speex.org/). In order to stream or decode speex
 files, libspeex (`libspeex-dev`) is required.
@@ -309,7 +416,7 @@ to install executables under /usr/local/bin and the man pages under
 Configuration
 -------------
 
-### Create a paraslash user ###
+<h3> Create a paraslash user </h3>
 
 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
@@ -353,7 +460,7 @@ Finally, tell para_client to connect to server_host:
        echo 'hostname server_host' > $conf
 
 
-### Start para_server ###
+<h3> Start para_server </h3>
 
 For this first try, we'll use the info loglevel to make the output
 of para_server more verbose.
@@ -369,7 +476,7 @@ commands. Open a new shell as bar@client_host and try
 to retrieve the list of available commands and some server info.
 Don't proceed if this doesn't work.
 
-### Create and populate the database ###
+<h3> Create and populate the database </h3>
 
 An empty database is created with
 
@@ -397,7 +504,7 @@ You may print the list of all known audio files with
 
        para_client ls
 
-### Configure para_audiod ###
+<h3> Configure para_audiod </h3>
 
 We will have to tell para_audiod that it should receive the audio
 stream from server_host via http:
@@ -418,14 +525,6 @@ 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
@@ -448,9 +547,9 @@ User management
 para_server uses a challenge-response mechanism to authenticate
 requests from incoming connections, similar to ssh's public key
 authentication method. Authenticated connections are encrypted using
-a stream cipher, either RC4 or AES in integer counter mode.
+the AES stream cipher in integer counter mode.
 
-In this chapter we briefly describe RSA, RC4 and AES, and sketch the
+In this chapter we briefly describe RSA 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).
@@ -458,33 +557,33 @@ 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 and 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,
+A block cipher is a transformation which operates on fixed-length
+blocks. For symmetric block ciphers the transformation is determined
+by a single key for both encryption and decryption. For asymmetric
+block ciphers, on the other hand, the key consists of two parts,
 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.
+with either key and only the counterpart of that key can decrypt the
+message. Asymmetric block ciphers can be used for both signing and
+encrypting a message.
+
+RSA is an asymmetric block cipher which is used in many applications,
+including ssh and gpg. The RSA public key encryption and signatures
+algorithms are defined in detail in RFC 2437. Paraslash relies on
+RSA for authentication.
+
+Stream ciphers XOR the input with a pseudo-random key stream to produce
+the output. Decryption uses the same function calls as encryption.
+Any block cipher can be turned into a stream cipher by generating the
+pseudo-random key stream by encrypting successive values of a counter
+(counter mode).
 
 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.
+cipher. Paraslash employs AES in counter mode as described above to
+encrypt communications. Since a stream cipher key must not be used
+twice, a random key is generated for every new connection.
 
 Client-server authentication
 ----------------------------
@@ -524,8 +623,8 @@ 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
+by the crypto library (openssl or libgcrypt), on the security of
+the implementation of the RSA and AES crypto routines and on the
 infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their
@@ -567,8 +666,7 @@ execute. The output of
 
        para_client help
 
-contains in the third column the permissions needed to execute the
-command.
+contains 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
@@ -594,27 +692,21 @@ 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.
+also with para_client via the command handler whenever an AFS command
+is executed, for example to answer a database query.
 
-Besides the traditional playlists, AFS supports audio file selection
+Besides the simple 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
+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
+and contains practical 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.
@@ -623,25 +715,85 @@ 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.
+the database tables. The AFS process accepts incoming connections
+which arrive either on a pipe which is shared with para_server,
+or on the local socket it is listening on. The setup is as follows.
+
+                 .___________________.  .______________.
+                 |                   |  |              |
+                 | virtual streaming |  | audio format |
+                 |      system       |  |   handler    |
+                 |_________   _______|  |_____   ______|
+                           \ /                \ /
+                            |                  |
+         .-'""""`-.         |                  |          .-'""""`-.
+        (          )        |                  |         (          )
+        |`-.____.-'|    .__/ \________________/ \___.    |`-.____.-'|
+        |          |    |                           |    |          |
+        |   file   |----| AFS (audio file selector) |----|    OSL   |
+        |  system  |    |         process           |    | database |
+        |          |    |___________________________|    |          |
+        |. ' "" ` .|                 |                   |. ' "" ` .|
+        |          |                 |                   |          |
+         `-.____.-'                  |                    `-.____.-'
+                            ._______/ \_______.
+                            |                 |
+                            | command handler |
+                            |_______   _______|
+                                    \ /
+                                     |
+                                     |
+                                     |
+                              ._____/ \_____.
+                              |             |
+                              | para_client |
+                              |_____________|
+
+The virtual streaming system, which is part of the server process,
+communicates with the AFS process via pipes and shared memory. When
+the current audio file changes, it sends a notification through the
+shared pipe. The AFS process queries the database to determine the
+next audio file, opens it, verifies that it has not been changed since
+it was added to the database and passes the open file descriptor back
+to the virtual streaming system, along with audio file meta-data such
+as file name, duration, audio format and so on. The virtual streaming
+system then starts to stream the file.
+
+The command handlers of all AFS server commands use the local socket
+to query or update the database. For example, the command handler of
+the add command sends the path of an audio file to the local socket.
+The AFS process opens the file and tries to find an audio format
+handler which recognizes the file. If all goes well, a new database
+entry with metadata obtained from the audio format handler is added
+to the database.
+
+Note that AFS employs
+[libosl](http://people.tuebingen.mpg.de/maan/osl/), the object
+storage layer library, as the database backend. This library offers
+functionality similar to a relational database, but is much more
+lightweight than a full featured database management system.
 
 Database layout
 ---------------
 
-### The audio file table ###
+Metadata about the known audio files is stored in an OSL database. This
+database consists of the following tables:
+
+- The audio file table contains path, hash and metadata of each
+known file.
+
+- The "attributes" table maps each of the 64 possible attributes to a
+string.
+
+- The "blob" tables store images, lyrics, moods, playlists. All of
+these are optional.
+
+- The "score" table describes the subset of admissible files for the
+current playlist or mood.
+
+All tables are described in more detail below.
+
+<h3> The audio file table </h3>
 
 This is the most important and usually also the largest table of the
 AFS database. It contains the information needed to stream each audio
@@ -690,7 +842,7 @@ 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 ###
+<h3> The attribute table </h3>
 
 The attribute table contains two columns, _name_ and _bitnum_. An
 attribute is simply a name for a certain bit number in the attribute
@@ -750,7 +902,7 @@ Read the output of
 for more information and a complete list of command line options to
 these commands.
 
-### Blob tables ###
+<h3> Blob tables </h3>
 
 The image, lyrics, moods and playlists tables are all blob tables.
 Blob tables consist of three columns each: The identifier which is
@@ -777,7 +929,7 @@ 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 ###
+<h3> The score table </h3>
 
 The score table describes those audio files which are admissible for
 the current mood or playlist (see below). The table has two columns:
@@ -803,7 +955,7 @@ 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 ###
+<h3> Playlists </h3>
 
 Playlists are accommodated in the playlist table of the afs database,
 using the aforementioned blob format for tables. A new playlist is
@@ -822,7 +974,7 @@ 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 ###
+<h3> Moods </h3>
 
 A mood consists of a unique name and its *mood definition*, which is
 a set of *mood lines* containing expressions in terms of attributes
@@ -897,7 +1049,7 @@ special treatment:
 
 
 
-### List of mood_methods ###
+<h3> List of mood_methods </h3>
 
        no_attributes_set
 
@@ -943,7 +1095,7 @@ if the year tag is a two-digit number. In this case either 1900 or
 greater than 2000 plus the current year.
 
 
-### Mood usage ###
+<h3> Mood usage </h3>
 
 To create a new mood called "my_mood", write its definition into
 some temporary file, say "tmpfile", and add it to the mood table
@@ -971,7 +1123,7 @@ if the "-a" switch is given:
        para ls -a
 
 
-### Example mood definition ###
+<h3> Example mood definition </h3>
 
 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
@@ -1060,7 +1212,7 @@ Audio formats
 
 The following audio formats are supported by paraslash:
 
-### MP3 ###
+<h3> MP3 </h3>
 
 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
@@ -1070,7 +1222,7 @@ 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 ###
+<h3> OGG/Vorbis </h3>
 
 OGG is a standardized audio container format, while Vorbis is an
 open source codec for lossy audio compression. Since Vorbis is most
@@ -1080,7 +1232,7 @@ 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 ###
+<h3> OGG/Speex </h3>
 
 Speex is an open-source speech codec that is based on CELP (Code
 Excited Linear Prediction) coding. It is designed for voice
@@ -1090,7 +1242,7 @@ 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 ###
+<h3> OGG/Opus </h3>
 
 Opus is a lossy audio compression format standardized through RFC
 6716 in 2012. It combines the speech-oriented SILK codec and the
@@ -1099,7 +1251,7 @@ 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 ###
+<h3> AAC </h3>
 
 Advanced Audio Coding (AAC) is a standardized, lossy compression
 and encoding scheme for digital audio which is the default audio
@@ -1107,7 +1259,7 @@ 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 ###
+<h3> WMA </h3>
 
 Windows Media Audio (WMA) is an audio data compression technology
 developed by Microsoft. A WMA file is usually encapsulated in the
@@ -1116,7 +1268,7 @@ 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 ###
+<h3> FLAC </h3>
 
 The Free Lossless Audio Codec (FLAC) compresses audio without quality
 loss. It gives better compression ratios than a general purpose
@@ -1581,7 +1733,7 @@ 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 ###
+<h3> amp </h3>
 
 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
@@ -1608,7 +1760,7 @@ To store V in the audio file table, the command
 is used. The reader is encouraged to write a script that performs
 these computations :)
 
-### compress ###
+<h3> compress </h3>
 
 Unlike the amplification filter, the compress filter adjusts the volume
 of the audio stream dynamically without prior knowledge about the peak
@@ -1700,10 +1852,6 @@ 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
@@ -1833,12 +1981,14 @@ welcome. Here's a list of things you can do to help the project:
 - 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.
+- Post 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>.
+New releases are announced by email. If you would like to receive
+these announcements, contact the author through the above address.
 
 Tools
 -----
@@ -2027,14 +2177,14 @@ Appendix
 Network protocols
 -----------------
 
-### IP ###
+<h3> IP </h3>
 
 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 ###
+<h3> Connection-oriented and connectionless protocols </h3>
 
 Connectionless protocols differ from connection-oriented ones in
 that state associated with the sending/receiving endpoints is treated
@@ -2050,7 +2200,7 @@ 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 ###
+<h3> Reliability </h3>
 
 In IP networking, packets can be lost, duplicated, or delivered
 out of order, and different network protocols handle these
@@ -2062,7 +2212,7 @@ out-of-order. Retransmission is used to guarantee loss-free
 delivery. Unreliable protocols, in contrast, do not guarantee ordering
 or data integrity.
 
-### Classification ###
+<h3> Classification </h3>
 
 With these definitions the protocols which are used by paraslash for
 steaming audio data may be classified as follows.
@@ -2073,7 +2223,7 @@ steaming audio data may be classified as follows.
 
 Below we give a short descriptions of these protocols.
 
-### TCP ###
+<h3> TCP </h3>
 
 The _Transmission Control Protocol_ provides reliable, ordered delivery
 of a stream and a classic window-based congestion control. In contrast
@@ -2084,7 +2234,7 @@ 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 ###
+<h3> UDP </h3>
 
 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
@@ -2095,7 +2245,7 @@ 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 ###
+<h3> DCCP </h3>
 
 The _Datagram Congestion Control Protocol_ combines the
 connection-oriented state maintenance known from TCP with the
@@ -2112,7 +2262,7 @@ 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 ###
+<h3> HTTP </h3>
 
 The _Hypertext Transfer Protocol_ is an application layer protocol
 on top of TCP. It is spoken by web servers and is most often used
@@ -2122,7 +2272,7 @@ 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 ###
+<h3> Multicast </h3>
 
 IP multicast is not really a protocol but a technique for one-to-many
 communication over an IP network. The challenge is to deliver
index b4935f0d44f30fc6091f8431b99a63f23674aed6..ebb2f9c34c97dbb445b312dd1e8ab2004b0d5bc6 100644 (file)
--- a/wma_afh.c
+++ b/wma_afh.c
@@ -38,8 +38,7 @@ static int count_frames(const char *buf, int buf_size, uint32_t packet_size,
                sfc++;
        }
        PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc);
-       if (num_superframes)
-               *num_superframes = sfc;
+       *num_superframes = sfc;
        return fc;
 }
 
@@ -68,7 +67,7 @@ static int put_utf8(uint32_t val, char *result)
                *out++ = in;
                return 1;
        }
-       bytes = (wma_log2(in) + 4) / 5;
+       bytes = DIV_ROUND_UP(wma_log2(in), 5);
        shift = (bytes - 1) * 6;
        *out++ = (256 - (256 >> bytes)) | (in >> shift);
        while (shift >= 6) {
@@ -229,6 +228,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size
                }
        }
        afhi->chunks_total = j;
+       set_max_chunk_size(afhi);
        set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
        return 1;
 fail:
@@ -365,8 +365,7 @@ 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;
+       uint16_t orig_cr_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 */
@@ -389,6 +388,7 @@ static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
        comment_bytes = ret;
 
        if (cdo) {
+               uint16_t orig_title_bytes, orig_artist_bytes, orig_comment_bytes;
                /*
                 * Sizes of the five fields (stored as 16-bit numbers) are
                 * located after the header (16 bytes) and the cdo size (8
@@ -402,10 +402,7 @@ static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
                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;
index 6d57c00be2d2627624f5642a74948723bc2726fb..fcaf3ce4574de8297a7bffa4c53bf09658ea6982 100644 (file)
@@ -178,7 +178,5 @@ __a_const int wma_log2(unsigned int v)
                v >>= 8;
                n += 8;
        }
-       n += log2_tab[v];
-
-       return n;
+       return n + log2_tab[v];
 }
index 4c7c047a482e2b3f256cfb8c12dcd6b4de3a7500..525ed3150cda5affb9b479029b7e9aa5f461a0aa 100644 (file)
 #include "para.h"
 #include "error.h"
 #include "list.h"
-#include "ggo.h"
 #include "string.h"
 #include "sched.h"
 #include "buffer_tree.h"
 #include "filter.h"
+#include "portable_io.h"
 #include "bitstream.h"
 #include "imdct.h"
 #include "wma.h"
@@ -1259,16 +1259,10 @@ static void wmadec_open(struct filter_node *fn)
        fn->min_iqs = 4096;
 }
 
-/**
- * The init function of the wma decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void wmadec_filter_init(struct filter *f)
-{
-       f->open = wmadec_open;
-       f->close = wmadec_close;
-       f->execute = wmadec_execute;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = wmadec_post_select;
-}
+const struct filter lsg_filter_cmd_com_wmadec_user_data = {
+       .open = wmadec_open,
+       .close = wmadec_close,
+       .execute = wmadec_execute,
+       .pre_select = generic_filter_pre_select,
+       .post_select = wmadec_post_select,
+};
diff --git a/write.c b/write.c
index 62caf09757ec52d45f825698d7f4656696d7bcff..a1907b6f46a04f966722117e894f9fe0a69edb11 100644 (file)
--- a/write.c
+++ b/write.c
@@ -8,17 +8,17 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
+#include "write.lsg.h"
 #include "para.h"
 #include "string.h"
-#include "write.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "stdin.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
 #include "fd.h"
 #include "error.h"
 #include "version.h"
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
-static struct write_args_info conf;
+#define CMD_PTR (lls_cmd(0, write_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_WRITE_PARA_WRITE_OPT_ ## _name, _lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
 
 static struct stdin_task sit;
-
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(write);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_writer_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
-       exit(0);
-}
-
-/*
- * Parse config and register a task for a writer node.
- *
- * \param arg Command line arguments.
- * \param parent The new node will be a child of \a parent.
- * \param wn The writer node.
- *
- * If arg is \p NULL, the OS-dependent default writer is used with no
- * arguments.  The default writers are alsa for Linux, osx for OS X, oss for
- * *BSD, and the file writer if the default writer is not supported.
- *
- * Once the writer configuration has been retrieved from the ->parse_config
- * callback a writer node is created, its buffer tree node is added to the
- * buffer tree as a child of the given parent.
- *
- * Finally, the new writer node's task structure is initialized and registered
- * to the paraslash scheduler.
- *
- * \return Standard.
- */
-static void setup_writer_node(const char *arg, struct btr_node *parent,
-               struct writer_node *wn, struct sched *s)
-{
-       wn->conf = check_writer_arg_or_die(arg, &wn->writer_num);
-       register_writer_node(wn, parent, s);
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_writer_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+       exit(EXIT_SUCCESS);
 }
 
 struct write_task {
@@ -88,9 +70,9 @@ static int write_post_select(__a_unused struct sched *s, void *context)
        return check_wav_post_select(wt->cwc);
 }
 
-static int setup_and_schedule(void)
+static int setup_and_schedule(struct lls_parse_result *lpr)
 {
-       int i, ret;
+       int i, n, ret, writer_given = OPT_GIVEN(WRITER, lpr);
        struct btr_node *cw_btrn;
        struct writer_node *wns;
        static struct sched s;
@@ -101,7 +83,7 @@ static int setup_and_schedule(void)
                EMBRACE(.name = "stdin"));
        stdin_task_register(&sit, &s);
 
-       COPY_WAV_PARMS(&wp, &conf);
+       LLS_COPY_WAV_PARMS(&wp, LSG_WRITE_PARA_WRITE, lpr);
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
        wt.task = task_register(&(struct task_info) {
                .name = "write",
@@ -109,28 +91,26 @@ static int setup_and_schedule(void)
                .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);
-               i = 1;
-       } else {
-               wns = para_calloc(conf.writer_given * sizeof(*wns));
-               for (i = 0; i < conf.writer_given; i++)
-                       setup_writer_node(conf.writer_arg[i], cw_btrn,
-                               wns + i, &s);
-       }
 
+       n = writer_given? writer_given : 1;
+       wns = para_calloc(n * sizeof(*wns));
+       for (i = 0; i < n; i++) {
+               const char *arg = i < writer_given?
+                       lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
+               wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
+               register_writer_node(wns + i, cw_btrn, &s);
+       }
        s.default_timeout.tv_sec = 10;
        s.default_timeout.tv_usec = 50000;
        ret = schedule(&s);
        if (ret >= 0) {
                int j, ts;
-               for (j = 0; j < i; j++) {
+               for (j = 0; j < n; j++) {
                        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];
+                               const char *name = writer_name(wn->wid);
                                PARA_ERROR_LOG("%s: %s\n", name,
                                        para_strerror(-ts));
                                if (ret >= 0)
@@ -138,14 +118,12 @@ static int setup_and_schedule(void)
                        }
                }
        }
-       for (i--; i >= 0; i--) {
+       for (i = n - 1; i >= 0; i--) {
                struct writer_node *wn = wns + i;
-               struct writer *w = writers + wn->writer_num;
-
-               w->close(wn);
+               writer_get(wn->wid)->close(wn);
                btr_remove_node(&wn->btrn);
-               w->free_config(wn->conf);
-               free(wn->conf);
+               lls_free_parse_result(wns[i].lpr,
+                       lls_cmd(wn->wid, write_cmd_suite));
        }
        free(wns);
        check_wav_shutdown(wt.cwc);
@@ -167,18 +145,23 @@ static int setup_and_schedule(void)
 int main(int argc, char *argv[])
 {
        int ret;
-
-       write_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       writer_init();
-       version_handle_flag("write", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-
-       ret = setup_and_schedule();
+       struct lls_parse_result *lpr;
+       char *errctx;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("write",  OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+       ret = setup_and_schedule(lpr);
+       lls_free_parse_result(lpr, CMD_PTR);
+out:
        if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               exit(EXIT_FAILURE);
        }
-       exit(EXIT_SUCCESS);
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/write.h b/write.h
index c7c227eefb146db15160b70d8cb25618adb1f0b3..fd3983ddf262f1edec986680c856c1d7f7c9c511 100644 (file)
--- a/write.h
+++ b/write.h
@@ -6,19 +6,16 @@
 
 /** \file write.h Writer-related structures. */
 
-/** The list of supported writers. */
-enum writer_enum {WRITER_ENUM};
-
 /**
  * Describes one running instance of a writer.
  */
 struct writer_node {
-       /** The number of this writer. */
-       int writer_num;
+       /** The ID of this writer. */
+       int wid;
        /** Writer-specific data. */
        void *private_data;
-       /** The writer-specific configuration of this node. */
-       void *conf;
+       /** The parsed command line, merged with options given in the config file. */
+       struct lls_parse_result *lpr;
        /** The buffer tree node associated with this writer node. */
        struct btr_node *btrn;
        /** The task of this writer node. */
@@ -29,30 +26,6 @@ struct writer_node {
 
 /** Describes one supported writer. */
 struct writer {
-       /**
-        * The init function of the writer.
-        *
-        * It must fill in all other function pointers of the given
-        * writer structure.
-        */
-       void (*init)(struct writer *w);
-       /**
-        * The command line parser of the writer.
-        *
-        * It should check whether the command line options given by \a argv
-        * and \a argc are valid and return a pointer to the writer-specific
-        * configuration data determined by these options. This function must
-        * either succeed or call exit(). Note that parse_config_or_die() might
-        * be called more than once with different values of \a options. \sa
-        * \ref free_config().
-        */
-       void *(*parse_config_or_die)(int argc, char **argv);
-       /**
-        * Deallocate all configuration resources.
-        *
-        * This should free whatever was allocated by \ref parse_config_or_die().
-        */
-       void (*free_config)(void *config);
        /**
         * Prepare the fd sets for select.
         *
@@ -72,8 +45,6 @@ struct writer {
         * This function is assumed to succeed.
         */
        void (*close)(struct writer_node *);
-       /** The short and the log help text of this writer. */
-       struct ggo_help help;
        /**
         * The callback handler.
         *
@@ -83,14 +54,23 @@ struct writer {
        btr_command_handler execute;
 };
 
-/** Loop over each supported writer. */
-#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
-
-/** Declare the init functions of all supported writers. */
-DECLARE_WRITER_INITS;
+#define WRITE_CMD(_num) (lls_cmd(_num, write_cmd_suite))
 
-/** Array containing the name of each writer. */
-extern const char *writer_names[];
+#define WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+       (lls_opt_result(LSG_WRITE_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define WRITE_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define WRITE_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
+#define WRITE_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+       (lls_string_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
 
-/** The writer structure for each supported writer. */
-extern struct writer writers[NUM_SUPPORTED_WRITERS];
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp);
+const struct writer *writer_get(int wid);
+const char *writer_name(int wid);
+void register_writer_node(struct writer_node *wn, struct btr_node *parent,
+               struct sched *s);
+void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+void get_btr_channels(struct btr_node *btrn, int32_t *result);
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+void print_writer_helps(bool detailed);
index cdb67e58fd65bf4438522fd0bed4ef9092aaaeaf..d4d1b10b2ec597fa58ecf77dfad82ac01efa5d0c 100644 (file)
 /** \file write_common.c common functions of para_audiod and para_write */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #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};
+/** Loop over all writers. */
+#define FOR_EACH_WRITER(i) for (i = 1; lls_cmd(i, write_cmd_suite); i++)
 
-/** the array of supported writers */
-struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
 
-/**
- * Call the init function of each supported paraslash writer.
- */
-void writer_init(void)
+static inline bool writer_supported(int wid)
+{
+       return lls_user_data(WRITE_CMD(wid));
+}
+
+/* simply return the first available writer */
+static int default_writer_id(void)
 {
        int i;
 
        FOR_EACH_WRITER(i)
-               writers[i].init(&writers[i]);
+               if (writer_supported(i))
+                       return i;
+       assert(0); /* the file writer should always be available */
 }
+
 /**
- * Check if given string is a valid command line for any writer.
+ * Return the writer structure from a writer ID.
  *
- * \param \wa String of the form writer_name:options.
- * \param writer_num Contains the number of the writer upon success.
+ * \param wid If non-positive, a pointer to the default writer is returned.
  *
- * This function checks whether \a wa starts with the name of a supported
- * paraslash writer, optionally followed by a colon and any options for that
- * writer.  If a valid writer name was found and further are present, the
- * remaining part of \a wa is passed to that writer's config parser.
+ * \return Pointer to a (constant) struct writer.
+ */
+const struct writer *writer_get(int wid)
+{
+       if (wid < 0)
+               wid = default_writer_id();
+       return lls_user_data(WRITE_CMD(wid));
+}
+
+/**
+ * Return name of the writer identified by a writer ID.
+ *
+ * \param wid If non-positive, the name of the default writer is returned.
  *
- * \return On success, a pointer to the gengetopt args info struct is returned
- * and \a writer_num contains the number of the writer. Otherwise this function
- * prints an error message and calls exit().
+ * \return The returned buffer must not be freed by the caller.
  */
-void *check_writer_arg_or_die(const char *wa, int *writer_num)
+const char *writer_name(int wid)
 {
-       int i, ret, argc;
-       const char *cmdline;
-       char **argv;
-       void *conf;
+       if (wid <= 0)
+               wid = default_writer_id();
+       return lls_command_name(WRITE_CMD(wid));
+}
 
-       if (!wa || !*wa) {
-               i = DEFAULT_WRITER;
-               cmdline = NULL;
-               goto check;
-       }
-       PARA_INFO_LOG("checking %s\n", wa);
-       FOR_EACH_WRITER(i) {
-               const char *name = writer_names[i];
-               size_t len = strlen(name);
-               char c;
+/**
+ * Check if the given string is a valid command line for any writer.
+ *
+ * \param wa String of the form writer_name options.
+ * \param lprp Contains the parsed command line on success.
+ *
+ * If wa is \p NULL, the (configuration-dependent) default writer is assumed.
+ * Otherwise, the function checks whether \a wa starts with the name of a
+ * supported writer. If a valid writer name was found, the rest of the command
+ * line is passed to the config parser of this writer.
+ *
+ * \return On success, the positive writer ID is returned. Otherwise the
+ * function prints an error message and calls exit().
+ */
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp)
+{
+       int ret, writer_num, argc;
+       char **argv = NULL, *errctx = NULL;
+       const struct lls_command *cmd;
 
-               if (strlen(wa) < len)
-                       continue;
-               if (strncmp(name, wa, len))
-                       continue;
-               c = wa[len];
-               if (!c || c == ' ') {
-                       cmdline = c? wa + len + 1 : NULL;
-                       goto check;
-               }
-       }
-       PARA_EMERG_LOG("invalid writer %s\n", wa);
-       exit(EXIT_FAILURE);
-check:
-       ret = create_shifted_argv(cmdline, " \t", &argv);
-       if (ret < 0) {
-               PARA_EMERG_LOG("%s: %s\n", wa, para_strerror(-ret));
-               exit(EXIT_FAILURE);
+       if (!wa || !*wa) {
+               writer_num = default_writer_id();
+               cmd = WRITE_CMD(writer_num);
+               argv = para_malloc(2 * sizeof(char *));
+               argc = 1;
+               argv[0] = para_strdup(lls_command_name(cmd));
+               argv[1] = NULL;
+               goto parse;
        }
+       ret = create_argv(wa, " \t\n", &argv);
+       if (ret < 0)
+               goto fail;
        argc = ret;
-       argv[0] = make_message("%s_write", writer_names[i]);
-       *writer_num = i;
-       conf = writers[i].parse_config_or_die(argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], write_cmd_suite, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       writer_num = ret;
+       cmd = WRITE_CMD(writer_num);
+       if (!writer_supported(writer_num)) {
+               ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               errctx = make_message("%s writer is not supported",
+                       lls_command_name(cmd));
+               goto free_argv;
+       }
+parse:
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret >= 0)
+               ret = writer_num;
+free_argv:
        free_argv(argv);
-       return conf;
+       if (ret >= 0)
+               return ret;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 /**
@@ -99,20 +132,17 @@ check:
  * \param wn The writer node to open.
  * \param parent The parent btr node (the source for the writer node).
  * \param s The scheduler instance to register the task to.
- *
- * The configuration of the writer node stored in \p wn->conf must be
- * initialized before this function may be called.
  */
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s)
 {
-       struct writer *w = writers + wn->writer_num;
+       const struct writer *w = writer_get(wn->wid);
 
        wn->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = writer_names[wn->writer_num], .parent = parent,
+               EMBRACE(.name = writer_name(wn->wid), .parent = parent,
                .handler = w->execute, .context = wn));
        wn->task = task_register(&(struct task_info) {
-               .name = writer_names[wn->writer_num],
+               .name = writer_name(wn->wid),
                .pre_select = w->pre_select,
                .post_select = w->post_select,
                .context = wn,
@@ -122,24 +152,29 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
 /**
  * Print the help text of all writers to stdout.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
  */
-void print_writer_helps(unsigned flags)
+void print_writer_helps(bool detailed)
 {
        int i;
 
-       printf_or_die("\nAvailable writers: ");
-       FOR_EACH_WRITER(i)
-               printf_or_die("%s%s", i? " " : "", writer_names[i]);
-       printf_or_die("\n");
+       printf("\nAvailable writers: ");
        FOR_EACH_WRITER(i) {
-               struct writer *w = writers + i;
-
-               if (!w->help.short_help)
+               if (!writer_supported(i))
+                       continue;
+               printf("%s%s", i? " " : "", writer_name(i));
+       }
+       printf("\n");
+       FOR_EACH_WRITER(i) {
+               const struct lls_command *cmd = WRITE_CMD(i);
+               char *help;
+               if (!writer_supported(i))
+                       continue;
+               help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
                        continue;
-               printf_or_die("\n%s: %s", writer_names[i],
-                       w->help.purpose);
-               ggo_print_help(&w->help, flags);
+               printf("%s\n", help);
+               free(help);
        }
 }
 
diff --git a/write_common.h b/write_common.h
deleted file mode 100644 (file)
index 1828875..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file write_common.h Exported symbols from write_common.c. */
-
-void writer_init(void);
-void *check_writer_arg_or_die(const char *wa, int *writer_num);
-void print_writer_helps(unsigned flags);
-void register_writer_node(struct writer_node *wn, struct btr_node *parent,
-               struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);