]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
web: Generate left- and right-justified text. master
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 7 May 2024 18:43:15 +0000 (20:43 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Mon, 13 May 2024 17:30:23 +0000 (19:30 +0200)
Larger pages such as the manual page look much nicer after this patch.

85 files changed:
Doxyfile
INSTALL
Makefile.real
NEWS.md
README
afh_recv.c
afs.c
afs.h
aft.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.h
audiod_command.c
blob.c
buffer_tree.c
client.c
client_common.c
command.c
compress_filter.c
configure.ac
crypt.h
crypt_common.c
dccp_recv.c
dccp_send.c
error.h
fd.c
fd.h
fecdec_filter.c
flacdec_filter.c
gcrypt.c
gui.c
gui_theme.c
http_recv.c
http_send.c
interactive.c
ipc.c
m4/lls/mixer.suite.m4
m4/lls/server_cmd.suite.m4
mixer.c
mood.c
mood.h [deleted file]
mp.c
mp3_afh.c
mp3dec_filter.c
mp4.c
mp4.h
net.c
net.h
ogg_afh_common.c
ogg_afh_common.h
oggdec_filter.c
openssl.c
opusdec_filter.c
oss_write.c
para.h
play.c
playlist.c
resample_filter.c
sched.c
score.c
send.h
send_common.c
signal.c
string.c
sync_filter.c
t/audio_files/short-44100-2.mp3 [new file with mode: 0644]
t/t0004-server.sh
t/test-lib.sh
udp_recv.c
udp_send.c
user_list.c
vss.c
wav_filter.c
web/about.in.html
web/download.in.html
web/images/signature.png
web/images/tar-icon.png
web/manual.md
web/para.css
wmadec_filter.c
write.c

index b11683e424443baef8754b34097ad3cb8dbed4fe..58e8023973d510edbe95c74148b55b5ce6aa1796 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.11
+# Doxyfile 1.8.17
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
 # The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
@@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES    = NO
 
 OUTPUT_LANGUAGE        = English
 
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
 # 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.
@@ -179,6 +187,16 @@ SHORT_NAMES            = NO
 
 JAVADOC_AUTOBRIEF      = YES
 
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
 # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
 # line (until the first dot) of a Qt-style comment as the brief description. If
 # set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -226,16 +244,15 @@ TAB_SIZE               = 8
 # will allow you to put the command \sideeffect (or @sideeffect) in the
 # documentation, which will result in a user-defined paragraph with heading
 # "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
 
 ALIASES                =
 
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST              =
-
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
 # only. Doxygen will then generate output that is more tailored for C. For
 # instance, some of the names that are used will be different. The list of all
@@ -264,17 +281,26 @@ OPTIMIZE_FOR_FORTRAN   = NO
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
 # Doxygen selects the parser to use depending on the extension of the files it
 # parses. With this tag you can assign which parser to use for a given
 # extension. Doxygen has a built-in mapping, but you can override or extend it
 # using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran (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.
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# 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, tcl. 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.
 #
@@ -285,7 +311,7 @@ EXTENSION_MAPPING      =
 
 # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
 # according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
 # The output of markdown processing is further processed by doxygen, so you can
 # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
 # case of backward compatibilities issues.
@@ -293,6 +319,15 @@ EXTENSION_MAPPING      =
 
 MARKDOWN_SUPPORT       = YES
 
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 5
+
 # 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 putting a % sign in front of the word or
@@ -318,7 +353,7 @@ BUILTIN_STL_SUPPORT    = NO
 CPP_CLI_SUPPORT        = NO
 
 # Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
 # will parse them like normal C++ but will assume all classes use public instead
 # of private inheritance when no explicit protection keyword is present.
 # The default value is: NO.
@@ -424,6 +459,12 @@ EXTRACT_ALL            = YES
 
 EXTRACT_PRIVATE        = NO
 
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
 # 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.
@@ -478,8 +519,8 @@ HIDE_UNDOC_MEMBERS     = 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
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
 # The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
@@ -502,7 +543,7 @@ INTERNAL_DOCS          = NO
 # 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.
+# (including Cygwin) ands Mac users are advised to set this option to NO.
 # The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
@@ -689,7 +730,7 @@ LAYOUT_FILE            =
 # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
 # the reference definitions. This must be a list of .bib files. The .bib
 # extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# to be installed. See also https://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. See also \cite for info how to create references.
@@ -734,7 +775,8 @@ 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.
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
 # The default value is: NO.
 
 WARN_NO_PARAMDOC       = YES
@@ -776,7 +818,7 @@ INPUT                  = .
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
 # possible encodings.
 # The default value is: UTF-8.
 
@@ -793,8 +835,10 @@ INPUT_ENCODING         = UTF-8
 # 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.
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
+# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
 
 FILE_PATTERNS          = *.c \
                          *.h
@@ -950,7 +994,7 @@ INLINE_SOURCES         = NO
 STRIP_CODE_COMMENTS    = YES
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
+# entity all documented functions referencing it will be listed.
 # The default value is: NO.
 
 REFERENCED_BY_RELATION = YES
@@ -982,12 +1026,12 @@ SOURCE_TOOLTIPS        = YES
 # If the USE_HTAGS tag is set to YES then the references to source code will
 # point to the HTML generated by the htags(1) tool instead of doxygen built-in
 # source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
+# (see https://www.gnu.org/software/global/global.html). You will need version
 # 4.8.6 or higher.
 #
 # To use it do the following:
 # - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
 # - Make sure the INPUT points to the root of the source tree
 # - Run doxygen as normal
 #
@@ -1127,7 +1171,7 @@ HTML_EXTRA_FILES       =
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
 # 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
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
 # purple, and 360 is red again.
 # Minimum value: 0, maximum value: 359, default value: 220.
@@ -1163,6 +1207,17 @@ HTML_COLORSTYLE_GAMMA  = 80
 
 HTML_TIMESTAMP         = YES
 
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
 # page has loaded.
@@ -1186,13 +1241,13 @@ HTML_INDEX_NUM_ENTRIES = 100
 
 # If the GENERATE_DOCSET tag is set to YES, additional index files will be
 # generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
 # Makefile in the HTML output directory. Running make will produce the docset in
 # that directory and running make install will install the docset in
 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
@@ -1231,7 +1286,7 @@ DOCSET_PUBLISHER_NAME  = Publisher
 # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
 # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
 # Windows.
 #
 # The HTML Help Workshop contains a compiler that can convert all HTML output
@@ -1307,7 +1362,7 @@ QCH_FILE               =
 
 # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
 # Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
 # The default value is: org.doxygen.Project.
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1315,7 +1370,7 @@ QHP_NAMESPACE          = org.doxygen.Project
 
 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
 # Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
 # folders).
 # The default value is: doc.
 # This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1324,7 +1379,7 @@ QHP_VIRTUAL_FOLDER     = doc
 
 # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
 # filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1332,7 +1387,7 @@ QHP_CUST_FILTER_NAME   =
 
 # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
 # custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1340,7 +1395,7 @@ QHP_CUST_FILTER_ATTRS  =
 
 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
 # project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_SECT_FILTER_ATTRS  =
@@ -1433,7 +1488,7 @@ EXT_LINKS_IN_WINDOW    = NO
 
 FORMULA_FONTSIZE       = 10
 
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
 # generated for formulas are transparent PNGs. Transparent PNGs are not
 # supported properly for IE 6.0, but are supported on all modern browsers.
 #
@@ -1444,8 +1499,14 @@ FORMULA_FONTSIZE       = 10
 
 FORMULA_TRANSPARENT    = YES
 
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
 # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
 # 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
@@ -1472,8 +1533,8 @@ MATHJAX_FORMAT         = HTML-CSS
 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
 # Content Delivery Network so you can quickly see the result without installing
 # MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
@@ -1515,7 +1576,7 @@ MATHJAX_CODEFILE       =
 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
+# implemented using a web server instead of a web client using JavaScript. There
 # 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
@@ -1534,7 +1595,7 @@ SERVER_BASED_SEARCH    = NO
 #
 # 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/).
+# Xapian (see: https://xapian.org/).
 #
 # See the section "External Indexing and Searching" for details.
 # The default value is: NO.
@@ -1547,7 +1608,7 @@ EXTERNAL_SEARCH        = NO
 #
 # 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
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
 # Searching" for details.
 # This tag requires that the tag SEARCHENGINE is set to YES.
 
@@ -1599,21 +1660,35 @@ LATEX_OUTPUT           = latex
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
 # invoked.
 #
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
 # index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
 # The default file is: makeindex.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
 # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
 # documents. This may be useful for small projects and may help to save some
 # trees in general.
@@ -1629,7 +1704,7 @@ COMPACT_LATEX          = NO
 # The default value is: a4.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
-PAPER_TYPE             = a4wide
+PAPER_TYPE             = a4
 
 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
 # that should be included in the LaTeX output. The package can be specified just
@@ -1734,7 +1809,7 @@ LATEX_SOURCE_CODE      = NO
 
 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
 # bibliography, e.g. plainnat, or ieeetr. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
 # The default value is: plain.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
@@ -1748,6 +1823,14 @@ LATEX_BIB_STYLE        = plain
 
 LATEX_TIMESTAMP        = NO
 
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
 #---------------------------------------------------------------------------
 # Configuration options related to the RTF output
 #---------------------------------------------------------------------------
@@ -1787,9 +1870,9 @@ COMPACT_RTF            = NO
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
 #
 # See also section "Doxygen usage" for information on how to generate the
 # default style sheet that doxygen normally uses.
@@ -1798,8 +1881,8 @@ RTF_HYPERLINKS         = NO
 RTF_STYLESHEET_FILE    =
 
 # Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
 # This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_EXTENSIONS_FILE    =
@@ -1885,6 +1968,13 @@ XML_OUTPUT             = xml
 
 XML_PROGRAMLISTING     = YES
 
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
 #---------------------------------------------------------------------------
@@ -1917,9 +2007,9 @@ DOCBOOK_PROGRAMLISTING = NO
 #---------------------------------------------------------------------------
 
 # 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.
+# AutoGen Definitions (see http://autogen.sourceforge.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
@@ -2019,8 +2109,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = __GNUC__=4 \
-                         __GNUC_MINOR__=4
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
@@ -2087,12 +2176,6 @@ EXTERNAL_GROUPS        = YES
 
 EXTERNAL_PAGES         = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH              = /usr/bin/perl
-
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
@@ -2106,15 +2189,6 @@ PERL_PATH              = /usr/bin/perl
 
 CLASS_DIAGRAMS         = YES
 
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
 # You can include diagrams made with dia in doxygen documentation. Doxygen will
 # then run dia to produce the diagram and insert it in the documentation. The
 # DIA_PATH tag allows you to specify the directory where the dia binary resides.
@@ -2342,6 +2416,11 @@ DIAFILE_DIRS           =
 
 PLANTUML_JAR_PATH      =
 
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
 # When using plantuml, the specified paths are searched for files specified by
 # the !include statement in a plantuml block.
 
diff --git a/INSTALL b/INSTALL
index 45676b7ea44d0bb5cfc854272bdcbd5cf7a2e3a0..cabeb10ff0976c007003eb46222c883ce1b7b4b5 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -2,15 +2,15 @@ Any knowledge of how to work with mouse and icons is not required.
 
 Installing lopsub
 ~~~~~~~~~~~~~~~~~
-       git clone git://git.tuebingen.mpg.de/lopsub
+       git clone https://git.tuebingen.mpg.de/lopsub
        cd lopsub && make && sudo make install
-       (see http://people.tuebingen.mpg.de/maan/lopsub/)
+       (see https://people.tuebingen.mpg.de/maan/lopsub/)
 
 Installing osl
 ~~~~~~~~~~~~~~
-       git clone git://git.tuebingen.mpg.de/osl
+       git clone https://git.tuebingen.mpg.de/osl
        cd osl && make && sudo make install
-       (see http://people.tuebingen.mpg.de/maan/osl/)
+       (see https://people.tuebingen.mpg.de/maan/osl/)
 
 Installing paraslash from tarball
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -37,4 +37,4 @@ Example for cross-compiling
 
 For details see the user manual:
 
-       http://people.tuebingen.mpg.de/maan/paraslash/manual.html
+       https://people.tuebingen.mpg.de/maan/paraslash/manual.html
index dc658fe1b65747e66407e2f3f67f3b929f256d13..bd2bd9d95fe8356f09e756268340f2f4cad52706 100644 (file)
@@ -21,7 +21,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2022
+COPYRIGHT_YEAR := 2024
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -119,6 +119,7 @@ CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -I$(lls_suite_dir)
 CPPFLAGS += -I$(yy_build_dir)
 CPPFLAGS += $(lopsub_cppflags)
+CPPFLAGS += -Wunused-macros
 
 STRICT_CFLAGS += -fno-strict-aliasing
 STRICT_CFLAGS += -ftrapv
@@ -133,6 +134,11 @@ STRICT_CFLAGS += -Wredundant-decls
 STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
+STRICT_CFLAGS += -fdata-sections -ffunction-sections
+STRICT_CFLAGS += -Wstrict-prototypes
+STRICT_CFLAGS += -Wshadow
+
+LDFLAGS += -Wl,--gc-sections
 
 ifeq ($(ENABLE_UBSAN), yes)
        STRICT_CFLAGS += -fsanitize=undefined
@@ -141,12 +147,7 @@ endif
 
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
-       CPPFLAGS += -Wunused-macros
-       STRICT_CFLAGS += -fdata-sections -ffunction-sections
-       STRICT_CFLAGS += -Wstrict-prototypes
-       STRICT_CFLAGS += -Wshadow
        STRICT_CFLAGS += -Wunused -Wall
-       LDFLAGS += -Wl,--gc-sections
 endif
 
 cc-option = $(shell \
@@ -156,6 +157,7 @@ cc-option = $(shell \
 
 STRICT_CFLAGS += $(call cc-option, -Wformat-signedness)
 STRICT_CFLAGS += $(call cc-option, -Wdiscarded-qualifiers)
+STRICT_CFLAGS += $(call cc-option, -Wsuggest-attribute=malloc)
 
 # To put more focus on warnings, be less verbose as default
 # Use 'make V=1' to see the full commands
diff --git a/NEWS.md b/NEWS.md
index 5a00175c3255702b03ec86e0af1938863f53ba4d..d5812289640d71c52a60bd850928c72b3c5ba0b8 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,71 @@
 NEWS
 ====
 
+---------------------------------------------
+0.7.4 (to be announced) "genetic contraction"
+---------------------------------------------
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.xz)
+
+-----------------------------------------
+0.7.3 (2024-03-12) "weighted correctness"
+-----------------------------------------
+
+The highlight of this release is the new "ls --admissible=m/foo"
+feature described below. Other user-visible changes include minor
+additions to the "ls" and "select" server commands. The release also
+includes a fair number of cleanups for the crypto code and the file
+descriptor utilities, both without visible effects. Old ssh keys
+and outdated openssl library versions are now deprecated and cause
+warnings.
+
+- Old style PEM keys are now deprecated. They still work but their
+  use results in a run-time warning. The removal of PEM key support is
+  scheduled for paraslash-0.8.0.
+- Version 1.0 of the openssl library has been deprecated. A warning
+  is printed at compile-time on systems which have this outdated version
+  because it will no longer be supported once paraslash-0.8.0 comes out.
+- A spring cleanup for the senescent code in fd.c.
+- The --admissible option of the ls command now takes an optional
+  argument. When invoked like --admissible=m/foo, only files which are
+  admissible with respect to mood foo are listed.
+- The select server command is now quiet by default, The new --verbose
+  option can be used to show information about the newly loaded mood
+  or playlist.
+- The ls server command gained the --limit option to force a limit
+  on the number of files listed.
+- Cleanup of the openssl-specific code.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.3.tar.xz),
+[signature](./releases/paraslash-0.7.3.tar.xz.asc)
+
+-------------------------------------
+0.7.2 (2023-03-08) "optical friction"
+-------------------------------------
+
+The improved error reporting of afs commands and the two new options
+for the sleep subcommand of para_mixer are the most prominent features
+of this minor release. The bulk of the changes are cleanups of the
+afs and net subsystems, which should both have no user-visible impact.
+
+- A major cleanup of the audio file selector.
+- The client no longer prints error messages from afs commands to
+  stdout but to stderr.
+- The sleep subcommand of para_mixer gained two options to control
+  the startup mood and the time period before fade-out starts. A bunch
+  of further improvements for this subcommand went in as well.
+- Minor cleanup of the net subsystem.
+- The openssl specific code now employs the EVP API to compute hashes.
+  It should compile without warnings against openssl-3.
+- The deprecated syntax for specifying negative offsets in the argument
+  to the "ff" server command has been removed.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.2.tar.xz),
+[signature](./releases/paraslash-0.7.2.tar.xz.asc)
+
 --------------------------------------
 0.7.1 (2022-10-03) "digital spindrift"
 --------------------------------------
@@ -27,6 +92,7 @@ usual mix of bug fixes and minor improvements not mentioned here.
   requires support from the compiler, the oldest supported gcc version
   has been bumped to gcc-5.4 (released in 2015).
 
+Downloads:
 [tarball](./releases/paraslash-0.7.1.tar.xz),
 [signature](./releases/paraslash-0.7.1.tar.xz.asc)
 
@@ -219,6 +285,23 @@ Downloads:
 [tarball](./releases/paraslash-0.6.1.tar.xz),
 [signature](./releases/paraslash-0.6.1.tar.xz.asc)
 
+---------------------------------------
+0.5.9 (2021-11-04) "reversed dimension"
+---------------------------------------
+This release contains a few important fixes which have accumulated in
+the maint branch. The paraslash-0.5.x series has now reached its end
+of life and will no longer be supported. All users should upgrade to
+a more recent version at this point.
+
+- Fix an issue with the bash completion script.
+- Initialize the random seed also when using libgrypt.
+- Fix some compiler warnings in the resample filter
+- Don't return spurious errors from the ff server command.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.9.tar.bz2),
+[signature](./releases/paraslash-0.5.9.tar.bz2.asc)
+
 ---------------------------------------
 0.5.8 (2017-09-23) "branching parabola"
 ---------------------------------------
diff --git a/README b/README
index d8a545fc7abd744e6df57c51638dee607574e69e..510c7e1fd033e6898aa6ca7b96a673797e3d6bff 100644 (file)
--- a/README
+++ b/README
@@ -5,9 +5,9 @@ audio files. See the user manual for details.
 Distribution of paraslash is covered by the GNU GPL, version 2 unless
 otherwise stated. See file COPYING.
 
-Web page:             http://people.tuebingen.mpg.de/maan/paraslash/
-Gitweb:               http://git.tuebingen.mpg.de/paraslash.git/
-Git URL:              git://git.tuebingen.mpg.de/paraslash.git
+Web page:             https://people.tuebingen.mpg.de/maan/paraslash/
+Gitweb:               https://git.tuebingen.mpg.de/paraslash.git/
+Git URL:              https://git.tuebingen.mpg.de/paraslash.git
 Email:                Andre Noll <maan@tuebingen.mpg.de>
 
 Comments and bug reports are welcome.
index eebac67f9c80a270cd91337190ed9ad1eb569898..8449e787f96fa24b60c2c3af9ec5218eb264ff53 100644 (file)
@@ -205,7 +205,7 @@ static int afh_recv_post_monitor(__a_unused struct sched *s, void *context)
                        PARA_DEBUG_LOG("adding %u bytes\n", len);
                        btr_add_output_dont_free(start, len, btrn);
                }
-               ret = -E_RECV_EOF;
+               ret = -E_EOF;
                goto out;
        }
        if (pard->current_chunk == pard->first_chunk)
@@ -226,7 +226,7 @@ static int afh_recv_post_monitor(__a_unused struct sched *s, void *context)
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
        btr_add_output_dont_free(start, len, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
-               ret = -E_RECV_EOF;
+               ret = -E_EOF;
                goto out;
        }
        pard->current_chunk++;
diff --git a/afs.c b/afs.c
index 3da39f324b83b2a6d83d1f468732f74cc4313ae2..445d5871097b79cdcd14170c2a1f59998354dc98 100644 (file)
--- a/afs.c
+++ b/afs.c
 #include "sched.h"
 #include "fd.h"
 #include "signal.h"
-#include "mood.h"
 #include "sideband.h"
 #include "command.h"
 
-/** The osl tables used by afs. \sa \ref blob.c. */
-enum afs_table_num {
-       /** Contains audio file information. See \ref aft.c. */
-       TBLNUM_AUDIO_FILES,
-       /** The table for the paraslash attributes. See \ref attribute.c. */
-       TBLNUM_ATTRIBUTES,
-       /*
-        * Moods and playlists organize the current set of admissible files in
-        * an osl table which contains only volatile columns. Each row consists
-        * of a pointer to an audio file and the score value of this file.
-        */
-       TBLNUM_SCORES,
-       /**
-        * A standard blob table containing the mood definitions. For details
-        * see \ref mood.c.
-        */
-       TBLNUM_MOODS,
-       /** A blob table containing lyrics on a per-song basis. */
-       TBLNUM_LYRICS,
-       /** Another blob table for images (for example album cover art). */
-       TBLNUM_IMAGES,
-       /** Yet another blob table for storing standard playlists. */
-       TBLNUM_PLAYLIST,
-       /** How many tables are in use? */
-       NUM_AFS_TABLES
-};
-
-static struct afs_table afs_tables[NUM_AFS_TABLES] = {
-       [TBLNUM_AUDIO_FILES] = {.init = aft_init, .name = "audio_files"},
-       [TBLNUM_ATTRIBUTES] = {.init = attribute_init, .name = "attributes"},
-       [TBLNUM_SCORES] = {.init = score_init, .name = "scores"},
-       [TBLNUM_MOODS] = {.init = moods_init, .name = "moods"},
-       [TBLNUM_LYRICS] = {.init = lyrics_init, .name = "lyrics"},
-       [TBLNUM_IMAGES] = {.init = images_init, .name = "images"},
-       [TBLNUM_PLAYLIST] = {.init = playlists_init, .name = "playlists"},
+/**
+ * The array of tables of the audio file selector.
+ *
+ * We organize them in an array to be able to loop over all tables.
+ */
+static const struct afs_table {
+       /** The name is no table operation, so define it here. */
+       const char * const name;
+       /** The only way to invoke the ops is via this pointer. */
+       const struct afs_table_operations *ops;
+} afs_tables[] = {
+       {.name = "audio_files", .ops = &aft_ops},
+       {.name = "attributes", .ops = &attr_ops},
+       {.name = "scores", .ops = &score_ops},
+       {.name = "moods", .ops = &moods_ops},
+       {.name = "lyrics", .ops = &lyrics_ops},
+       {.name = "images", .ops = &images_ops},
+       {.name = "playlists", .ops = &playlists_ops},
 };
+/** Used to loop over the afs tables. */
+#define NUM_AFS_TABLES ARRAY_SIZE(afs_tables)
 
 struct command_task {
        /** The file descriptor for the local socket. */
@@ -116,7 +100,7 @@ extern uint32_t afs_socket_cookie;
  */
 struct callback_query {
        /** The function to be called. */
-       afs_callback *handler;
+       afs_callback *cb;
        /** The number of bytes of the query */
        size_t query_size;
 };
@@ -207,7 +191,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
        if (ret < 0)
                goto out;
        cq = query_shm;
-       cq->handler = f;
+       cq->cb = f;
        cq->query_size = query_shm_size - sizeof(*cq);
 
        if (query)
@@ -447,40 +431,30 @@ no_admissible_files:
        return write_all(server_socket, buf, 8);
 }
 
-static int activate_mood_or_playlist(const char *arg, int *num_admissible,
-               char **errmsg)
+static int activate_mood_or_playlist(const char *arg, struct para_buffer *pb)
 {
        enum play_mode mode;
        int ret;
+       char *msg;
 
-       if (!arg) {
+       if (!arg) { /* load dummy mood */
+               ret = mood_load(NULL, NULL, &msg);
+               mode = PLAY_MODE_MOOD;
+       } else if (!strncmp(arg, "p/", 2)) {
+               ret = playlist_load(arg + 2, NULL, &msg);
+               mode = PLAY_MODE_PLAYLIST;
+       } else if (!strncmp(arg, "m/", 2)) {
+               ret = mood_load(arg + 2, NULL, &msg);
                mode = PLAY_MODE_MOOD;
-               ret = change_current_mood(NULL, errmsg);
-               if (ret < 0) {
-                       if (num_admissible)
-                               *num_admissible = 0;
-                       return ret;
-               }
        } else {
-               if (!strncmp(arg, "p/", 2)) {
-                       ret = playlist_open(arg + 2);
-                       if (ret < 0 && errmsg)
-                               *errmsg = make_message( "could not open %s",
-                                       arg);
-                       mode = PLAY_MODE_PLAYLIST;
-               } else if (!strncmp(arg, "m/", 2)) {
-                       ret = change_current_mood(arg + 2, errmsg);
-                       mode = PLAY_MODE_MOOD;
-               } else {
-                       if (errmsg)
-                               *errmsg = make_message("%s: parse error", arg);
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               if (ret < 0)
-                       return ret;
+               ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               msg = make_message("%s: parse error\n", arg);
        }
-       if (num_admissible)
-               *num_admissible = ret;
+       if (pb)
+               para_printf(pb, "%s", msg);
+       free(msg);
+       if (ret < 0)
+               return ret;
        current_play_mode = mode;
        /*
         * We get called with arg == current_mop from the signal dispatcher
@@ -490,22 +464,15 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible,
         */
        if (arg != current_mop) {
                free(current_mop);
-               if (arg) {
-                       current_mop = para_strdup(arg);
-                       mutex_lock(mmd_mutex);
-                       strncpy(mmd->afs_mode_string, arg,
-                               sizeof(mmd->afs_mode_string));
-                       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-               } else {
-                       mutex_lock(mmd_mutex);
-                       strcpy(mmd->afs_mode_string, "dummy");
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-                       current_mop = NULL;
-               }
+               current_mop = arg? para_strdup(arg) : NULL;
        }
+       /* Notify the server about the mood/playlist change. */
+       mutex_lock(mmd_mutex);
+       strncpy(mmd->afs_mode_string, arg? arg: "dummy",
+               sizeof(mmd->afs_mode_string));
+       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
+       mmd->events++;
+       mutex_unlock(mmd_mutex);
        return 1;
 }
 
@@ -558,77 +525,14 @@ static void flush_and_free_pb(struct para_buffer *pb)
        free(pb->buf);
 }
 
-static int com_select_callback(struct afs_callback_arg *aca)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       const char *arg;
-       int num_admissible, ret;
-       char *errmsg;
-
-       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\n");
-               goto free_lpr;
-       }
-       if (current_play_mode == PLAY_MODE_MOOD)
-               close_current_mood();
-       else
-               playlist_close();
-       ret = activate_mood_or_playlist(arg, &num_admissible, &errmsg);
-       if (ret >= 0)
-               goto out;
-       /* ignore subsequent errors (but log them) */
-       para_printf(&aca->pbout, "%s\n", errmsg);
-       free(errmsg);
-       para_printf(&aca->pbout, "could not activate %s\n", arg);
-       if (current_mop && strcmp(current_mop, arg) != 0) {
-               int ret2;
-               para_printf(&aca->pbout, "switching back to %s\n", current_mop);
-               ret2 = activate_mood_or_playlist(current_mop, &num_admissible,
-                       &errmsg);
-               if (ret2 >= 0)
-                       goto out;
-               para_printf(&aca->pbout, "%s\n", errmsg);
-               free(errmsg);
-               para_printf(&aca->pbout, "could not reactivate %s: %s\n",
-                       current_mop, para_strerror(-ret2));
-       }
-       para_printf(&aca->pbout, "activating dummy mood\n");
-       activate_mood_or_playlist(NULL, &num_admissible, NULL);
-out:
-       para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n",
-               current_mop? current_mop : "dummy mood", num_admissible,
-                       num_admissible == 1? "" : "s");
-free_lpr:
-       lls_free_parse_result(aca->lpr, cmd);
-       return ret;
-}
-
-static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       char *errctx;
-       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
-
-       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(const char *arg)
 {
-       int ret = activate_mood_or_playlist(arg, NULL, NULL);
+       int ret = activate_mood_or_playlist(arg, NULL);
        if (ret < 0) {
-               PARA_WARNING_LOG("could not activate %s: %s\n", arg,
-                       para_strerror(-ret));
+               PARA_WARNING_LOG("could not activate %s: %s\n", arg?
+                       arg : "dummy", para_strerror(-ret));
                if (arg)
-                       activate_mood_or_playlist(NULL, NULL, NULL);
+                       activate_mood_or_playlist(NULL, NULL);
        }
 }
 
@@ -656,7 +560,7 @@ static void close_afs_tables(void)
        int i;
        PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].close();
+               afs_tables[i].ops->close();
        free(database_dir);
        database_dir = NULL;
 }
@@ -676,26 +580,15 @@ static void get_database_dir(void)
        PARA_INFO_LOG("afs_database dir %s\n", database_dir);
 }
 
-static int make_database_dir(void)
-{
-       int ret;
-
-       get_database_dir();
-       ret = para_mkdir(database_dir, 0777);
-       if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
-               return 1;
-       return ret;
-}
-
 static int open_afs_tables(void)
 {
        int i, ret;
 
        get_database_dir();
-       PARA_NOTICE_LOG("opening %d osl tables in %s\n", NUM_AFS_TABLES,
+       PARA_NOTICE_LOG("opening %zu osl tables in %s\n", NUM_AFS_TABLES,
                database_dir);
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               ret = afs_tables[i].open(database_dir);
+               ret = afs_tables[i].ops->open(database_dir);
                if (ret >= 0)
                        continue;
                PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
@@ -704,7 +597,7 @@ static int open_afs_tables(void)
        if (ret >= 0)
                return ret;
        while (i)
-               afs_tables[--i].close();
+               afs_tables[--i].ops->close();
        return ret;
 }
 
@@ -824,6 +717,43 @@ err:
        return ret;
 }
 
+/**
+ * Format and send an error message to the command handler.
+ *
+ * To pass an error message from the callback of an afs command to the client,
+ * this function should be called. It formats the message into a buffer which
+ * is passed as a shared memory area to the command handler from where it
+ * propagates to the client.
+ *
+ * The message will be tagged with the ERROR_LOG sideband designator so that
+ * the client writes it to its stderr stream rather than to stdout as with
+ * aca->pbout. In analogy to the default Unix semantics of stderr, the message
+ * is sent without buffering.
+ *
+ * If sending the error message fails, an error is logged on the server side,
+ * but no other action is taken.
+ *
+ * \param aca Used to obtain the fd to send the shmid to.
+ * \param fmt Usual format string.
+ */
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...)
+{
+       va_list argp;
+       char *msg;
+       unsigned n;
+       int ret;
+
+       va_start(argp, fmt);
+       n = xvasprintf(&msg, fmt, argp);
+       va_end(argp);
+       ret = pass_buffer_as_shm(aca->fd, SBD_ERROR_LOG, msg, n + 1);
+       if (ret < 0)
+               PARA_ERROR_LOG("Could not send %s: %s\n", msg,
+                       para_strerror(-ret));
+       free(msg);
+}
+
 static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
@@ -843,7 +773,7 @@ static int call_callback(int fd, int query_shmid)
                .fd = fd,
                .band = SBD_OUTPUT
        };
-       ret = cq->handler(&aca);
+       ret = cq->cb(&aca);
        ret2 = shm_detach(query_shm);
        if (ret2 < 0) {
                if (ret < 0) /* ignore (but log) detach error */
@@ -996,12 +926,10 @@ static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 __noreturn void afs_init(int socket_fd)
 {
        static struct sched s;
-       int i, ret;
+       int ret;
 
        register_signal_task(&s);
        init_list_head(&afs_client_list);
-       for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
        if (ret < 0)
                goto out;
@@ -1023,7 +951,8 @@ __noreturn void afs_init(int socket_fd)
        }
        ret = schedule(&s);
        sched_shutdown(&s);
-       close_current_mood();
+       mood_unload(NULL);
+       playlist_unload(NULL);
 out_close:
        close_afs_tables();
 out:
@@ -1036,6 +965,57 @@ out:
        exit(EXIT_FAILURE);
 }
 
+static int com_select_callback(struct afs_callback_arg *aca)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       const char *arg;
+       int ret;
+       struct para_buffer *pbout;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       arg = lls_input(0, aca->lpr);
+       pbout = SERVER_CMD_OPT_GIVEN(SELECT, VERBOSE, aca->lpr)?
+               &aca->pbout : NULL;
+       score_clear();
+       if (current_play_mode == PLAY_MODE_MOOD)
+               mood_unload(NULL);
+       else
+               playlist_unload(NULL);
+       ret = activate_mood_or_playlist(arg, pbout);
+       if (ret >= 0)
+               goto free_lpr;
+       /* ignore subsequent errors (but log them) */
+       if (current_mop && strcmp(current_mop, arg) != 0) {
+               int ret2;
+               afs_error(aca, "switching back to %s\n", current_mop);
+               ret2 = activate_mood_or_playlist(current_mop, pbout);
+               if (ret2 >= 0)
+                       goto free_lpr;
+               afs_error(aca, "could not reactivate %s: %s\n", current_mop,
+                       para_strerror(-ret2));
+       }
+       activate_mood_or_playlist(NULL, pbout);
+free_lpr:
+       lls_free_parse_result(aca->lpr, cmd);
+       return ret;
+}
+
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       ret = send_lls_callback_request(com_select_callback, cmd, lpr, cc);
+       return ret == osl(-E_OSL_RB_KEY_NOT_FOUND)? -E_BAD_MOP : ret;
+}
+EXPORT_SERVER_CMD_HANDLER(select);
+
 static int com_init_callback(struct afs_callback_arg *aca)
 {
        uint32_t table_mask = *(uint32_t *)aca->query.data;
@@ -1044,16 +1024,15 @@ static int com_init_callback(struct afs_callback_arg *aca)
        close_afs_tables();
        get_database_dir();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
+               const struct afs_table *t = afs_tables + i;
 
                if (!(table_mask & (1 << i)))
                        continue;
-               if (!t->create)
+               if (!t->ops->create)
                        continue;
-               ret = t->create(database_dir);
+               ret = t->ops->create(database_dir);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "cannot create table %s\n",
-                               t->name);
+                       afs_error(aca, "cannot create table %s\n", t->name);
                        goto out;
                }
                para_printf(&aca->pbout, "successfully created %s table\n",
@@ -1061,7 +1040,7 @@ static int com_init_callback(struct afs_callback_arg *aca)
        }
        ret = open_afs_tables();
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot open afs tables: %s\n",
+               afs_error(aca, "cannot open afs tables: %s\n",
                        para_strerror(-ret));
 out:
        return ret;
@@ -1075,14 +1054,15 @@ static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
                .size = sizeof(table_mask)};
        unsigned num_inputs = lls_num_inputs(lpr);
 
-       ret = make_database_dir();
+       get_database_dir();
+       ret = para_mkdir(database_dir);
        if (ret < 0)
                return ret;
        if (num_inputs > 0) {
                table_mask = 0;
                for (i = 0; i < num_inputs; i++) {
                        for (j = 0; j < NUM_AFS_TABLES; j++) {
-                               struct afs_table *t = &afs_tables[j];
+                               const struct afs_table *t = afs_tables + j;
 
                                if (strcmp(lls_input(i, lpr), t->name))
                                        continue;
@@ -1156,10 +1136,10 @@ __must_check int afs_event(enum afs_events event, struct para_buffer *pb,
        int i, ret;
 
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
-               if (!t->event_handler)
+               const struct afs_table *t = afs_tables + i;
+               if (!t->ops->event_handler)
                        continue;
-               ret = t->event_handler(event, pb, data);
+               ret = t->ops->event_handler(event, pb, data);
                if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %u: %s\n", t->name,
                                event, para_strerror(-ret));
diff --git a/afs.h b/afs.h
index b1606493a05f047afb1e8ecb0fdd0a5bbcbafcfe..e8b8c865bda36b9f905b2c1cb435d03bc12d3cbd 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -73,19 +73,15 @@ struct afsi_change_event_data {
        struct afs_info *old_afsi;
 };
 
-/** Function pointers for table handling.  */
-struct afs_table {
-       /** Initializes the other pointers in this struct. */
-       void (*init)(struct afs_table *t);
-       /** The name of this table. */
-       const char *name;
-       /** Gets called on startup and on \p SIGHUP. */
+/** Methods for table startup/shutdown and event handling. */
+struct afs_table_operations {
+       /** Gets called on startup and on SIGHUP. */
        int (*open)(const char *base_dir);
-       /** Gets called on shutdown and on \p SIGHUP. */
+       /** Gets called on shutdown and on SIGHUP. */
        void (*close)(void);
-       /** Called by the \a init afs command. */
+       /** Called from the init command. */
        int (*create)(const char *);
-       /** Handles afs events. */
+       /** Handle events generated by other tables. See enum \ref afs_events. */
        int (*event_handler)(enum afs_events event, struct para_buffer *pb,
                void *data);
 };
@@ -173,6 +169,8 @@ struct afs_callback_arg {
 };
 
 /**
+ * The "top half" of an afs command.
+ *
  * Afs command handlers run as a process which is not related to the afs
  * process, i.e. they can not change the address space of afs directly.
  * Therefore afs commands typically consist of two functions: The command
@@ -183,9 +181,13 @@ struct afs_callback_arg {
 typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
+ * Dispatch the output of an afs callback.
+ *
  * 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.
+ * related functions to dispatch the data in the command handler process. Most
+ * (but not all) afs commands pass \ref afs_cb_result_handler(), which sends
+ * the output of the callback to the connected client.
  */
 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);
@@ -229,30 +231,33 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
 int send_lls_callback_request(afs_callback *f,
                const struct lls_command * const cmd,
                struct lls_parse_result *lpr, void *private_result_data);
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...);
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
 int for_each_matching_row(struct pattern_match_data *pmd);
 
 /* score */
-void score_init(struct afs_table *t);
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func);
+extern const struct afs_table_operations score_ops;
+void score_open(struct osl_table **result);
+void score_close(struct osl_table *t);
+int score_loop(osl_rbtree_loop_func *func, struct osl_table *t, void *data);
 int score_get_best(struct osl_row **aft_row, long *score);
 int get_score_and_aft_row(struct osl_row *score_row, long *score, struct osl_row **aft_row);
-int score_add(const struct osl_row *row, long score);
+int score_add(const struct osl_row *aft_row, long score, struct osl_table *t);
 int score_update(const struct osl_row *aft_row, long new_score);
-int get_num_admissible_files(unsigned *num);
 int score_delete(const struct osl_row *aft_row);
-int clear_score_table(void);
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank);
+void score_clear(void);
+bool row_belongs_to_score_table(const struct osl_row *aft_row);
 
 /* attribute */
-void attribute_init(struct afs_table *t);
+extern const struct afs_table_operations attr_ops;
 void get_attribute_bitmap(const uint64_t *atts, char *buf); /* needed by com_ls() */
 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum);
 int get_attribute_text(uint64_t *atts, const char *delim, char **text);
 int attribute_check_callback(struct afs_callback_arg *aca);
 
 /* aft */
-void aft_init(struct afs_table *t);
+extern const struct afs_table_operations aft_ops;
 int aft_get_row_of_path(const char *path, struct osl_row **row);
 int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
 int open_and_update_audio_file(int *fd);
@@ -264,9 +269,18 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
 int aft_check_callback(struct afs_callback_arg *aca);
 void free_status_items(void);
 
+/* mood */
+struct mood_instance;
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg);
+int mood_loop(struct mood_instance *m, osl_rbtree_loop_func *func, void *data);
+void mood_unload(struct mood_instance *m);
+int mood_check_callback(struct afs_callback_arg *aca);
+
 /* playlist */
-int playlist_open(const char *name);
-void playlist_close(void);
+struct playlist_instance;
+int playlist_load(const char *name, struct playlist_instance **result, char **msg);
+int playlist_loop(struct playlist_instance *pi, osl_rbtree_loop_func *func, void *data);
+void playlist_unload(struct playlist_instance *pi);
 int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
@@ -275,15 +289,15 @@ int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** Define exported functions and a table pointer for an osl blob table. */
 #define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \
-       void table_name ## _init(struct afs_table *t); \
        int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
        int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def); \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def); \
        int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
                char **name, struct osl_object *def); \
        int table_name ##_event_handler(enum afs_events event, \
                struct para_buffer *pb, void *data); \
-       extern struct osl_table *table_name ## _table;
+       extern struct osl_table *table_name ## _table; \
+       extern const struct afs_table_operations table_name ## _ops;
 
 /** \cond blob_symbols */
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
diff --git a/aft.c b/aft.c
index f690e81951b4c7911c2f726b36fee9a673badce7..f1aca7fb8ccc3e31575303a4762d6a7ea35a054f 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -6,6 +6,7 @@
 #include <sys/mman.h>
 #include <fnmatch.h>
 #include <sys/shm.h>
+#include <dirent.h>
 #include <osl.h>
 #include <lopsub.h>
 
@@ -799,6 +800,12 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
        int ret, i;
        char *buf;
 
+       para_printf(b, "%s\nchunk_time: %lu:%lu\n", d->path,
+               (long unsigned) d->afhi.chunk_tv.tv_sec,
+               (long unsigned) d->afhi.chunk_tv.tv_usec
+       );
+       if (afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+               return 0;
        ret = aft_get_row_of_hash(d->hash, &aft_row);
        if (ret < 0)
                return ret;
@@ -806,12 +813,7 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0)
                return ret;
-       para_printf(b, "%s\n"
-               "chunk_time: %lu:%lu\nchunk_offsets: ",
-               d->path,
-               (long unsigned) d->afhi.chunk_tv.tv_sec,
-               (long unsigned) d->afhi.chunk_tv.tv_usec
-       );
+       para_printf(b, "chunk_offsets: ");
        buf = chunk_table_obj.data;
        for (
                i = 0;
@@ -912,7 +914,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                goto out;
        write_image_items(b, afsi);
        write_lyrics_items(b, afsi);
-       hash_to_asc(d->hash, asc_hash);
+       hash2_to_asc(d->hash, asc_hash);
        WRITE_STATUS_ITEM(b, SI_hash, "%s\n", asc_hash);
        WRITE_STATUS_ITEM(b, SI_bitrate, "%dkbit/s\n", afhi->bitrate);
        WRITE_STATUS_ITEM(b, SI_format, "%s\n",
@@ -1066,8 +1068,8 @@ again:
        if (ret < 0)
                return ret;
        if (!d->hash)
-               d->hash = alloc(HASH_SIZE);
-       memcpy(d->hash, tmp_hash, HASH_SIZE);
+               d->hash = alloc(HASH2_SIZE);
+       memcpy(d->hash, tmp_hash, HASH2_SIZE);
        free(d->path);
        ret = get_audio_file_path_of_row(current_aft_row, &d->path);
        if (ret < 0)
@@ -1101,7 +1103,7 @@ again:
        if (ret < 0)
                goto out;
        hash2_function(map.data, map.size, file_hash);
-       ret = hash_compare(file_hash, d->hash);
+       ret = hash2_compare(file_hash, d->hash);
        para_munmap(map.data, map.size);
        if (ret) {
                ret = -E_HASH_MISMATCH;
@@ -1360,28 +1362,67 @@ err:
        return ret;
 }
 
+static int mop_loop(const char *arg, struct afs_callback_arg *aca,
+               struct ls_options *opts)
+{
+       int ret;
+       char *msg;
+
+       if (!arg || strcmp(arg, ".") == 0)
+               return score_loop(prepare_ls_row, NULL, opts);
+       if (!strncmp(arg, "m/", 2)) {
+               struct mood_instance *m;
+               ret = mood_load(arg + 2, &m, &msg);
+               if (ret < 0)
+                       afs_error(aca, "%s", msg);
+               free(msg);
+               if (ret < 0)
+                       return ret;
+               ret = mood_loop(m, prepare_ls_row, opts);
+               mood_unload(m);
+               return ret;
+       }
+       if (!strncmp(arg, "p/", 2)) {
+               struct playlist_instance *pi;
+               ret = playlist_load(arg + 2, &pi, &msg);
+               if (ret < 0)
+                       afs_error(aca, "%s", msg);
+               free(msg);
+               if (ret < 0)
+                       return ret;
+               ret = playlist_loop(pi, prepare_ls_row, opts);
+               playlist_unload(pi);
+               return ret;
+       }
+       afs_error(aca, "bad mood/playlist specifier: %s\n", arg);
+       return -ERRNO_TO_PARA_ERROR(EINVAL);
+}
+
 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;
-       int i = 0, ret;
+       int ret;
        time_t current_time;
-       const struct lls_opt_result *r_r;
+       const struct lls_opt_result *r_r, *r_a;
+       uint32_t limit, k, n;
 
        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);
-
+       r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
        aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
-       if (admissible_only(opts))
-               ret = admissible_file_loop(opts, prepare_ls_row);
-       else
+       if (admissible_only(opts)) {
+               const char *arg = lls_string_val(0, r_a);
+               ret = mop_loop(arg, aca, opts);
+       } else
                ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
                        prepare_ls_row));
        if (ret < 0)
                goto out;
-       if (opts->num_matching_paths == 0) {
+       n = opts->num_matching_paths;
+       if (n == 0) {
                ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
                goto out;
        }
@@ -1389,20 +1430,14 @@ static int com_ls_callback(struct afs_callback_arg *aca)
        if (ret < 0)
                goto out;
        time(&current_time);
-       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);
-                       if (ret < 0)
-                               goto out;
-               }
-       else
-               for (i = 0; i < opts->num_matching_paths; i++) {
-                       ret = print_list_item(opts->data_ptr[i], opts,
-                               &aca->pbout, current_time);
-                       if (ret < 0)
-                               goto out;
-               }
+       limit = SERVER_CMD_UINT32_VAL(LS, LIMIT, opts->lpr);
+       for (k = 0; k < n && (limit == 0 || k < limit); k++) {
+               uint32_t idx = lls_opt_given(r_r)? n - 1 - k : k;
+               ret = print_list_item(opts->data_ptr[idx], opts, &aca->pbout,
+                       current_time);
+               if (ret < 0)
+                       goto out;
+       }
 out:
        lls_free_parse_result(opts->lpr, cmd);
        free(opts->data);
@@ -1443,7 +1478,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                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;
+                       opts->mode = LS_MODE_CHUNKS;
                else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
                        opts->mode = LS_MODE_PARSER;
                else {
@@ -1637,7 +1672,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
        char asc[2 * HASH2_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
-       char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
+       uint32_t slpr_offset = read_u32(buf + CAB_LPR_OFFSET);
+       char *slpr = buf + slpr_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);
@@ -1649,7 +1685,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
-       hash_to_asc(hash, asc);
+       hash2_to_asc(hash, asc);
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
        objs[AFTCOL_HASH].size = HASH2_SIZE;
 
@@ -1705,6 +1741,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        /* no hs or force mode, child must have sent afhi */
        afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
        chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
+       assert(chunks_offset <= slpr_offset);
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
@@ -1712,14 +1749,14 @@ static int com_add_callback(struct afs_callback_arg *aca)
        if (!objs[AFTCOL_AFHI].size) /* "impossible" */
                goto out;
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
-       objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
+       objs[AFTCOL_CHUNKS].size = slpr_offset - chunks_offset;
        if (pb && !hs) { /* update pb's hash */
                char old_asc[2 * HASH2_SIZE + 1];
                unsigned char *old_hash;
                ret = get_hash_of_row(pb, &old_hash);
                if (ret < 0)
                        goto out;
-               hash_to_asc(old_hash, old_asc);
+               hash2_to_asc(old_hash, old_asc);
                if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
@@ -1761,7 +1798,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "could not add %s\n", path);
+               afs_error(aca, "could not add %s\n", path);
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
@@ -1903,6 +1940,53 @@ out_free:
        return send_ret;
 }
 
+/*
+ * Call back once for each regular file below a directory.
+ *
+ * Traverse the given directory recursively and call the supplied callback for
+ * each regular file encountered. The first argument to the callback will be
+ * the path to the regular file and the second argument will be the data
+ * pointer. All file types except regular files and directories are ignored. In
+ * particular, symlinks are not followed. Subdirectories are ignored silently
+ * if the calling process has insufficient access permissions.
+ */
+static int for_each_file_in_dir(const char *dirname,
+               int (*func)(const char *, void *), void *data)
+{
+       int ret;
+       DIR *dir;
+       struct dirent *entry;
+
+       dir = opendir(dirname);
+       if (!dir)
+               return errno == EACCES? 1 : -ERRNO_TO_PARA_ERROR(errno);
+       /* scan cwd recursively */
+       while ((entry = readdir(dir))) {
+               char *tmp;
+               struct stat s;
+
+               if (!strcmp(entry->d_name, "."))
+                       continue;
+               if (!strcmp(entry->d_name, ".."))
+                       continue;
+               tmp = make_message("%s/%s", dirname, entry->d_name);
+               ret = 0;
+               if (lstat(tmp, &s) != -1) {
+                       if (S_ISREG(s.st_mode))
+                               ret = func(tmp, data);
+                       else if (S_ISDIR(s.st_mode))
+                               ret = for_each_file_in_dir(tmp, func, data);
+               }
+               free(tmp);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = 1;
+out:
+       closedir(dir);
+       return ret;
+}
+
 static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret;
@@ -1979,12 +2063,12 @@ static int touch_audio_file(__a_unused struct osl_table *table,
 
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        ret = load_afsi(&old_afsi, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        new_afsi = old_afsi;
@@ -2038,7 +2122,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                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: %u\n", id);
+                       afs_error(aca, "invalid image ID: %u\n", id);
                        return ret;
                }
        }
@@ -2047,7 +2131,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                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: %u\n", id);
+                       afs_error(aca, "invalid lyrics ID: %u\n", id);
                        return ret;
                }
        }
@@ -2090,7 +2174,7 @@ static int remove_audio_file(__a_unused struct osl_table *table,
                return ret;
        ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
        return ret;
 }
 
@@ -2326,7 +2410,7 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                free(p);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid argument: %s\n", arg);
+                       afs_error(aca, "invalid argument: %s\n", arg);
                        goto out;
                }
                if (c == '+')
@@ -2648,15 +2732,10 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
        }
 }
 
-/**
- * Initialize the audio file table.
- *
- * \param t Pointer to the structure to be initialized.
- */
-void aft_init(struct afs_table *t)
-{
-       t->open = aft_open;
-       t->close = aft_close;
-       t->create = aft_create;
-       t->event_handler = aft_event_handler;
-}
+/** The audio file table contains information about known audio files. */
+const struct afs_table_operations aft_ops = {
+       .open = aft_open,
+       .close = aft_close,
+       .create = aft_create,
+       .event_handler = aft_event_handler,
+};
index 92d6cb703b7a4911d0b5aa61282f703ce0bb490c..53d7b1b454932c36b3e578d794d688cee6cf7270 100644 (file)
@@ -277,7 +277,7 @@ again:
        bytes = btr_next_buffer(btrn, &data);
        if (ret < 0 || bytes < wn->min_iqs) { /* eof */
                assert(btr_no_parent(btrn));
-               ret = -E_WRITE_COMMON_EOF;
+               ret = -E_EOF;
                if (!pad)
                        goto err;
                /* wait until pending frames are played */
index 360e3fc2d7429bb7aa8219248ff4d38505a86ac2..be69ad67a8d78d419f3147546785130e42c7b80b 100644 (file)
@@ -67,7 +67,7 @@ next_buffer:
        in_bytes = btr_next_buffer(btrn, (char **)&in);
        len = in_bytes / 2;
        if (len == 0) { /* eof and in_bytes == 1 */
-               ret = -E_AMP_EOF;
+               ret = -E_EOF;
                goto err;
        }
 
index 950f8f73808e0b6528c0b73a2a43da58ec17f4ee..43a58dd6d5a364f2370659608302e6c7d9f24324 100644 (file)
@@ -266,7 +266,7 @@ static void *aow_play(void *priv)
                                if (frames > 0)
                                        break;
                                /* eof and less than a single frame available */
-                               ret = -E_WRITE_COMMON_EOF;
+                               ret = -E_EOF;
                                goto fail;
                        }
                        /*
@@ -388,7 +388,7 @@ static int aow_post_monitor(__a_unused struct sched *s, void *context)
        if (!wn->btrn) {
                if (!pawd->thread_btrn) {
                        pthread_join(pawd->thread, NULL);
-                       return -E_AO_EOF;
+                       return -E_EOF;
                }
                PARA_INFO_LOG("waiting for play thread to terminate\n");
                return 0;
index fb1b3eac3482f1c0725e0e79a4b80166be511350..51630b257f7d88829c657361b06c1fd09d008326 100644 (file)
@@ -119,7 +119,7 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "%s: %s\n", name, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
        para_printf(&aca->pbout, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
@@ -168,11 +168,6 @@ static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
 }
 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)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
@@ -188,12 +183,10 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
-               struct addatt_event_data aed;
 
                len = strlen(name);
                if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
-                       para_printf(&aca->pbout,
-                               "invalid attribute name: %s\n", name);
+                       afs_error(aca, "invalid attribute name: %s\n", name);
                        continue;
                }
                ret = get_attribute_bitnum_by_name(name, &bitnum);
@@ -225,16 +218,14 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
-               aed.name = name;
-               aed.bitnum = bitnum;
-               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
+               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, NULL);
                if (ret < 0)
                        goto out;
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "error while adding %s\n",
+               afs_error(aca, "error while adding %s\n",
                        lls_input(i, aca->lpr));
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
@@ -277,7 +268,7 @@ static int com_mvatt_callback(struct afs_callback_arg *aca)
        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);
+               afs_error(aca, "cannot rename %s to %s\n", old, new);
        else
                ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
        lls_free_parse_result(aca->lpr, cmd);
@@ -306,13 +297,13 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        para_printf(&aca->pbout, "removing attribute %s\n", name);
        ret = osl(osl_del_row(table, row));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return afs_event(ATTRIBUTE_REMOVE, &aca->pbout, &red);
@@ -499,14 +490,9 @@ static int attribute_create(const char *dir)
        return osl(osl_create_table(&attribute_table_desc));
 }
 
-/**
- * Initialize the attribute table structure.
- *
- * \param t The table structure to initialize.
- */
-void attribute_init(struct afs_table *t)
-{
-       t->open = attribute_open;
-       t->close = attribute_close;
-       t->create = attribute_create;
-}
+/** The attribute table stores name/bitnum pairs. */
+const struct afs_table_operations attr_ops = { /* no event handler */
+       .open = attribute_open,
+       .close = attribute_close,
+       .create = attribute_create,
+};
index d6c14cbcf7245a3a7db4bb3f86cf7e35efb8384b..f2e4cb91de228dcd78cae534e08d76e8514b6d88 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -175,7 +175,7 @@ static int audioc_post_monitor(struct sched *s, void *context)
        ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
-               ret = -E_AUDIOC_EOF;
+               ret = -E_EOF;
        if (ret < 0)
                goto out;
        btr_add_output(buf, ret, at->btrn);
index 0e8e5981085b7d05b8a2a08f35a9bb7944a4d72d..7c223995a9a29cec3c8e8ae5497c3be58655f63c 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -290,7 +290,7 @@ static int get_play_time_slot_num(void)
  *
  * \return A string that must be freed by the caller.
  */
-char *get_time_string(void)
+__malloc char *get_time_string(void)
 {
        int ret, seconds = 0, length = stat_task->length_seconds;
        struct timeval *tmp, sum, sss, /* server stream start */
@@ -958,7 +958,7 @@ static int init_default_filters(void)
                 */
                if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
                        tmp = para_strdup("fecdec");
-                       add_filter(i, tmp);
+                       ret = add_filter(i, tmp);
                        free(tmp);
                        if (ret < 0)
                                goto out;
index dedb038fbf772848330b024b93efabce58c1c9d3..39beda1bd4223d145fb327f07be4c5e5d4845d94 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -15,7 +15,7 @@ extern int audiod_status;
 struct btr_node *audiod_get_btr_root(void);
 __malloc char *audiod_get_decoder_flags(void);
 void clear_and_dump_items(void);
-char *get_time_string(void);
+__malloc char *get_time_string(void);
 bool uid_is_whitelisted(uid_t uid);
 
 /* defined in audiod_command.c */
index 6e2f8ee9e4aaf446267270e3be2398a4ee6d7c96..5f0b35a5dc0f3e4f02d134faa2ef828c6b357763 100644 (file)
@@ -81,27 +81,12 @@ static int num_clients;
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEMS};
 
-static void dump_stat_client_list(void)
-{
-       struct stat_client *sc;
-
-       list_for_each_entry(sc, &client_list, node)
-               PARA_INFO_LOG("stat client on fd %d\n", sc->fd);
-}
-/**
- * Add a status client to the list.
- *
- * \param fd The file descriptor of the client.
- * \param mask Bitfield of status items for this client.
- * \param parser_friendly Enable parser-friendly output mode.
+/*
+ * Add a status client to the global client list and increment num_clients.
  *
- * Only those status items having the bit set in \a mask will be
- * sent to the client.
- *
- * \return Positive value on success, or -E_TOO_MANY_CLIENTS if
- * the number of connected clients exceeds #MAX_STAT_CLIENTS.
+ * The mask parameter specifies which status items are sent to the client.
  */
-static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
+static int stat_client_add(int fd, uint64_t mask, bool parser_friendly)
 {
        struct stat_client *new_client;
        int ret;
@@ -121,7 +106,6 @@ static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
        if (parser_friendly)
                new_client->flags = SCF_PARSER_FRIENDLY;
        para_list_add(&new_client->node, &client_list);
-       dump_stat_client_list();
        num_clients++;
        return 1;
 }
@@ -180,20 +164,12 @@ void stat_client_write_item(int item_num)
                        continue;
                /* write error or short write */
                close_stat_client(sc);
-               dump_stat_client_list();
        }
        free(pb.buf);
        free(pfpb.buf);
 }
 
-/**
- * Check if string is a known status item.
- *
- * \param item Buffer containing the text to check.
- *
- * \return If \a item is a valid status item, the number of that status item is
- * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM.
- */
+/* Check if the given string is a known status item and return its index. */
 static int stat_item_valid(const char *item)
 {
        int i;
@@ -291,7 +267,8 @@ EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
 static int com_stat(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret, parser_friendly = 0;
+       int i, ret;
+       bool parser_friendly = false;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
@@ -303,7 +280,7 @@ static int com_stat(int fd, struct lls_parse_result *lpr)
                return ret;
        r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
        if (lls_opt_given(r) > 0) {
-               parser_friendly = 1;
+               parser_friendly = true;
                b.flags = PBF_SIZE_PREFIX;
        }
        num_inputs = lls_num_inputs(lpr);
diff --git a/blob.c b/blob.c
index b63724014138513dfb89d4300cb38056bfaa601a..1802de5d31f89bf6fe951c5977cbef4967f9c11e 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -100,7 +100,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot list %s\n", name);
+               afs_error(aca, "cannot list %s\n", name);
                return ret;
        }
        id = read_u32(obj.data);
@@ -210,7 +210,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        int ret = osl(osl_del_row(table, row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return 1;
@@ -338,7 +338,7 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        ret = afs_event(BLOB_ADD, NULL, table);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot add %s\n", name);
+               afs_error(aca, "cannot add %s\n", name);
        else
                para_printf(&aca->pbout, "added %s as id %u\n", name, id);
        return ret;
@@ -446,15 +446,14 @@ static int com_mvblob_callback(const struct lls_command * const cmd,
        ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot find source blob %s\n", src);
+               afs_error(aca, "cannot find source blob %s\n", src);
                goto out;
        }
        obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot rename blob %s to %s\n",
-                       src, dest);
+               afs_error(aca, "cannot rename blob %s to %s\n", src, dest);
                goto out;
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
@@ -520,11 +519,11 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
-static int blob_get_def_by_name(struct osl_table *table, char *name,
+static int blob_get_def_by_name(struct osl_table *table, const char *name,
                struct osl_object *def)
 {
        struct osl_row *row;
-       struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+       struct osl_object obj = {.data = (void *)name, .size = strlen(name) + 1};
        int ret;
 
        def->data = NULL;
@@ -538,7 +537,7 @@ static int blob_get_def_by_name(struct osl_table *table, char *name,
 
 /** Define the \p get_def_by_id function for this blob type. */
 #define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def) \
        { \
                return blob_get_def_by_name(table_name ## _table, name, def); \
        }
@@ -625,25 +624,21 @@ static int blob_open(struct osl_table **table,
                        &table_name ## _table_desc, dir); \
        }
 
-
-/** Define the \p init function for this blob type. */
-#define DEFINE_BLOB_INIT(table_name) \
-       void table_name ## _init(struct afs_table *t) \
-       { \
-               t->open = table_name ## _open; \
-               t->close = table_name ## _close; \
-               t->create = table_name ## _create;\
-               t->event_handler = table_name ##_event_handler; \
-               table_name ## _table = NULL; \
-       }
-
+/** Blob tables map integers to blobs. */
+#define DEFINE_BLOB_AFS_TABLE_OPS(table_name) \
+       const struct afs_table_operations table_name ## _ops = { \
+               .open = table_name ## _open, \
+               .close = table_name ## _close, \
+               .create = table_name ## _create, \
+               .event_handler = table_name ##_event_handler, \
+       };
 
 /** Define all functions for this blob type. */
 #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_AFS_TABLE_OPS(table_name) \
        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) \
index 05cdfa83630004e49561f0c7bef0138bd0a35c27..35353f56c9f69cb99be6bb6a319f680876d4a9cc 100644 (file)
@@ -1208,7 +1208,7 @@ int btr_node_status(struct btr_node *btrn, size_t min_iqs,
        if (type != BTR_NT_LEAF && btr_no_children(btrn))
                return -E_BTR_NO_CHILD;
        if (type != BTR_NT_ROOT && btr_eof(btrn))
-               return -E_BTR_EOF;
+               return -E_EOF;
 
        if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING)
                return 0;
index a6aee0f8b9afc79439032db3695ba9c82bb594a1..d2d11fd34344d5b4f5186daf568850fe153cbacb 100644 (file)
--- a/client.c
+++ b/client.c
@@ -549,6 +549,7 @@ __noreturn static void interactive_session(void)
        i9e_close();
        para_log = stderr_log;
 out:
+       free(ici.history_file);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
@@ -666,8 +667,6 @@ int main(int argc, char *argv[])
                        /* these are not errors */
                        case -E_SERVER_CMD_SUCCESS:
                        case -E_EOF:
-                       case -E_SERVER_EOF:
-                       case -E_BTR_EOF:
                                ret = 0;
                                break;
                        default: ret = -E_SERVER_CMD_FAILURE;
index 64f6c67612a5c665b87dcdb8d3e051bc7500b455..fe8234f98fe90f6ce74ea5e22699fbfc0059a042 100644 (file)
@@ -262,7 +262,7 @@ static bool has_feature(const char *feature, struct client_task *ct)
                return false;
        for (int i = 0; ct->features[i]; i++)
                if (strcmp(feature, ct->features[i]) == 0)
-                       return i;
+                       return true;
        return false;
 }
 
@@ -344,15 +344,18 @@ static int client_post_monitor(struct sched *s, void *context)
                        goto out;
                ct->challenge_hash = alloc(HASH2_SIZE);
                if (has_feature("sha256", ct)) {
-                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE,
+                               ct->challenge_hash);
                        hash2_to_asc(ct->challenge_hash, buf);
                } else {
-                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE,
+                               ct->challenge_hash);
                        hash_to_asc(ct->challenge_hash, buf);
                }
-               ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
-               ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN);
+               ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE,
+                        SESSION_KEY_LEN);
+               ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE
+                       + SESSION_KEY_LEN, SESSION_KEY_LEN);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
@@ -398,12 +401,12 @@ static int client_post_monitor(struct sched *s, void *context)
                        char *buf2;
                        size_t sz;
                        ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
-                       if (ret == -E_BTR_EOF) {
+                       if (ret == -E_EOF) {
                                /* empty blob data packet indicates EOF */
                                PARA_INFO_LOG("blob sent\n");
                                ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true);
                                if (ret >= 0)
-                                       ret = -E_BTR_EOF;
+                                       ret = -E_EOF;
                        }
                        if (ret < 0)
                                goto close1;
@@ -490,7 +493,7 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, host, port);
+       ret = para_connect(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
@@ -578,8 +581,9 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
                kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
        else {
+               struct stat statbuf;
                kf = make_message("%s/.paraslash/key.%s", home, user);
-               if (!file_exists(kf)) {
+               if (stat(kf, &statbuf) != 0) { /* assume file does not exist */
                        free(kf);
                        kf = make_message("%s/.ssh/id_rsa", home);
                }
index 00d2c5a61ada8fdcedfad2989e43844265ca46d5..bddb9cf0b16ed7387fc8f3520ff92db699dac28d 100644 (file)
--- a/command.c
+++ b/command.c
 #include "signal.h"
 #include "version.h"
 
+/** \cond server_cmd_aux_info */
 #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
+/** \endcond server_cmd_aux_info */
 
 /** Commands including options must be shorter than this. */
 #define MAX_COMMAND_LEN 32768
@@ -47,12 +49,14 @@ extern struct misc_meta_data *mmd;
 int send_afs_status(struct command_context *cc, int parser_friendly);
 static bool subcmd_should_die;
 
+/*
+ * Don't call PARA_XXX_LOG() here as we might already hold the log mutex. See
+ * generic_signal_handler() for details.
+ */
 static void command_handler_sighandler(int s)
 {
-       if (s != SIGTERM)
-               return;
-       PARA_EMERG_LOG("terminating on signal %d\n", SIGTERM);
-       subcmd_should_die = true;
+       if (s == SIGTERM)
+               subcmd_should_die = true;
 }
 
 /*
@@ -418,7 +422,8 @@ static int com_version(struct command_context *cc, struct lls_parse_result *lpr)
 }
 EXPORT_SERVER_CMD_HANDLER(version);
 
-/** These status items are cleared if no audio file is currently open. */
+/** \cond empty_status_items */
+/* These status items are cleared if no audio file is currently open. */
 #define EMPTY_STATUS_ITEMS \
        ITEM(path) \
        ITEM(directory) \
@@ -452,6 +457,8 @@ EXPORT_SERVER_CMD_HANDLER(version);
        ITEM(amplification) \
        ITEM(play_time) \
 
+/** \endcond empty_status_items */
+
 /*
  * Create a set of audio-file related status items with empty values. These are
  * written to stat clients when no audio file is open.
@@ -505,6 +512,7 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
         * while we sleep.
         */
        para_block_signal(SIGTERM);
+       para_block_signal(SIGUSR1);
        for (;;) {
                sigset_t set;
                /*
@@ -536,8 +544,10 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
                 * open a race window similar to the one described above.
                 */
                pselect(1, NULL, NULL, NULL, &ts, &set);
-               if (subcmd_should_die)
+               if (subcmd_should_die) {
+                       PARA_EMERG_LOG("terminating on SIGTERM\n");
                        goto out;
+               }
                ret = -E_SERVER_CRASH;
                if (getppid() == 1)
                        goto out;
@@ -705,7 +715,7 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
 {
        long promille;
        int i, ret;
-       char c, *errctx;
+       char *errctx;
 
        ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
        if (ret < 0) {
@@ -713,21 +723,8 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
                return ret;
        }
        ret = para_atoi32(lls_input(0, lpr), &i);
-       if (ret < 0) {
-               if (ret != -E_ATOI_JUNK_AT_END)
-                       return ret;
-               /*
-                * Compatibility code to keep the historic syntax (ff 30-)
-                * working. This can be removed after 0.7.0.
-                */
-               ret = sscanf(lls_input(0, lpr), "%i%c", &i, &c);
-               if (ret <= 0)
-                       return -E_COMMAND_SYNTAX;
-               if (ret > 1 && c == '-') {
-                       PARA_WARNING_LOG("use of obsolete syntax\n");
-                       i = -i;
-               }
-       }
+       if (ret < 0)
+               return ret;
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
@@ -819,19 +816,11 @@ static int parse_auth_request(char *buf, int len, const struct user **u,
                *p = '\0';
                p++;
                create_argv(p, ",", &features);
-               /*
-                * Still accept sideband and AES feature requests (as a no-op)
-                * because some 0.6.x clients request them. The two checks
-                * below may be removed after 0.7.1.
-                */
                for (i = 0; features[i]; i++) {
-                       if (strcmp(features[i], "sideband") == 0)
-                               continue;
-                       if (strcmp(features[i], "aes_ctr128") == 0)
-                               continue;
                        /*
-                        * ->sha256_requested can go away after 0.7.0 but the
-                        * check has to stay until 0.9.0.
+                        * ->sha256_requested can go away after 0.7.0 so that
+                        * sha256 is used unconditionally, but we need to
+                        * accept the feature request until 0.9.0.
                         */
                        if (strcmp(features[i], "sha256") == 0)
                                cf->sha256_requested = true;
index a6a001919acc5038039d5de0b0590a265052e978..1bce35f53f9ef3411a57bc6ea4564df6eea419b7 100644 (file)
@@ -59,7 +59,7 @@ next_buffer:
        btr_merge(btrn, fn->min_iqs);
        length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1;
        if (length == 0) { /* eof and 1 byte available */
-               ret = -E_COMPRESS_EOF;
+               ret = -E_EOF;
                goto err;
        }
        ip = (int16_t *)inbuf;
index c72581118326924a25b5ca9b020316439135d60b..b9ac3d0d47a4dc2adcfd8b6c28468aab35c50601 100644 (file)
@@ -1,7 +1,7 @@
 AC_PREREQ([2.61])
 
 AC_INIT([paraslash], [m4_esyscmd_s(./GIT-VERSION-GEN)],
-       [maan@tuebingen.mpg.de], [], [http://people.tuebingen.mpg.de/maan/paraslash/])
+       [maan@tuebingen.mpg.de], [], [https://people.tuebingen.mpg.de/maan/paraslash/])
 AC_CONFIG_HEADERS([config.h])
 
 AC_CONFIG_FILES([Makefile])
@@ -82,7 +82,7 @@ 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
+               git clone https://git.tuebingen.mpg.de/lopsub.git
        Install the library, then run this configure script again.
 ])
 fi
@@ -102,9 +102,14 @@ if test $HAVE_OPENSSL = yes; then
        if test "$ac_cv_have_decl_RSA_set0_key" != "$ac_cv_lib_crypto_RSA_set0_key"; then
                AC_MSG_ERROR([openssl header/library mismatch])
        fi
-       test "$ac_cv_have_decl_RSA_set0_key" = yes &&
+       if test "$ac_cv_have_decl_RSA_set0_key" = yes; then
                AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl >= 1.1])
-
+       else
+               AC_MSG_WARN([
+       Old openssl library detected. Support for openssl-1.0 and earlier
+       will be removed in the next major paraslash release. Please upgrade
+       your openssl installation.])
+       fi
        HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes
        AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [],
                [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no],
diff --git a/crypt.h b/crypt.h
index 5ca6a54112b60f6d61b16485248fb28ff708df6b..5578cd563fae4dc24d673e653dcf21329eba90bc 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -48,7 +48,7 @@ int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
  * \param key_file The file containing the key.
  * \param result The key structure is returned here.
  *
- * \return The size of the key on success, negative on errors.
+ * \return The size of the key in bytes on success, negative on errors.
  */
 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result);
 
index d7471235682855d3f376f0d5796b39567498937b..286ebe38f2215b879c3171183490b2199779f500 100644 (file)
@@ -295,7 +295,7 @@ int decode_private_key(const char *key_file, unsigned char **result,
                key_type = PKT_PEM;
                begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
                footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
-               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
+               PARA_WARNING_LOG("detected legacy PEM key %s\n", key_file);
        } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
                        strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
                key_type = PKT_OPENSSH;
index fe2f7abf2af5fdb9abb27dcfb41c9da705ebf91a..0b20bcc8b1149132c4a1fa81a238c4479cb60df6 100644 (file)
 #include "net.h"
 #include "fd.h"
 
+#ifndef DCCP_SOCKOPT_CCID
+#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
+#endif
+
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -59,6 +63,9 @@ static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
        return 1;
 }
 
+/** Flowopt shortcut */
+#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len)
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
        struct lls_parse_result *lpr = rn->lpr;
@@ -83,7 +90,7 @@ static int dccp_recv_open(struct receiver_node *rn)
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
+       fd = makesock(IPPROTO_DCCP, false, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
index 15a361babfa8570f61d2b51c15e60c83a8bab009..6182c964fde04719ee4736dc43482caf434ee395 100644 (file)
@@ -45,6 +45,10 @@ static void dccp_pre_monitor(struct sched *s)
                        sched_monitor_readfd(dss->listen_fds[n], s);
 }
 
+#ifndef DCCP_SOCKOPT_TX_CCID
+#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
+#endif
+
 /**
  * Query the TX CCID used on the sender-client half connection.
  * \param sockfd Server socket descriptor to query (after accept(2)).
@@ -89,6 +93,13 @@ static void dccp_shutdown(void)
        free_sender_status(dss);
 }
 
+#ifndef DCCP_SOCKOPT_GET_CUR_MPS
+#define DCCP_SOCKOPT_GET_CUR_MPS 5 /**< Max packet size, RFC 4340, 14. */
+#endif
+
+/** Estimated worst-case length of a DCCP header including options. */
+#define DCCP_MAX_HEADER 128
+
 /** * Obtain current MPS according to RFC 4340, sec. 14. */
 static int dccp_init_fec(struct sender_client *sc)
 {
diff --git a/error.h b/error.h
index 82920ea8007e0d17d9de4b02ead6d402c9a334b4..8805c9c7a4c0de6fe958e1523df883ad47effaad 100644 (file)
--- a/error.h
+++ b/error.h
@@ -2,6 +2,7 @@
 
 /** \file error.h List of error codes and messages. */
 
+/** \cond para_error */
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
        PARA_ERROR(ALSA_MIX_RANGE, "value control element out of range"), \
        PARA_ERROR(ALSA_MIX_SET_VAL, "could not set control element state"), \
-       PARA_ERROR(AMP_EOF, "amp: end of file"), \
        PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \
        PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
        PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
-       PARA_ERROR(AO_EOF, "ao: end of file"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
        PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
@@ -35,7 +34,6 @@
        PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
        PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \
        PARA_ERROR(ATT_TABLE_FULL, "no more space left in attribute table"), \
-       PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
        PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
        PARA_ERROR(AUDIOD_SIGNAL, "caught deadly signal"), \
        PARA_ERROR(AUDIOD_TERM, "terminating on user request"), \
@@ -50,6 +48,7 @@
        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_MOP, "invalid mood or playlist"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
@@ -63,7 +62,6 @@
        PARA_ERROR(BIGNUM, "bignum error"), \
        PARA_ERROR(BLINDING, "failed to activate key blinding"), \
        PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
-       PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \
        PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
        PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
        PARA_ERROR(CHILD_CONTEXT, "now running in child context"), \
@@ -71,7 +69,6 @@
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(CLIENT_WRITE, "client write error"), \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
-       PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
        PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
        PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
        PARA_ERROR(DECRYPT, "decrypt error"), \
@@ -82,7 +79,6 @@
        PARA_ERROR(EOF, "end of file"), \
        PARA_ERROR(EOP, "end of playlist"), \
        PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
-       PARA_ERROR(FECDEC_EOF, "received eof packet"), \
        PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
        PARA_ERROR(FEC_PARMS, "invalid fec parameters"), \
        PARA_ERROR(FEC_PIVOT, "pivot column not found"), \
@@ -95,7 +91,6 @@
        PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \
        PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
        PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
-       PARA_ERROR(FLACDEC_EOF, "flacdec encountered end of file condition"), \
        PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \
        PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \
        PARA_ERROR(FLAC_REPLACE_COMMENT, "could not replace vorbis comment"), \
        PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
        PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
        PARA_ERROR(HTTP_RECV_OVERRUN, "http_recv: output buffer overrun"), \
-       PARA_ERROR(I9E_EOF, "end of input"), \
        PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \
        PARA_ERROR(I9E_TERM_RQ, "received termination request"), \
        PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \
        PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
        PARA_ERROR(MOOD_PARSE, "mood parse 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(MP4_READ, "mp4: read error or unexpected end of file"), \
        PARA_ERROR(MP4_CORRUPT, "invalid/corrupt mp4 file"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
        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(OPUS_SET_GAIN, "opus: could not set gain"), \
        PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
-       PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private 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(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(SERVER_CMD_FAILURE, "command failed"), \
        PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
-       PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
        PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
        PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
        PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
        PARA_ERROR(VORBIS_COMMENTHEADER, "could not create vorbis comment header"), \
        PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
        PARA_ERROR(WAV_BAD_FC, "invalid filter configuration"), \
-       PARA_ERROR(WAV_EOF, "wav filter: end of file"), \
        PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \
        PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \
        PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \
        PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \
-       PARA_ERROR(WMADEC_EOF, "wmadec: end of file"), \
        PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
        PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \
-       PARA_ERROR(WRITE_COMMON_EOF, "end of file"), \
 
 /**
  * This is temporarily defined to expand to its first argument (prefixed by
@@ -261,6 +246,7 @@ enum para_error_codes {PARA_ERRORS};
 extern const char * const para_errlist[];
 /** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
+/** \endcond para_error */
 
 /**
  * This bit indicates whether a number is considered a system error number
@@ -304,18 +290,20 @@ static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror"
  */
 _static_inline_ const char *para_strerror(int num)
 {
+       unsigned idx = num & ~((1U << OSL_ERROR_BIT) | (1U << LLS_ERROR_BIT)
+               | (1U << SYSTEM_ERROR_BIT));
        assert(num > 0);
        if (IS_OSL_ERROR(num)) {
                assert(weak_osl_strerror);
-               return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
+               return weak_osl_strerror(idx);
        }
        if (IS_LLS_ERROR(num)) {
                assert(weak_lls_strerror);
-               return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+               return weak_lls_strerror(idx);
        }
        if (IS_SYSTEM_ERROR(num))
-               return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
-       return para_errlist[num];
+               return strerror(idx);
+       return para_errlist[idx];
 }
 
 /**
diff --git a/fd.c b/fd.c
index 763f756cdc3b5086ee9d26f432dbbfb760e02e4a..1af902f9af440cbf570377ed219eea08e76417a2 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -37,26 +37,27 @@ int xrename(const char *oldpath, const char *newpath)
 }
 
 /**
- * Write an array of buffers to a file descriptor.
+ * Write an array of buffers, handling non-fatal errors.
  *
- * \param fd The file descriptor.
+ * \param fd The file descriptor to write to.
  * \param iov Pointer to one or more buffers.
  * \param iovcnt The number of buffers.
  *
- * EAGAIN/EWOULDBLOCK is not considered a fatal error condition. For example
- * DCCP CCID3 has a sending wait queue which fills up and is emptied
- * asynchronously. The EAGAIN case means that there is currently no space in
- * the wait queue, but this can change at any moment.
+ * EAGAIN, EWOULDBLOCK and EINTR are not considered error conditions. If a
+ * write operation fails with EAGAIN or EWOULDBLOCK, the number of bytes that
+ * have been written so far is returned. In the EINTR case the operation is
+ * retried. Short writes are handled by issuing a subsequent write operation
+ * for the remaining part.
  *
  * \return Negative on fatal errors, number of bytes written else.
  *
  * For blocking file descriptors, this function returns either the sum of all
- * buffer sizes, or the error code of the fatal error that caused the last
- * write call to fail.
+ * buffer sizes or a negative error code which indicates the fatal error that
+ * caused a write call to fail.
  *
- * For nonblocking file descriptors there is a third possibility: Any positive
- * return value less than the sum of the buffer sizes indicates that some bytes
- * have been written but the next write would block.
+ * For nonblocking file descriptors there is a third possibility: Any
+ * non-negative return value less than the sum of the buffer sizes indicates
+ * that a write operation returned EAGAIN/EWOULDBLOCK.
  *
  * \sa writev(2), \ref xwrite().
  */
@@ -126,14 +127,15 @@ int xwrite(int fd, const char *buf, size_t len)
 }
 
 /**
- * Write all data to a file descriptor.
+ * Write to a file descriptor, fail on short writes.
  *
  * \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
+ * \param buf The buffer to be written.
+ * \param len The length of the buffer.
  *
- * This is like \ref xwrite() but returns \p -E_SHORT_WRITE if not
- * all data could be written.
+ * For blocking file descriptors this function behaves identical to \ref
+ * xwrite(). For non-blocking file descriptors it returns -E_SHORT_WRITE
+ * (rather than a value less than len) if not all data could be written.
  *
  * \return Number of bytes written on success, negative error code else.
  */
@@ -149,12 +151,20 @@ int write_all(int fd, const char *buf, size_t len)
 }
 
 /**
- * Write a buffer given by a format string.
+ * A fprintf-like function for raw file descriptors.
+ *
+ * This function creates a string buffer according to the given format and
+ * writes this buffer to a file descriptor.
  *
  * \param fd The file descriptor.
  * \param fmt A format string.
  *
+ * The difference to fprintf(3) is that the first argument is a file
+ * descriptor, not a FILE pointer. This function does not rely on stdio.
+ *
  * \return The return value of the underlying call to \ref write_all().
+ *
+ * \sa fprintf(3), \ref xvasprintf().
  */
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
 {
@@ -250,64 +260,44 @@ int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes)
 }
 
 /**
- * Read a buffer and check its content for a pattern.
- *
- * \param fd The file descriptor to receive from.
- * \param pattern The expected pattern.
- * \param bufsize The size of the internal buffer.
+ * Read a buffer and compare its contents to a string, ignoring case.
  *
- * This function tries to read at most \a bufsize bytes from the non-blocking
- * file descriptor \a fd. If at least \p strlen(\a pattern) bytes have been
- * received, the beginning of the received buffer is compared with \a pattern,
- * ignoring case.
+ * \param fd The file descriptor to read from.
+ * \param expectation The expected string to compare to.
  *
- * \return Positive if \a pattern was received, negative on errors, zero if no data
- * was available to read.
+ * The given file descriptor is expected to be in non-blocking mode. The string
+ * comparison is performed using strncasecmp(3).
  *
- * \sa \ref read_nonblock(), \sa strncasecmp(3).
+ * \return Zero if no data was available, positive if a buffer was read whose
+ * contents compare as equal to the expected string, negative otherwise.
+ * Possible errors: (a) not enough data was read, (b) the buffer contents
+ * compared as non-equal, (c) a read error occurred. In the first two cases,
+ * -E_READ_PATTERN is returned. In the read error case the (negative) return
+ * value of the underlying call to \ref read_nonblock() is returned.
  */
-int read_pattern(int fd, const char *pattern, size_t bufsize)
+int read_and_compare(int fd, const char *expectation)
 {
-       size_t n, len;
-       char *buf = alloc(bufsize + 1);
-       int ret = read_nonblock(fd, buf, bufsize, &n);
+       size_t n, len = strlen(expectation);
+       char *buf = alloc(len + 1);
+       int ret = read_nonblock(fd, buf, len, &n);
 
-       buf[n] = '\0';
        if (ret < 0)
                goto out;
+       buf[n] = '\0';
        ret = 0;
        if (n == 0)
                goto out;
        ret = -E_READ_PATTERN;
-       len = strlen(pattern);
        if (n < len)
                goto out;
-       if (strncasecmp(buf, pattern, len) != 0)
+       if (strncasecmp(buf, expectation, len) != 0)
                goto out;
        ret = 1;
 out:
-       if (ret < 0) {
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-               PARA_NOTICE_LOG("recvd %zu bytes: %s\n", n, buf);
-       }
        free(buf);
        return ret;
 }
 
-/**
- * Check whether a file exists.
- *
- * \param fn The file name.
- *
- * \return True iff file exists.
- */
-bool file_exists(const char *fn)
-{
-       struct stat statbuf;
-
-       return !stat(fn, &statbuf);
-}
-
 /**
  * Set a file descriptor to blocking mode.
  *
@@ -397,86 +387,32 @@ int para_open(const char *path, int flags, mode_t mode)
 }
 
 /**
- * Wrapper for chdir(2).
- *
- * \param path The specified directory.
+ * Create a directory, don't fail if it already exists.
  *
- * \return Standard.
- */
-int para_chdir(const char *path)
-{
-       int ret = chdir(path);
-
-       if (ret >= 0)
-               return 1;
-       return -ERRNO_TO_PARA_ERROR(errno);
-}
-
-/**
- * Save the cwd and open a given directory.
- *
- * \param dirname Path to the directory to open.
- * \param dir Result pointer.
- * \param cwd File descriptor of the current working directory.
- *
- * \return Standard.
- *
- * Opening the current directory (".") and calling fchdir() to return is
- * usually faster and more reliable than saving cwd in some buffer and calling
- * chdir() afterwards.
- *
- * If \a cwd is not \p NULL "." is opened and the resulting file descriptor is
- * stored in \a cwd. If the function returns success, and \a cwd is not \p
- * NULL, the caller must close this file descriptor (probably after calling
- * fchdir(*cwd)).
- *
- * On errors, the function undos everything, so the caller needs neither close
- * any files, nor change back to the original working directory.
+ * \param path Name of the directory to create.
  *
- * \sa getcwd(3).
+ * This function passes the fixed mode value 0777 to mkdir(3) (which consults
+ * the file creation mask and restricts this value).
  *
+ * \return Zero if the path already existed as a directory or as a symbolic
+ * link which leads to a directory, one if the path did not exist and the
+ * directory has been created successfully, negative error code else.
  */
-static int para_opendir(const char *dirname, DIR **dir, int *cwd)
+int para_mkdir(const char *path)
 {
-       int ret;
+       /*
+        * We call opendir(3) rather than relying on stat(2) because this way
+        * we don't need extra code to get the symlink case right.
+        */
+       DIR *dir = opendir(path);
 
-       *dir = NULL;
-       if (cwd) {
-               ret = para_open(".", O_RDONLY, 0);
-               if (ret < 0)
-                       return ret;
-               *cwd = ret;
-       }
-       ret = para_chdir(dirname);
-       if (ret < 0)
-               goto close_cwd;
-       *dir = opendir(".");
-       if (*dir)
-               return 1;
-       ret = -ERRNO_TO_PARA_ERROR(errno);
-       /* Ignore return value of fchdir() and close(). We're busted anyway. */
-       if (cwd) {
-               int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */
+       if (dir) {
+               closedir(dir);
+               return 0;
        }
-close_cwd:
-       if (cwd)
-               close(*cwd);
-       return ret;
-}
-
-/**
- * A wrapper for mkdir(2).
- *
- * \param path Name of the directory to create.
- * \param mode The permissions to use.
- *
- * \return Standard.
- */
-int para_mkdir(const char *path, mode_t mode)
-{
-       if (!mkdir(path, mode))
-               return 1;
-       return -ERRNO_TO_PARA_ERROR(errno);
+       if (errno != ENOENT)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       return mkdir(path, 0777) == 0? 1 : -ERRNO_TO_PARA_ERROR(errno);
 }
 
 /**
@@ -549,22 +485,21 @@ out:
  * \param start The start address of the memory mapping.
  * \param length The size of the mapping.
  *
- * \return Standard.
+ * If NULL is passed as the start address, the length value is ignored and the
+ * function does nothing.
+ *
+ * \return Zero if NULL was passed, one if the memory area was successfully
+ * unmapped, a negative error code otherwise.
  *
  * \sa munmap(2), \ref mmap_full_file().
  */
 int para_munmap(void *start, size_t length)
 {
-       int err;
-
        if (!start)
                return 0;
        if (munmap(start, length) >= 0)
                return 1;
-       err = errno;
-       PARA_ERROR_LOG("munmap (%p/%zu) failed: %s\n", start, length,
-               strerror(err));
-       return -ERRNO_TO_PARA_ERROR(err);
+       return -ERRNO_TO_PARA_ERROR(errno);
 }
 
 /**
@@ -643,64 +578,3 @@ void valid_fd_012(void)
                }
        }
 }
-
-/**
- * Traverse the given directory recursively.
- *
- * \param dirname The directory to traverse.
- * \param func The function to call for each entry.
- * \param private_data Pointer to an arbitrary data structure.
- *
- * For each regular file under \a dirname, the supplied function \a func is
- * called.  The full path of the regular file and the \a private_data pointer
- * are passed to \a func. Directories for which the calling process has no
- * permissions to change to are silently ignored.
- *
- * \return Standard.
- */
-int for_each_file_in_dir(const char *dirname,
-               int (*func)(const char *, void *), void *private_data)
-{
-       DIR *dir;
-       struct dirent *entry;
-       int cwd_fd, ret = para_opendir(dirname, &dir, &cwd_fd);
-
-       if (ret < 0)
-               return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret;
-       /* scan cwd recursively */
-       while ((entry = readdir(dir))) {
-               mode_t m;
-               char *tmp;
-               struct stat s;
-
-               if (!strcmp(entry->d_name, "."))
-                       continue;
-               if (!strcmp(entry->d_name, ".."))
-                       continue;
-               if (lstat(entry->d_name, &s) == -1)
-                       continue;
-               m = s.st_mode;
-               if (!S_ISREG(m) && !S_ISDIR(m))
-                       continue;
-               tmp = make_message("%s/%s", dirname, entry->d_name);
-               if (!S_ISDIR(m)) {
-                       ret = func(tmp, private_data);
-                       free(tmp);
-                       if (ret < 0)
-                               goto out;
-                       continue;
-               }
-               /* directory */
-               ret = for_each_file_in_dir(tmp, func, private_data);
-               free(tmp);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       closedir(dir);
-       if (fchdir(cwd_fd) < 0 && ret >= 0)
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-       close(cwd_fd);
-       return ret;
-}
diff --git a/fd.h b/fd.h
index 270d0ce200df30f16333fcfbdb47173500cacddc..e4f3090301b94c90c96452a94fb4b5fa11b7d801 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -5,14 +5,12 @@
 int xrename(const char *oldpath, const char *newpath);
 int write_all(int fd, const char *buf, size_t len);
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
-bool file_exists(const char *);
 int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
 __must_check int mark_fd_nonblocking(int fd);
 __must_check int mark_fd_blocking(int fd);
 int para_mmap(size_t length, int prot, int flags, int fd, void *map);
 int para_open(const char *path, int flags, mode_t mode);
-int para_mkdir(const char *path, mode_t mode);
-int para_chdir(const char *path);
+int para_mkdir(const char *path);
 int mmap_full_file(const char *filename, int open_mode, void **map,
        size_t *size, int *fd_ptr);
 int para_munmap(void *start, size_t length);
@@ -21,11 +19,10 @@ int write_ok(int fd);
 void valid_fd_012(void);
 int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes);
 int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes);
-int read_pattern(int fd, const char *pattern, size_t bufsize);
+int read_and_compare(int fd, const char *expectation);
 int xwrite(int fd, const char *buf, size_t len);
 int xwritev(int fd, struct iovec *iov, int iovcnt);
-int for_each_file_in_dir(const char *dirname,
-               int (*func)(const char *, void *), void *private_data);
+
 /**
  * Write a \p NULL-terminated buffer.
  *
index 1d8fbc16db71094d4f81b2c8c6bb88014087582c..375f4c0afb244b52e5d7b437a6b612c3da9d7f5b 100644 (file)
@@ -360,7 +360,7 @@ static int read_fec_header(char *buf, size_t len, struct fec_header *h)
        h->bos = read_u8(buf + 22);
        h->header_stream = read_u8(buf + 23);
        if (!memcmp(buf, FEC_EOF_PACKET, FEC_EOF_PACKET_LEN))
-               return -E_FECDEC_EOF;
+               return -E_EOF;
 //     PARA_DEBUG_LOG("group %u, slize %u, slices per group: %u\n",
 //             h->group_num, h->slice_num, h->slices_per_group);
        return 1;
index f3060f5721c1c22753cceb4229ee4dbe178dbf52..fb8ebf15d1ec664e39923c58515a3704441ab0da 100644 (file)
@@ -232,7 +232,7 @@ static int flacdec_post_monitor(__a_unused struct sched *s, void *context)
        if (output_queue_full(btrn))
                return 0;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
+       if (ret < 0 && ret != -E_EOF) /* fatal error */
                goto out;
        if (ret <= 0 && !pfd->have_more) /* nothing to do */
                goto out;
@@ -248,7 +248,7 @@ static int flacdec_post_monitor(__a_unused struct sched *s, void *context)
        pfd->have_more = false;
        FLAC__stream_decoder_process_single(pfd->decoder);
        state = FLAC__stream_decoder_get_state(pfd->decoder);
-       ret = -E_FLACDEC_EOF;
+       ret = -E_EOF;
        if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
                goto out;
        if (state == FLAC__STREAM_DECODER_ABORTED) {
index 763fa6de3252d2f5f188fc3f447f2029d0f5775a..b46f8f9555824eb86b7bbf600b4352558ed2db26 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -114,7 +114,6 @@ void crypt_shutdown(void)
 
 struct asymmetric_key {
        gcry_sexp_t sexp;
-       int num_bytes;
 };
 
 static const char *gcrypt_strerror(gcry_error_t gret)
@@ -457,10 +456,9 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        }
        PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
        key = alloc(sizeof(*key));
-       key->num_bytes = ret;
        key->sexp = sexp;
        *result = key;
-       ret = bits;
+       ret = bits / 8;
 release_n:
        gcry_mpi_release(n);
 release_e:
@@ -564,8 +562,6 @@ int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        size_t nbytes;
        int ret;
 
-       PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes);
-
        /* get pub key */
        pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
        if (!pub_key)
diff --git a/gui.c b/gui.c
index ebfab3564836e7b1708dc430e9375815b00d49b7..66fb7870bd65868a750b5918747da59ad4681b6d 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -1166,6 +1166,7 @@ static void com_cancel_scroll(void)
        }
        scroll_position = 0;
        redraw_bot_win();
+       print_in_bar(COLOR_MSG, " ");
 }
 
 static void com_page_down(void)
@@ -1267,6 +1268,12 @@ err_out:
        print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
 }
 
+static void print_ll_msg(void)
+{
+       const char *sev[] = {SEVERITIES};
+       print_in_bar(COLOR_MSG, "new loglevel: %s\n", sev[loglevel]);
+}
+
 static void com_ll_decr(void)
 {
        if (loglevel <= LL_DEBUG) {
@@ -1275,7 +1282,7 @@ static void com_ll_decr(void)
                return;
        }
        loglevel--;
-       print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+       print_ll_msg();
 }
 
 static void com_ll_incr(void)
@@ -1286,7 +1293,7 @@ static void com_ll_incr(void)
                return;
        }
        loglevel++;
-       print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+       print_ll_msg();
 }
 
 static void com_reread_conf(void)
index f71e802b5c6561d4d5b7340209a76cebf3235206..81bbe0f6760b82f88fb9f97f45f2f5a42ed494ab 100644 (file)
@@ -266,7 +266,7 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_amplification].align = RIGHT;
        d[SI_amplification].x = 92;
        d[SI_amplification].y = 27;
-       d[SI_amplification].len = 8;
+       d[SI_amplification].len = 9;
 
        d[SI_techinfo].prefix = "";
        d[SI_techinfo].postfix = "";
index 5aafacb840df1f863536fac1911c8d1caf8eb2b0..8d2add19091f4894814c1aeeaeeefee5d5c8c19e 100644 (file)
@@ -105,7 +105,7 @@ static int http_recv_post_monitor(struct sched *s, void *context)
                return 0;
        }
        if (phd->status == HTTP_SENT_GET_REQUEST) {
-               ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
+               ret = read_and_compare(rn->fd, HTTP_OK_MSG);
                if (ret < 0) {
                        PARA_ERROR_LOG("did not receive HTTP OK message\n");
                        goto out;
@@ -150,7 +150,7 @@ static int http_recv_open(struct receiver_node *rn)
        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);
+       int fd, ret = para_connect(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
index 90e3ee57d1b6d40152ef6fc4026331766e2524c5..429b4662a70497d522fd68a0e3467941e7edc959 100644 (file)
@@ -170,7 +170,7 @@ static void http_post_monitor(__a_unused struct sched *s)
                case HTTP_STREAMING: /* nothing to do */
                        break;
                case HTTP_CONNECTED: /* need to recv get request */
-                       ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE);
+                       ret = read_and_compare(sc->fd, HTTP_GET_MSG);
                        if (ret < 0)
                                phsd->status = HTTP_INVALID_GET_REQUEST;
                        else if (ret > 0) {
index e367a65920c53982af55436d7b96ea53138408f8..1376cf1d72739b3f7c9c3218dd96666a5d475812 100644 (file)
@@ -287,7 +287,7 @@ static int i9e_post_monitor(__a_unused struct sched *s, __a_unused void *context
        char *buf;
        size_t sz, consumed = 0;
 
-       ret = -E_I9E_EOF;
+       ret = -E_EOF;
        if (i9ep->input_eof)
                goto rm_btrn;
        ret = -E_I9E_TERM_RQ;
@@ -306,7 +306,7 @@ static int i9e_post_monitor(__a_unused struct sched *s, __a_unused void *context
                                        goto rm_btrn;
                                }
                                if (ret == 0) {
-                                       ret = -E_I9E_EOF;
+                                       ret = -E_EOF;
                                        goto rm_btrn;
                                }
                                buf[1] = '\0';
diff --git a/ipc.c b/ipc.c
index 8e9dd51a2369e7e60ebd4bda3fa889eaed892248..c245f690788630e19f2c7e82bda903360e1c0fa5 100644 (file)
--- a/ipc.c
+++ b/ipc.c
@@ -218,7 +218,7 @@ size_t shm_get_shmmax(void)
        {
                int fd = open("/proc/sys/kernel/shmmax", O_RDONLY);
                if (fd >= 0) {
-                       char buf[100] = "";
+                       char buf[100];
                        int ret = read(fd, buf, sizeof(buf) - 1);
                        if (ret > 0) {
                                buf[ret] = '\0';
index 3019f7693a1d986f53cfcea45d13e572a4295a5f..e366a201569e538bc374f5356bf3d1c957164955 100644 (file)
@@ -147,11 +147,10 @@ caption = List of subcommands
 [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.
+               Set the initial volume and mood, start playing and sleep. Then switch
+               to the fade-out mood and fade to the fade-out volume. Next, switch to
+               the sleep mood and wait until wake time minus fade-in time. Finally,
+               switch to the wake mood and fade in to the fade-in volume.
        [/description]
        [option ivol]
                summary = set initial volume
@@ -165,6 +164,26 @@ caption = List of subcommands
                        channel part may be omitted, in which case the default channel is
                        used. This option may be given multiple times.
                [/help]
+       [option initial-mood]
+               summary = mood or playlist to start with
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               [help]
+                       This mood or playlist is selected right after setting the initial
+                       volume and before fade-out starts. If unset, fade-out starts
+                       immediately.
+               [/help]
+       [option initial-delay]
+               summary = time before fade-out starts.
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 0
+               [help]
+                       If left at the default, no initial delay occurs even if an initial
+                       mood is given.
+               [/help]
        [option fo-mood]
                summary = mood or playlist for fade-out
                arg_info = required_arg
index 8200c6249449f81b5743775f04796462fc912b83..02afaabb5252a0580711547ad399cef932cfcadc 100644 (file)
@@ -222,6 +222,16 @@ m4_include(`com_ll.m4')
                        also given), chunk time and chunk offsets.
 
                [/help]
+       [option limit]
+               short_opt = L
+               summary = list at most this many files
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       An argument of zero means "unlimited". This is also the default which
+                       applies if the option is not given.
+               [/help]
        [option basename]
                short_opt = b
                summary = list and match basenames only
@@ -233,9 +243,19 @@ m4_include(`com_ll.m4')
        [option admissible]
                short_opt = a
                summary = list only admissible files
+               arg_type = string
+               arg_info = optional_arg
+               typestr = specifier/name
+               default_val = .
                [help]
-                       List only files which are admissible with respect to the current mood
-                       or playlist.
+                       If the optional argument is supplied, it must be of the form "p/foo"
+                       or "m/bar" (which refer to the playlist named "foo" and the mood named
+                       "bar", respectively). The command then restricts its output to the set
+                       of files which are admissible with respect to the thusly identified
+                       mood or playlist.
+
+                       If no argument is given, or if the argument is the special value ".",
+                       the current mood or playlist is assumed.
                [/help]
        [option reverse]
                short_opt = r
@@ -407,6 +427,9 @@ m4_include(`com_ll.m4')
 
                activates the mood named 'foo'.
        [/description]
+       [option verbose]
+               short_opt = v
+               summary = print information about the loaded mood or playlist
 
 [subcommand sender]
        purpose = control paraslash senders
diff --git a/mixer.c b/mixer.c
index eae8929164770053c5c18843f4cf4e4e8bc5e616..dda7fc1df729722266db7136733a28ac2391e977 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -187,11 +187,11 @@ static int com_fade(const struct mixer *m)
 }
 EXPORT_CMD(fade);
 
-static void client_cmd(const char *cmd)
+static void run(const char *exe, const char *cmd)
 {
        int ret, status, fds[3] = {0, 0, 0};
        pid_t pid;
-       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+       char *cmdline = make_message("%s %s", exe, cmd);
 
        PARA_NOTICE_LOG("%s\n", cmdline);
        ret = para_exec_cmdline_pid(&pid, cmdline, fds);
@@ -215,6 +215,16 @@ fail:
        exit(EXIT_FAILURE);
 }
 
+static void client_cmd(const char *cmd)
+{
+       run(BINDIR "/para_client", cmd);
+}
+
+static void audioc_cmd(const char *cmd)
+{
+       run(BINDIR "/para_audioc", cmd);
+}
+
 static void change_afs_mode(const char *afs_mode)
 {
        char *cmd;
@@ -263,6 +273,20 @@ static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
        return 1;
 }
 
+static void stop(const struct mixer *m, struct mixer_handle *h)
+{
+       int ret, old_vol = 0;
+
+       ret = m->get(h);
+       if (ret > 0)
+               old_vol = ret;
+       fade(m, h, 0, 3);
+       audioc_cmd("off");
+       client_cmd("stop");
+       audioc_cmd("on");
+       m->set(h, old_vol);
+}
+
 static int com_sleep(const struct mixer *m)
 {
        time_t t1, wake_time_epoch;
@@ -270,6 +294,7 @@ static int com_sleep(const struct mixer *m)
        struct tm *tm;
        int ret;
        const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+       const char *initial_mood = OPT_STRING_VAL(SLEEP, INITIAL_MOOD);
        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);
@@ -316,17 +341,27 @@ static int com_sleep(const struct mixer *m)
        }
        wake_time_epoch = mktime(tm);
        PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
-       client_cmd("stop");
-       sleep(1);
+       stop(m, h);
+       ret = set_initial_volume(m, h);
+       if (ret < 0)
+               goto close_mixer;
+       /*
+        * Setting the volume invalidates the current channel setting, so we
+        * have to set it again.
+        */
+       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret < 0)
+               goto close_mixer;
+       delay = OPT_UINT32_VAL(SLEEP, INITIAL_DELAY);
+       if (delay > 0 && initial_mood && *initial_mood) {
+               change_afs_mode(initial_mood);
+               client_cmd("play");
+               sleep(delay);
+               stop(m, h);
+       }
        if (fot && fo_mood && *fo_mood) {
-               ret = set_initial_volume(m, h);
-               if (ret < 0)
-                       goto close_mixer;
                change_afs_mode(fo_mood);
                client_cmd("play");
-               ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
-               if (ret < 0)
-                       goto close_mixer;
                ret = fade(m, h, fov, fot);
                if (ret < 0)
                        goto close_mixer;
@@ -340,7 +375,7 @@ static int com_sleep(const struct mixer *m)
                if (!fot || !fo_mood) /* currently stopped */
                        client_cmd("play");
        } else if (fot && fo_mood && *fo_mood) /* currently playing */
-               client_cmd("stop");
+               stop(m, h);
        m->close(&h);
        if (!fit || !fi_mood || !*fi_mood) /* nothing to do */
                return 1;
@@ -355,13 +390,12 @@ static int com_sleep(const struct mixer *m)
                sleep(delay);
        }
        change_afs_mode(fi_mood);
-       if (sleep_mood && *sleep_mood) /* currently playing */
-               client_cmd("next");
-       else /* currently stopped */
-               client_cmd("play");
        ret = open_mixer_and_set_channel(m, &h);
        if (ret < 0)
                return ret;
+       if (sleep_mood && *sleep_mood) /* currently playing */
+               stop(m, h);
+       client_cmd("play");
        ret = fade(m, h, fiv, fit);
 close_mixer:
        m->close(&h);
diff --git a/mood.c b/mood.c
index d47c54efa360b856c63b660fd4cdb5b81d442b68..1e15ef0e081480381fcbc4fdad2179848f429c1f 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mood.h"
 
 /*
  * Mood parser API. It's overkill to have an own header file for
@@ -47,20 +46,31 @@ struct afs_statistics {
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics = {.normalization_divisor = 1};
 
-struct mood {
-       /** The name of this mood. */
+/**
+ * Stores an instance of a loaded mood (parser and statistics).
+ *
+ * A structure of this type is allocated and initialized when a mood is loaded.
+ */
+struct mood_instance {
+       /** NULL means that this is the "dummy" mood. */
        char *name;
-       /** Info for the bison parser. */
+       /** Bison's abstract syntax tree, used to determine admissibility. */
        struct mp_context *parser_context;
+       /** To compute the score. */
+       struct afs_statistics stats;
+       /** NULL means to operate on the global score table. */
+       struct osl_table *score_table;
 };
 
 /*
- * If current_mood is NULL then no mood is currently open. If
- * current_mood->name is NULL, the dummy mood is currently open.
+ * If current_mood is NULL then no mood is currently loaded. If
+ * current_mood->name is NULL, the current mood is the dummy mood.
+ *
+ * The statistics are adjusted dynamically through this pointer as files are
+ * added, removed or played.
  */
-static struct mood *current_mood;
+static struct mood_instance *current_mood;
 
 /*
  * Find the position of the most-significant set bit.
@@ -119,49 +129,47 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
-{
-       if (!m)
-               return -E_NO_MOOD;
-       return mp_eval_row(aft_row, m->parser_context);
-}
-
-static void destroy_mood(struct mood *m)
+static void destroy_mood(struct mood_instance *m)
 {
        if (!m)
                return;
        mp_shutdown(m->parser_context);
+       if (m->score_table)
+               score_close(m->score_table);
        free(m->name);
        free(m);
 }
 
-static struct mood *alloc_new_mood(const char *name)
+static struct mood_instance *alloc_new_mood(const char *name)
 {
-       struct mood *m = zalloc(sizeof(struct mood));
+       struct mood_instance *m = zalloc(sizeof(*m));
+
        if (name)
                m->name = para_strdup(name);
+       m->stats.normalization_divisor = 1;
        return m;
 }
 
-static int load_mood(const struct osl_row *mood_row, struct mood **m,
-               char **errmsg)
+static int init_mood_parser(const char *mood_name, struct mood_instance **m,
+               char **err)
 {
-       char *mood_name;
        struct osl_object mood_def;
        int ret;
 
-       ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+       if (!*mood_name) {
+               if (err)
+                       *err = make_message("empty mood name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = mood_get_def_by_name(mood_name, &mood_def);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message(
-                               "could not read mood definition");
+               if (err)
+                       *err = make_message("could not read mood definition\n");
                return ret;
        }
-       assert(*mood_name);
        *m = alloc_new_mood(mood_name);
-       PARA_INFO_LOG("opening mood %s\n", mood_name);
-       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
+       PARA_INFO_LOG("loading mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, err);
        osl_close_disk_object(&mood_def);
        if (ret < 0)
                destroy_mood(*m);
@@ -170,14 +178,14 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood *m;
+       struct mood_instance *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "cannot read mood\n");
+               afs_error(aca, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
@@ -186,9 +194,9 @@ static int check_mood(struct osl_row *mood_row, void *data)
        ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
                &errmsg);
        if (ret < 0) {
-               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               afs_error(aca, "%s: %s\n%s\n", mood_name, errmsg,
+                       para_strerror(-ret));
                free(errmsg);
-               para_printf(pb, "%s\n", para_strerror(-ret));
        } else
                destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
@@ -200,7 +208,7 @@ out:
 /**
  * Check all moods for syntax errors.
  *
- * \param aca Only ->pbout is used for diagnostics.
+ * \param aca Output goes to ->pbout, errors to ->fd on the error band.
  *
  * \return Negative on fatal errors. Inconsistent mood definitions are not
  * considered an error.
@@ -208,8 +216,7 @@ out:
 int mood_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking moods...\n");
-       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
-               check_mood));
+       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, aca, check_mood));
 }
 
 /*
@@ -249,25 +256,27 @@ int mood_check_callback(struct afs_callback_arg *aca)
  * overflows and rounding errors we store the common divisor of the
  * correction factors separately.
  */
-static long compute_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi,
+               const struct afs_statistics *stats)
 {
        int64_t mean_n, mean_l,score_n, score_l;
 
-       assert(statistics.normalization_divisor > 0);
-       assert(statistics.num > 0);
-       mean_n = statistics.num_played_sum / statistics.num;
-       mean_l = statistics.last_played_sum / statistics.num;
+       assert(stats->normalization_divisor > 0);
+       assert(stats->num > 0);
+       mean_n = stats->num_played_sum / stats->num;
+       mean_l = stats->last_played_sum / stats->num;
 
        score_n = -((int64_t)afsi->num_played - mean_n)
-               * statistics.num_played_correction
-               / statistics.normalization_divisor;
+               * stats->num_played_correction
+               / stats->normalization_divisor;
        score_l = -((int64_t)afsi->last_played - mean_l)
-               * statistics.last_played_correction
-               / statistics.normalization_divisor;
+               * stats->last_played_correction
+               / stats->normalization_divisor;
        return (score_n + score_l) / 2;
 }
 
-static int add_afs_statistics(const struct osl_row *row)
+static int add_afs_statistics(const struct osl_row *row,
+               struct afs_statistics *stats)
 {
        uint64_t n, x, s, q;
        struct afs_info afsi;
@@ -276,64 +285,65 @@ static int add_afs_statistics(const struct osl_row *row)
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        x = afsi.last_played;
-       s = statistics.last_played_sum;
+       s = stats->last_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.last_played_qd += q * q * n / (n + 1);
+               stats->last_played_qd += q * q * n / (n + 1);
        }
-       statistics.last_played_sum += x;
+       stats->last_played_sum += x;
 
        x = afsi.num_played;
-       s = statistics.num_played_sum;
+       s = stats->num_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.num_played_qd += q * q * n / (n + 1);
+               stats->num_played_qd += q * q * n / (n + 1);
        }
-       statistics.num_played_sum += x;
-       statistics.num++;
+       stats->num_played_sum += x;
+       stats->num++;
        return 1;
 }
 
 static int del_afs_statistics(const struct osl_row *row)
 {
+       struct afs_statistics *stats = &current_mood->stats;
        uint64_t n, s, q, a, new_s;
        struct afs_info afsi;
        int ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        assert(n);
        if (n == 1) {
-               memset(&statistics, 0, sizeof(statistics));
-               statistics.normalization_divisor = 1;
+               memset(stats, 0, sizeof(*stats));
+               stats->normalization_divisor = 1;
                return 1;
        }
 
-       s = statistics.last_played_sum;
-       q = statistics.last_played_qd;
+       s = stats->last_played_sum;
+       q = stats->last_played_qd;
        a = afsi.last_played;
        new_s = s - a;
-       statistics.last_played_sum = new_s;
-       statistics.last_played_qd = q + s * s / n - a * a
+       stats->last_played_sum = new_s;
+       stats->last_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       s = statistics.num_played_sum;
-       q = statistics.num_played_qd;
+       s = stats->num_played_sum;
+       q = stats->num_played_qd;
        a = afsi.num_played;
        new_s = s - a;
-       statistics.num_played_sum = new_s;
-       statistics.num_played_qd = q + s * s / n - a * a
+       stats->num_played_sum = new_s;
+       stats->num_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       statistics.num--;
+       stats->num--;
        return 1;
 }
 
 /*
- * At mood open time we determine the set of admissible files for the given
+ * At mood load time we determine the set of admissible files for the given
  * mood where each file is identified by a pointer to a row of the audio file
  * table. In the first pass the pointers are added to a temporary array and
  * statistics are computed. When all admissible files have been processed in
@@ -343,7 +353,7 @@ static int del_afs_statistics(const struct osl_row *row)
  */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
-       struct mood *m;
+       struct mood_instance *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
@@ -357,19 +367,18 @@ struct admissible_array {
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
-       int ret;
+       struct afs_statistics *stats = &aa->m->stats;
 
-       ret = row_is_admissible(aft_row, aa->m);
-       if (ret <= 0)
-               return ret;
-       if (statistics.num >= aa->size) {
+       if (!mp_eval_row(aft_row, aa->m->parser_context))
+               return 0;
+       if (stats->num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
                aa->array = arr_realloc(aa->array, aa->size,
                        sizeof(struct osl_row *));
        }
-       aa->array[statistics.num] = aft_row;
-       return add_afs_statistics(aft_row);
+       aa->array[stats->num] = aft_row;
+       return add_afs_statistics(aft_row, stats);
 }
 
 /**
@@ -414,29 +423,25 @@ _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,
+static void update_afs_statistics(struct afs_info *old_afsi,
                struct afs_info *new_afsi)
 {
-       unsigned n;
-       int ret = get_num_admissible_files(&n);
-
-       if (ret < 0)
-               return ret;
-       assert(n);
-
-       statistics.last_played_qd = update_quadratic_deviation(n,
-               statistics.last_played_qd, old_afsi->last_played,
-               new_afsi->last_played, statistics.last_played_sum);
-       statistics.last_played_sum += new_afsi->last_played - old_afsi->last_played;
-
-       statistics.num_played_qd = update_quadratic_deviation(n,
-               statistics.num_played_qd, old_afsi->num_played,
-               new_afsi->num_played, statistics.num_played_sum);
-       statistics.num_played_sum += new_afsi->num_played - old_afsi->num_played;
-       return 1;
+       struct afs_statistics *stats = &current_mood->stats;
+
+       assert(stats->num > 0);
+       stats->last_played_qd = update_quadratic_deviation(stats->num,
+               stats->last_played_qd, old_afsi->last_played,
+               new_afsi->last_played, stats->last_played_sum);
+       stats->last_played_sum += new_afsi->last_played - old_afsi->last_played;
+
+       stats->num_played_qd = update_quadratic_deviation(stats->num,
+               stats->num_played_qd, old_afsi->num_played,
+               new_afsi->num_played, stats->num_played_sum);
+       stats->num_played_sum += new_afsi->num_played - old_afsi->num_played;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row)
+static int add_to_score_table(const struct osl_row *aft_row,
+               struct mood_instance *m)
 {
        long score;
        struct afs_info afsi;
@@ -444,8 +449,8 @@ static int add_to_score_table(const struct osl_row *aft_row)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi);
-       return score_add(aft_row, score);
+       score = compute_score(&afsi, &m->stats);
+       return score_add(aft_row, score, m->score_table);
 }
 
 static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
@@ -457,23 +462,18 @@ static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
 }
 
 /**
- * Delete one entry from the statistics and from the score table.
+ * Delete an audio file from the score table and update mood statistics.
  *
- * \param aft_row The audio file which is no longer admissible.
+ * \param aft_row Identifies the row to delete.
  *
- * \return Positive on success, negative on errors.
+ * \return Standard.
  *
  * \sa \ref score_delete().
  */
 static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
-       int ret;
-
-       ret = row_belongs_to_score_table(aft_row, NULL);
-       if (ret < 0)
-               return ret;
-       if (!ret) /* not admissible, nothing to do */
-               return 1;
+       if (!row_belongs_to_score_table(aft_row))
+               return 0;
        return delete_from_statistics_and_score_table(aft_row);
 }
 
@@ -492,95 +492,84 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                struct afs_info *old_afsi)
 {
        long score, percent;
-       int ret, is_admissible, was_admissible = 0;
+       int ret;
+       bool is_admissible, was_admissible;
        struct afs_info afsi;
-       unsigned rank;
 
        if (!current_mood)
                return 1; /* nothing to do */
-       ret = row_belongs_to_score_table(aft_row, &rank);
-       if (ret < 0)
-               return ret;
-       was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood);
-       if (ret < 0)
-               return ret;
-       is_admissible = (ret > 0);
+       was_admissible = row_belongs_to_score_table(aft_row);
+       is_admissible = mp_eval_row(aft_row, current_mood->parser_context);
        if (!was_admissible && !is_admissible)
                return 1;
        if (was_admissible && !is_admissible)
                return delete_from_statistics_and_score_table(aft_row);
        if (!was_admissible && is_admissible) {
-               ret = add_afs_statistics(aft_row);
+               ret = add_afs_statistics(aft_row, &current_mood->stats);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row);
+               return add_to_score_table(aft_row, current_mood);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
-       if (old_afsi) {
-               ret = update_afs_statistics(old_afsi, &afsi);
-               if (ret < 0)
-                       return ret;
-       }
-       score = compute_score(&afsi);
+       if (old_afsi)
+               update_afs_statistics(old_afsi, &afsi);
+       score = compute_score(&afsi, &current_mood->stats);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
                percent = 100;
        else if (percent < 0)
                percent = 0;
-       PARA_DEBUG_LOG("moving from rank %u to %li%%\n", rank, percent);
+       PARA_DEBUG_LOG("moving to %li%%\n", percent);
        return score_update(aft_row, percent);
 }
 
 /* sse: seconds since epoch. */
-static void log_statistics(int64_t sse)
+static char *get_statistics(struct mood_instance *m, int64_t sse)
 {
-       unsigned n = statistics.num;
+       unsigned n = m->stats.num;
        int mean_days, sigma_days;
 
-       assert(current_mood);
-       PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
-               current_mood->name : "(dummy)");
-       if (!n) {
-               PARA_WARNING_LOG("no admissible files\n");
-               return;
-       }
-       PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
-       sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
-       PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
-               statistics.num_played_sum / n,
-               int_sqrt(statistics.num_played_qd / n));
-       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
-               statistics.num_played_correction);
-       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
-               statistics.last_played_correction);
-       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
-               statistics.normalization_divisor);
+       if (n == 0)
+               return make_message("no admissible files\n");
+       mean_days = (sse - m->stats.last_played_sum / n) / 3600 / 24;
+       sigma_days = int_sqrt(m->stats.last_played_qd / n) / 3600 / 24;
+       return make_message(
+               "loaded mood %s (%u files)\n"
+               "last_played mean/sigma: %d/%d days\n"
+               "num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n"
+               "correction factor ratio: %.2lf\n"
+       ,
+               m->name? m->name : "(dummy)",
+               n,
+               mean_days, sigma_days,
+               m->stats.num_played_sum / n,
+               int_sqrt(m->stats.num_played_qd / n),
+               86400.0 * m->stats.last_played_correction /
+                       m->stats.num_played_correction
+       );
 }
 
 /**
- * Close the current mood.
+ * Free all resources of a mood instance.
+ *
+ * \param m As obtained by \ref mood_load(). If NULL, unload the current mood.
  *
- * Frees all resources of the current mood.
+ * It's OK to call this with m == NULL even if no current mood is loaded.
  */
-void close_current_mood(void)
+void mood_unload(struct mood_instance *m)
 {
+       if (m)
+               return destroy_mood(m);
        destroy_mood(current_mood);
        current_mood = NULL;
-       memset(&statistics, 0, sizeof(statistics));
-       statistics.normalization_divisor = 1;
 }
 
-static void compute_correction_factors(int64_t sse)
+static void compute_correction_factors(int64_t sse, struct afs_statistics *s)
 {
-       struct afs_statistics *s = &statistics;
-
        if (s->num > 0) {
                s->normalization_divisor = int_sqrt(s->last_played_qd)
                        * int_sqrt(s->num_played_qd) / s->num / 100;
@@ -596,30 +585,45 @@ static void compute_correction_factors(int64_t sse)
 }
 
 /**
- * Change the current mood.
+ * Populate a score table with admissible files for the given mood.
  *
- * \param mood_name The name of the mood to open.
- * \param errmsg Error description is returned here.
+ * This consults the mood table to initialize the mood parser with the mood
+ * expression stored in the blob object which corresponds to the given name. A
+ * score table is allocated and populated with references to those entries of
+ * the audio file table which evaluate as admissible with respect to the mood
+ * expression. For each audio file a score value is computed and stored along
+ * with the file reference.
  *
- * If \a mood_name is \a NULL, load the dummy mood that accepts every audio file
- * and uses a scoring method based only on the \a last_played information.
+ * \param mood_name The name of the mood to load.
+ * \param result Opaque, refers to the mood parser and the score table.
+ * \param msg Error message or mood info is returned here.
  *
- * The errmsg pointer may be NULL, in which case no error message will be
- * returned. If a non-NULL pointer is given, the caller must free *errmsg.
+ * If the mood name is NULL, the dummy mood is loaded. This mood regards every
+ * audio file as admissible.
  *
- * If there is already an open mood, it will be closed first.
+ * A NULL result pointer instructs the function to operate on the current mood.
+ * That is, on the mood instance which is used by the server to select the next
+ * audio file for streaming. In this mode of operation, the mood which was
+ * active before the call, if any, is unloaded on success.
  *
- * \return Positive on success, negative on errors.
+ * If result is not NULL, the current mood is unaffected and *result points to
+ * an initialized mood instance on success. The caller can pass this reference
+ * to \ref mood_loop() to iterate over the admissible files, and should call
+ * \ref mood_unload() to free the mood instance afterwards.
  *
- * \sa struct \ref afs_info::last_played, \ref mp_eval_row().
+ * If the message pointer is not NULL, a suitable message is returned there in
+ * all cases. The caller must free this string.
+ *
+ * \return The number of admissible files on success, negative on errors. On
+ * errors, the current mood remains unaffected even if result is NULL. It is
+ * not considered an error if no files are admissible.
+ *
+ * \sa \ref mp_eval_row().
  */
-int change_current_mood(const char *mood_name, char **errmsg)
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg)
 {
        int i, ret;
-       struct admissible_array aa = {
-               .size = 0,
-               .array = NULL
-       };
+       struct admissible_array aa = {.size = 0};
        /*
         * We can not use the "now" pointer from sched.c here because we are
         * called before schedule(), which initializes "now".
@@ -627,85 +631,86 @@ int change_current_mood(const char *mood_name, char **errmsg)
        struct timeval rnow;
 
        if (mood_name) {
-               struct mood *m;
-               struct osl_row *row;
-               struct osl_object obj;
-
-               if (!*mood_name) {
-                       *errmsg = make_message("empty mood name");
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               obj.data = (char *)mood_name;
-               obj.size = strlen(mood_name) + 1;
-               ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
-               if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message("no such mood: %s",
-                                       mood_name);
-                       return ret;
-               }
-               ret = load_mood(row, &m, errmsg);
+               ret = init_mood_parser(mood_name, &aa.m, msg);
                if (ret < 0)
                        return ret;
-               close_current_mood();
-               current_mood = m;
-       } else { /* load dummy mood */
-               close_current_mood();
-               current_mood = alloc_new_mood(NULL);
-       }
-       aa.m = current_mood;
+       } else /* load dummy mood */
+               aa.m = alloc_new_mood(NULL);
        PARA_NOTICE_LOG("computing statistics of admissible files\n");
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message("audio file loop failed");
+               if (msg) /* false if we are called via the event handler */
+                       *msg = make_message("audio file loop failed\n");
                goto out;
        }
        clock_get_realtime(&rnow);
-       compute_correction_factors(rnow.tv_sec);
-       log_statistics(rnow.tv_sec);
-       for (i = 0; i < statistics.num; i++) {
-               ret = add_to_score_table(aa.array[i]);
+       compute_correction_factors(rnow.tv_sec, &aa.m->stats);
+       if (result)
+               score_open(&aa.m->score_table);
+       for (i = 0; i < aa.m->stats.num; i++) {
+               ret = add_to_score_table(aa.array[i], aa.m);
                if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message(
-                                       "could not add row to score table");
+                       if (msg)
+                               *msg = make_message(
+                                       "could not add row to score table\n");
                        goto out;
                }
        }
-       ret = statistics.num;
+       /* success */
+       if (msg)
+               *msg = get_statistics(aa.m, rnow.tv_sec);
+       ret = aa.m->stats.num;
+       if (result)
+               *result = aa.m;
+       else {
+               mood_unload(NULL);
+               current_mood = aa.m;
+       }
+       ret = 1;
 out:
        free(aa.array);
-       if (ret < 0)
-               close_current_mood();
+       if (ret <= 0) /* error, or no admissible files */
+               destroy_mood(aa.m);
        return ret;
 }
 
-/*
- * Close and re-open the current mood.
+/**
+ * Iterate over the admissible files of a mood instance.
  *
- * 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.
+ * This wrapper around \ref score_loop() is the mood counterpart of \ref
+ * playlist_loop().
+ *
+ * \param m Determines the score table to iterate. Must not be NULL.
+ * \param func See \ref score_loop().
+ * \param data See \ref score_loop().
+ *
+ * \return See \ref score_loop(), \ref playlist_loop().
+ */
+int mood_loop(struct mood_instance *m, osl_rbtree_loop_func *func, void *data)
+{
+       return score_loop(func, m->score_table, data);
+}
+
+/*
+ * Empty the score table and start over.
  *
- * If no mood is currently open, the function returns success.
+ * This function is called on events which render the current set of admissible
+ * files invalid, for example if an attribute is removed from the attribute
+ * table.
  */
 static int reload_current_mood(void)
 {
        int ret;
        char *mood_name = NULL;
 
-       ret = clear_score_table();
-       if (ret < 0)
-               return ret;
-       if (!current_mood)
-               return 1;
+       assert(current_mood);
+       score_clear();
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
-       close_current_mood();
-       ret = change_current_mood(mood_name, NULL);
+       mood_unload(NULL);
+       ret = mood_load(mood_name, NULL, NULL);
        free(mood_name);
        return ret;
 }
diff --git a/mood.h b/mood.h
deleted file mode 100644 (file)
index fcfe1ef..0000000
--- a/mood.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mood.h Public functions of mood.c. */
-
-int change_current_mood(const char *mood_name, char **errmsg);
-void close_current_mood(void);
-int mood_check_callback(struct afs_callback_arg *aca);
diff --git a/mp.c b/mp.c
index fb5a0c07472c46bb7d5ca42fcb4061d54755f96b..a068b043faf3c09b72befde39771058108f1aec9 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -557,7 +557,7 @@ int mp_init(const char *definition, int nbytes, struct mp_context **result,
  * function returns true (without looking at the audio file metadata) to
  * indicate that the given audio file should be considered admissible.
  *
- * \sa \ref change_current_mood(), \ref mp_eval_ast().
+ * \sa \ref mood_load(), \ref mp_eval_ast().
  */
 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
 {
index d7493ac01e28bba581b7112a41a37e84426be11a..56f28a09d8eb9fd18a9ba2262f14a58c6cec7997 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -37,9 +37,6 @@ struct mp3header {
        unsigned int freq;
        unsigned int padding;
        unsigned int mode;
-       unsigned int copyright;
-       unsigned int original;
-       unsigned int emphasis;
 };
 
 static const int frequencies[3][4] = {
@@ -473,7 +470,7 @@ static int frame_length(struct mp3header *header)
                + header->padding;
 }
 
-static int compare_headers(struct mp3header *h1,struct mp3header *h2)
+static int compare_headers(struct mp3header *h1, struct mp3header *h2)
 {
        if ((*(unsigned int*)h1) == (*(unsigned int*)h2))
                return 1;
@@ -481,10 +478,7 @@ static int compare_headers(struct mp3header *h1,struct mp3header *h2)
                        (h1->layer == h2->layer) &&
                        (h1->crc == h2->crc) &&
                        (h1->freq == h2->freq) &&
-                       (h1->mode == h2->mode) &&
-                       (h1->copyright == h2->copyright) &&
-                       (h1->original == h2->original) &&
-                       (h1->emphasis == h2->emphasis))
+                       (h1->mode == h2->mode))
                return 1;
        return 0;
 }
index bc8ccdaa99be724530372a03b71544abeece480b..d40df85edbe24b9dc54f01fc9305a1575c022507 100644 (file)
@@ -105,7 +105,7 @@ next_frame:
                mp3dec_consume(btrn, &pmd->stream, len);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
                        if (len == iqs && btr_no_parent(btrn)) {
-                               ret = -E_MP3DEC_EOF;
+                               ret = -E_EOF;
                                goto err;
                        }
                        fn->min_iqs += 100;
@@ -127,7 +127,7 @@ decode:
                        goto err;
                mad_stream_sync(&pmd->stream);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
-                       ret = -E_MP3DEC_EOF;
+                       ret = -E_EOF;
                        if (len == iqs && btr_no_parent(btrn))
                                goto err;
                        fn->min_iqs += 100;
diff --git a/mp4.c b/mp4.c
index f8515ca290681a271155e9749d903ea9214b9b13..fe9d4b37e3e94c0c906b3cc16234539cde19a6b6 100644 (file)
--- a/mp4.c
+++ b/mp4.c
@@ -131,7 +131,8 @@ static int read_int16(struct mp4 *f, uint16_t *result)
        return ret;
 }
 
-/** A macro defining the atoms we care about. It gets expanded twice. */
+/** \cond atom_items */
+/* A macro defining the atoms we care about. It gets expanded twice. */
 #define ATOM_ITEMS \
        ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
        ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
@@ -155,6 +156,8 @@ static int read_int16(struct mp4 *f, uint16_t *result)
        ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
        ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
 
+/** \endcond atom_items */
+
 /** For the C enumeration we concatenate ATOM_ with the first argument. */
 #define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
 /** The enumeration of interesting atoms. */
@@ -1044,7 +1047,7 @@ free_moov:
  * function also returns NULL. Otherwise a copy of the tag value is returned
  * and the caller should free this memory when it is no longer needed.
  */
-char *mp4_get_tag_value(const struct mp4 *f, const char *item)
+__malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item)
 {
        for (unsigned n = 0; n < f->meta.count; n++)
                if (!strcasecmp(f->meta.tags[n].item, item))
diff --git a/mp4.h b/mp4.h
index 1618aa31d2cb49ccc3ae2182ddf8d7ef1f5c5f6f..c36a1f81db84bbe8c0f6a800f74a75f36cc2d93b 100644 (file)
--- a/mp4.h
+++ b/mp4.h
@@ -84,4 +84,4 @@ uint64_t mp4_get_duration(const struct mp4 *f);
 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result);
 struct mp4_metadata *mp4_get_meta(struct mp4 *f);
 int mp4_update_meta(struct mp4 *f);
-char *mp4_get_tag_value(const struct mp4 *f, const char *item);
+__malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item);
diff --git a/net.c b/net.c
index e01af24be27c49a00a60a61a98448bc3d82046f0..9b3624428d5e8a0f63df7d814783df20afa54385 100644 (file)
--- a/net.c
+++ b/net.c
 #include "list.h"
 #include "fd.h"
 
+/* Whether the given address conforms to the IPv4 address format. */
+static inline bool is_valid_ipv4_address(const char *address)
+{
+       struct in_addr test_it;
+       return inet_pton(AF_INET, address, &test_it) != 0;
+}
+
 /**
  * Parse and validate IPv4 address/netmask string.
  *
@@ -58,13 +65,6 @@ failed:
        return NULL;
 }
 
-
-/**
- * Match string as a candidate IPv4 address.
- *
- * \param address The string to match.
- * \return True if \a address has "dot-quad" format.
- */
 static bool is_v4_dot_quad(const char *address)
 {
        bool result;
@@ -77,6 +77,13 @@ static bool is_v4_dot_quad(const char *address)
        return result;
 }
 
+/* Whether a string conforms to IPv6 address format (RFC 4291). */
+static inline bool is_valid_ipv6_address(const char *address)
+{
+       struct in6_addr test_it;
+       return inet_pton(AF_INET6, address, &test_it) != 0;
+}
+
 /**
  * Perform basic syntax checking on the host-part of an URL:
  *
@@ -183,7 +190,7 @@ failed:
  * \return In all cases the returned string is a allocated with malloc(3) and
  * has to be freed by the caller.
  */
-char *format_url(const char *url, int default_port)
+__malloc char *format_url(const char *url, int default_port)
 {
        char host[MAX_HOSTLEN];
        int url_port;
@@ -205,7 +212,7 @@ char *format_url(const char *url, int default_port)
  * \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 getservbyport(3), services(5), nsswitch.conf(5).
  */
 const char *stringify_port(int port, const char *transport)
 {
@@ -224,12 +231,13 @@ const char *stringify_port(int port, const char *transport)
        return service;
 }
 
-/**
- * Determine the socket type for a given layer-4 protocol.
- *
- * \param l4type The symbolic name of the transport-layer protocol.
- *
- * \sa ip(7), socket(2).
+#ifndef SOCK_DCCP
+#define SOCK_DCCP 6 /**< Linux socket type. */
+#endif
+
+/*
+ * Determine the socket type, given the symbolic name of the transport-layer
+ * protocol. See ip(7), socket(2).
  */
 static inline int sock_type(const unsigned l4type)
 {
@@ -241,9 +249,7 @@ static inline int sock_type(const unsigned l4type)
        return -1;              /* not supported here */
 }
 
-/**
- * Pretty-print transport-layer name.
- */
+/* Pretty-print transport-layer name. */
 static const char *layer4_name(const unsigned l4type)
 {
        switch (l4type) {
@@ -273,7 +279,12 @@ struct pre_conn_opt {
        struct list_head node;          /**< FIFO, as sockopt order matters. */
 };
 
-/** FIFO list of pre-connection socket options to be set */
+/**
+ * List of pre-connection socket options to be set.
+ *
+ * This list contains transport-layer independent encapsulation of socket
+ * options that need to be registered prior to setting up a connection.
+ */
 struct flowopts {
        struct list_head sockopts;
 };
@@ -325,7 +336,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
        list_add_tail(&new->node, &fo->sockopts);
 }
 
-/** Set the entire bunch of pre-connection options at once. */
+/* Set the entire bunch of pre-connection options at once. */
 static void flowopt_setopts(int sockfd, struct flowopts *fo)
 {
        struct pre_conn_opt *pc;
@@ -509,7 +520,7 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
        if (ai)
                freeaddrinfo(ai);
        if (ret < 0) {
-               PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
+               PARA_NOTICE_LOG("can not create %s socket %s#%d.\n",
                layer4_name(l4type), host? host : (passive?
                "[loopback]" : "[localhost]"), port_number);
        }
@@ -571,11 +582,7 @@ int para_listen_simple(unsigned l4type, uint16_t port)
        return para_listen(l4type, NULL, port);
 }
 
-/**
- * Determine IPv4/v6 socket address length.
- * \param sa Container of IPv4 or IPv6 address.
- * \return Address-family dependent address length.
- */
+/* Compute the address-family dependent address length of an IPv4/v6 socket. */
 static socklen_t salen(const struct sockaddr *sa)
 {
        assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
@@ -585,7 +592,7 @@ static socklen_t salen(const struct sockaddr *sa)
                : sizeof(struct sockaddr_in);
 }
 
-/** True if @ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
+/* True if ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
 static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
 {
        const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
@@ -593,10 +600,10 @@ static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
        return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr);
 }
 
-/**
+/*
  * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
- * \param ss Container of IPv4/6 address.
- * \return Pointer to normalized address (may be static storage).
+ * ss: Container of IPv4/6 address.
+ * Returns: Pointer to normalized address (may be static storage).
  *
  * \sa RFC 3493.
  */
@@ -617,7 +624,7 @@ normalize_ip_address(const struct sockaddr_storage *ss)
        return (const struct sockaddr *)ss;
 }
 
-/**
+/*
  * Generic/fallback MTU values
  *
  * These are taken from RFC 1122, RFC 2460, and RFC 5405.
@@ -632,7 +639,7 @@ static inline int generic_mtu(const int af_type)
        return af_type == AF_INET6 ? 1280 : 576;
 }
 
-/** Crude approximation of IP header overhead - neglecting options. */
+/* Crude approximation of IP header overhead - neglecting options. */
 static inline int estimated_header_overhead(const int af_type)
 {
        return af_type == AF_INET6 ? 40 : 20;
@@ -829,6 +836,10 @@ int para_accept(int fd, void *addr, socklen_t size, int *new_fd)
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
+#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
+#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
+#endif
+
 /**
  * Probe the list of DCCP CCIDs configured on this host.
  * \param ccid_array Pointer to return statically allocated array in.
@@ -843,7 +854,7 @@ int dccp_available_ccids(uint8_t **ccid_array)
        socklen_t nccids = sizeof(ccids);
        int ret, fd;
 
-       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       ret = fd = makesock(IPPROTO_DCCP, true /* passive */, NULL, 0, NULL);
        if (ret < 0)
                return ret;
 
@@ -861,6 +872,18 @@ int dccp_available_ccids(uint8_t **ccid_array)
        return nccids;
 }
 
+/**
+ * The buffer size of the sun_path component of struct sockaddr_un.
+ *
+ * While glibc doesn't define UNIX_PATH_MAX, it documents it has being limited
+ * to 108 bytes. On NetBSD it is only 104 bytes though. We trust UNIX_PATH_MAX
+ * if it is defined and use the size of the ->sun_path member otherwise. This
+ * should be safe everywhere.
+ */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
+#endif
+
 /*
  * Prepare a structure for AF_UNIX socket addresses.
  *
diff --git a/net.h b/net.h
index fd89dc5db0695ae6285484e0755ae0d28b286693..33acfc890495087991c964397371e4edf9b574f1 100644 (file)
--- a/net.h
+++ b/net.h
 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 /** \file net.h exported symbols from net.c */
 
-/**
- * The buffer size of the sun_path component of struct sockaddr_un.
- *
- * While glibc doesn't define \p UNIX_PATH_MAX, it documents it has being
- * limited to 108 bytes. On NetBSD it is only 104 bytes though. We trust \p
- * UNIX_PATH_MAX if it is defined and use the size of the ->sun_path member
- * otherwise. This should be safe everywhere.
- */
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
-#endif
-
 /* Userland defines for Linux DCCP support. */
 
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33 /**< IANA assigned value. */
-#endif
-
-#ifndef SOCK_DCCP
-#define SOCK_DCCP 6 /**< Linux socket type. */
-#endif
-
-#ifndef DCCP_SOCKOPT_RX_CCID
-/** Per-connection CCID support (set/get the RX CCID, since v2.6.30-rc1). */
-#define DCCP_SOCKOPT_RX_CCID 15
-#endif
-
 #ifndef SOL_DCCP
 #define SOL_DCCP 269 /**< Linux socket level. */
 #endif
 
-#ifndef DCCP_SOCKOPT_GET_CUR_MPS
-#define DCCP_SOCKOPT_GET_CUR_MPS  5 /**< Max packet size, RFC 4340, 14. */
-#endif
-
-#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
-#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
-#endif
-
-#ifndef DCCP_SOCKOPT_CCID
-#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
-#endif
-
-#ifndef DCCP_SOCKOPT_TX_CCID
-#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
-#endif
-
 /** The maximum length of the host component in an URL. */
 #define MAX_HOSTLEN 256
 
-/**
- * Flowopts: Transport-layer independent encapsulation of socket options
- *           that need to be registered prior to setting up a connection.
- */
+/* Opaque, only known to net.c. */
 struct flowopts;
 
-extern struct flowopts *flowopt_new(void);
-extern void flowopt_add(struct flowopts *fo, int level, int opt,
+struct flowopts *flowopt_new(void);
+void flowopt_add(struct flowopts *fo, int level, int opt,
                const char *name, const void *val, int len);
 void flowopt_cleanup(struct flowopts *fo);
-/** Flowopt shortcut macros */
-#define OPT_ADD(fo, lev, opt, val, len)        flowopt_add(fo, lev, opt, #opt, val, len)
 
 /**
  * Functions to parse and validate (parts of) URLs.
  */
-extern char *parse_cidr(const char *cidr,
-                       char *addr, ssize_t addrlen, int32_t *netmask);
-extern char *parse_url(const char *url,
-                      char *host, ssize_t hostlen, int32_t *port);
-char *format_url(const char *url, int default_port);
-extern const char *stringify_port(int port, const char *transport);
-/**
- * Ensure that string conforms to the IPv4 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if \a address conforms to the IPv4 address format, else 0.
- */
-_static_inline_ bool is_valid_ipv4_address(const char *address)
-{
-       struct in_addr test_it;
-
-       return inet_pton(AF_INET, address, &test_it) != 0;
-}
-
-/**
- * Ensure that string conforms to IPv6 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if string has a valid IPv6 address syntax, 0 if not.
- * \sa RFC 4291.
- */
-_static_inline_ bool is_valid_ipv6_address(const char *address)
-{
-       struct in6_addr test_it;
-
-       return inet_pton(AF_INET6, address, &test_it) != 0;
-}
+char *parse_cidr(const char *cidr,
+               char *addr, ssize_t addrlen, int32_t *netmask);
+char *parse_url(const char *url,
+               char *host, ssize_t hostlen, int32_t *port);
+__malloc char *format_url(const char *url, int default_port);
+const char *stringify_port(int port, const char *transport);
 
 int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result);
@@ -114,10 +40,9 @@ int makesock(unsigned l4type, bool passive, const char *host,
 int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo);
 
-static inline int para_connect_simple(unsigned l4type,
-                                     const char *host, uint16_t port)
+static inline int para_connect(unsigned l4type, const char *host, uint16_t port)
 {
-       return makesock(l4type, 0, host, port, NULL);
+       return makesock(l4type, false, host, port, NULL);
 }
 
 void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
@@ -133,12 +58,12 @@ int para_listen(unsigned l4type, const char *addr, uint16_t port);
 int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
-extern char *remote_name(int sockfd);
+char *remote_name(int sockfd);
 
 /**
  * Determining maximum payload (packet) size
  */
-extern int generic_max_transport_msg_size(int sockfd);
+int generic_max_transport_msg_size(int sockfd);
 
 int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
@@ -152,8 +77,6 @@ ssize_t send_cred_buffer(int, char*);
 /**
  * Functions and definitions to support \p IPPROTO_DCCP
  */
-/** Estimated worst-case length of a DCCP header including options. */
-#define DCCP_MAX_HEADER                128
 /** Hardcoded maximum number of separate CCID modules compiled into a host. */
 #define DCCP_MAX_HOST_CCIDS    20
-extern int dccp_available_ccids(uint8_t **ccid_array);
+int dccp_available_ccids(uint8_t **ccid_array);
index 3a5c263cbc09169f15c973040ddc2e0118b27c9c..0a27a4acebc1652da03dea1573bf337eaf9c52a9 100644 (file)
@@ -365,7 +365,7 @@ struct oac_custom_header {
  *
  * \sa \ref oac_custom_header_init().
  */
-struct oac_custom_header *oac_custom_header_new(void)
+__malloc struct oac_custom_header *oac_custom_header_new(void)
 {
        return zalloc(sizeof(struct oac_custom_header));
 }
index e0cf2d40c3ffb825c9f19338aa792558cb1c8742..03bf88b5794b0374fba493a79e502e4a89a686ca 100644 (file)
@@ -5,7 +5,7 @@
  * handlers that use the ogg container format.
  */
 
-struct oac_custom_header *oac_custom_header_new(void);
+__malloc struct oac_custom_header *oac_custom_header_new(void);
 void oac_custom_header_init(int serial, struct oac_custom_header *h);
 int oac_custom_header_append(ogg_packet *op, struct oac_custom_header *h);
 void oac_custom_header_flush(struct oac_custom_header *h);
index da70d4c04a2e2ec2dce8b955a5430442cd912519..b1aec4bc2c4cb0049ed942d3b7e7708b2de84688 100644 (file)
@@ -211,7 +211,7 @@ static int ogg_post_monitor(__a_unused struct sched *s, void *context)
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
-               if (ret != -E_BTR_EOF) /* fatal error */
+               if (ret != -E_EOF) /* fatal error */
                        goto out;
                if (fn->min_iqs == 0 && !pod->have_more) /* EOF */
                        goto out;
index 718498763c8ccc287fafc78edc22157d5fff7193..f696cd9e83606bc4e6bdd89d666f0885575f1d9f 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -37,12 +37,8 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
 }
 
 /*
- * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
- * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
- * not readable, the function calls exit().
- *
- * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
- * random(3), \ref para_random().
+ * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Then seed the
+ * PRNG used by random(3) with a random seed obtained from SSL.
  */
 void crypt_init(void)
 {
@@ -101,7 +97,7 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
        return bnsize + 4;
 }
 
-static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
+static int read_public_key(const unsigned char *blob, int blen, RSA **result)
 {
        int ret;
        RSA *rsa;
@@ -153,7 +149,7 @@ bio_free:
        return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
 }
 
-static int read_private_rsa_params(const unsigned char *blob,
+static int read_openssh_private_key(const unsigned char *blob,
                const unsigned char *end, RSA **result)
 {
        int ret;
@@ -220,11 +216,11 @@ static int read_private_rsa_params(const unsigned char *blob,
        rsa->n = n;
        rsa->e = e;
        rsa->d = d;
+       rsa->iqmp = iqmp;
        rsa->p = p;
        rsa->q = q;
        rsa->dmp1 = dmp1;
        rsa->dmq1 = dmq1;
-       rsa->iqmp = iqmp;
 #endif
        *result = rsa;
        ret = 1;
@@ -271,7 +267,7 @@ static int get_private_key(const char *path, RSA **rsa)
                if (ret < 0)
                        goto free_blob;
                PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
-               ret = read_private_rsa_params(blob + ret, end, rsa);
+               ret = read_openssh_private_key(blob + ret, end, rsa);
        } else
                ret = read_pem_private_key(path, rsa);
 free_blob:
@@ -284,34 +280,34 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        unsigned char *blob;
        size_t decoded_size;
        int ret;
-       struct asymmetric_key *key = alloc(sizeof(*key));
+       struct asymmetric_key *pub = alloc(sizeof(*pub));
 
        ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                goto out;
-       ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
+       ret = read_public_key(blob + ret, decoded_size - ret, &pub->rsa);
        if (ret < 0)
                goto free_blob;
-       ret = RSA_size(key->rsa);
+       ret = RSA_size(pub->rsa);
        assert(ret > 0);
-       *result = key;
+       *result = pub;
 free_blob:
        free(blob);
 out:
        if (ret < 0) {
-               free(key);
+               free(pub);
                *result = NULL;
                PARA_ERROR_LOG("can not load key %s\n", key_file);
        }
        return ret;
 }
 
-void apc_free_pubkey(struct asymmetric_key *key)
+void apc_free_pubkey(struct asymmetric_key *pub)
 {
-       if (!key)
+       if (!pub)
                return;
-       RSA_free(key->rsa);
-       free(key);
+       RSA_free(pub->rsa);
+       free(pub);
 }
 
 int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
index 85287be0c813d4899b2f802735f2684338ea420c..f36990faf43a4110c8e7f7dc2f822f5a8e09046c 100644 (file)
@@ -217,7 +217,7 @@ static int opusdec_post_monitor(__a_unused struct sched *s, void *context)
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
-               if (ret != -E_BTR_EOF) /* fatal error */
+               if (ret != -E_EOF) /* fatal error */
                        goto out;
                if (!ctx->have_more) /* EOF */
                        goto out;
index 96d7b1871a941bff1662ae2a9512b85c79e7b4b9..4ea85afa73f45023c8f22ffe54e875343ea3d13e 100644 (file)
@@ -218,7 +218,7 @@ static int oss_post_monitor(__a_unused struct sched *s, void *context)
        bytes = btr_next_buffer(btrn, &data);
        frames = bytes / powd->bytes_per_frame;
        if (!frames) { /* eof and less than a single frame available */
-               ret = -E_WRITE_COMMON_EOF;
+               ret = -E_EOF;
                goto out;
        }
        ret = 0;
diff --git a/para.h b/para.h
index bbf91330855610ce0316986f2195d5161e3bc2bb..280c282323db369e7d0e84173324016aa425a92d 100644 (file)
--- a/para.h
+++ b/para.h
@@ -222,6 +222,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 #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__)
 
+/** \cond status_items */
 #define STATUS_ITEMS \
        STATUS_ITEM(basename) \
        STATUS_ITEM(status) \
@@ -269,6 +270,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 enum status_items {STATUS_ITEMS NUM_STAT_ITEMS};
 #undef STATUS_ITEM
 #define STATUS_ITEM(_name) #_name,
+/** \endcond status items */
 
 extern const char *status_item_list[];
 /** Loop over each status item. */
diff --git a/play.c b/play.c
index 262f69ee4dc3d57686904fafb5597e1d955a189d..bd183b6b8dd48906e08cf142d7ff8c1e05aad48a 100644 (file)
--- a/play.c
+++ b/play.c
@@ -7,6 +7,7 @@
 #include <lopsub.h>
 
 #include "recv_cmd.lsg.h"
+#include "filter_cmd.lsg.h"
 #include "play_cmd.lsg.h"
 #include "write_cmd.lsg.h"
 #include "play.lsg.h"
 #include "write.h"
 #include "fd.h"
 
-/**
- * Besides playback tasks which correspond to the receiver/filter/writer nodes,
- * para_play creates two further tasks: The play task and the i9e task. It is
- * important whether a function can be called in the context of para_play or
- * i9e or both. As a rule, all command handlers are called only in i9e context via
- * the line handler (input mode) or the key handler (command mode) below.
- *
- * Playlist handling is done exclusively in play context.
- */
-
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
@@ -239,8 +230,7 @@ static int get_playback_error(void)
                return 0;
        if (task_status(pt->rn.task) >= 0)
                return 0;
-       if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF
-                       || err == -E_WRITE_COMMON_EOF)
+       if (err == -E_EOF)
                return 1;
        return err;
 }
@@ -266,6 +256,7 @@ static int eof_cleanup(void)
        if (decoder->close)
                decoder->close(&pt->fn);
        btr_remove_node(&pt->fn.btrn);
+       lls_free_parse_result(pt->fn.lpr, FILTER_CMD(pt->fn.filter_num));
        free(pt->fn.conf);
        memset(&pt->fn, 0, sizeof(struct filter_node));
 
@@ -1057,9 +1048,9 @@ static void session_open(void)
                char *dot_para = make_message("%s/.paraslash", home);
 
                free(home);
-               ret = para_mkdir(dot_para, 0777);
+               ret = para_mkdir(dot_para);
                /* warn, but otherwise ignore mkdir error */
-               if (ret < 0 && ret != -ERRNO_TO_PARA_ERROR(EEXIST))
+               if (ret < 0)
                        PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
                                para_strerror(-ret));
                history_file = make_message("%s/play.history", dot_para);
@@ -1246,10 +1237,25 @@ out:
 /**
  * The main function of para_play.
  *
- * \param argc Standard.
- * \param argv Standard.
+ * \param argc See man page.
+ * \param argv See man page.
+ *
+ * para_play distributes its work by submitting various tasks to the paraslash
+ * scheduler. The receiver, filter and writer tasks, which are used to play an
+ * audio file, require one task each to maintain their underlying buffer tree
+ * node. These tasks only exist when an audio file is playing.
+ *
+ * The i9 task, which is submitted and maintained by the i9e subsystem, reads
+ * an input line and calls the corresponding command handler such as com_stop()
+ * which is implemented in this file. The command handlers typically write a
+ * request to the global play_task structure, whose contents are read and acted
+ * upon by another task, the play task.
+ *
+ * As a rule, playlist handling is performed exclusively in play context, i.e.
+ * in the post-monitor step of the play task, while command handlers are only
+ * called in i9e context.
  *
- * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
+ * \return EXIT_FAILURE or EXIT_SUCCESS.
  */
 int main(int argc, char *argv[])
 {
index 5f83b0fe0a35c9e5c31c97828bfa7af2bebc6208..c145b0fd80ce0520bda58f0949c46211eeee5fdd 100644 (file)
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
-/** Structure used for adding entries to a playlist. */
-struct playlist_info {
+/**
+ * The state of a playlist instance.
+ *
+ * A structure of this type is allocated and initialized at playlist load time.
+ */
+struct playlist_instance {
        /** The name of the playlist. */
        char *name;
        /** The number of entries currently in the playlist. */
        unsigned length;
+       /** Contains all valid paths of the playlist. */
+       struct osl_table *score_table;
 };
-static struct playlist_info current_playlist;
+static struct playlist_instance current_playlist;
 
 /**
  * Re-insert an audio file into the tree of admissible files.
@@ -38,7 +44,7 @@ static int playlist_update_audio_file(const struct osl_row *aft_row)
 
 static int add_playlist_entry(char *path, void *data)
 {
-       struct playlist_info *playlist = data;
+       struct playlist_instance *pi = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
@@ -46,73 +52,43 @@ static int add_playlist_entry(char *path, void *data)
                PARA_NOTICE_LOG("%s: %s\n", path, para_strerror(-ret));
                return 1;
        }
-       ret = score_add(aft_row, -playlist->length);
+       ret = score_add(aft_row, -pi->length, pi->score_table);
        if (ret < 0) {
                PARA_ERROR_LOG("failed to add %s: %s\n", path, para_strerror(-ret));
                return ret;
        }
-       playlist->length++;
-       return 1;
-}
-
-/* returns -E_PLAYLIST_LOADED on _success_ to terminate the loop */
-static int load_playlist(struct osl_row *row, void *data)
-{
-       struct playlist_info *playlist = data;
-       struct osl_object playlist_def;
-       char *playlist_name;
-       int ret;
-
-       ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
-       if (ret < 0)
-               goto err;
-       playlist->length = 0;
-       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
-               playlist_def.size, add_playlist_entry, playlist);
-       osl_close_disk_object(&playlist_def);
-       if (ret < 0)
-               goto err;
-       ret = -E_PLAYLIST_EMPTY;
-       if (!playlist->length)
-               goto err;
-       playlist->name = para_strdup(playlist_name);
-       PARA_NOTICE_LOG("loaded playlist %s (%u files)\n", playlist->name,
-               playlist->length);
-       return -E_PLAYLIST_LOADED;
-err:
-       if (ret != -E_DUMMY_ROW)
-               PARA_NOTICE_LOG("unable to load playlist (%s)\n",
-                       para_strerror(-ret));
+       pi->length++;
        return 1;
 }
 
 static int check_playlist_path(char *path, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
        if (ret < 0)
-               para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", path, para_strerror(-ret));
        return 1; /* do not fail the loop on bad paths */
 }
 
 static int check_playlist(struct osl_row *row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
+       struct para_buffer *pb = &aca->pbout;
        struct osl_object playlist_def;
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
        if (ret < 0) { /* log error, but continue */
-               para_printf(pb, "failed to get playlist data: %s\n",
+               afs_error(aca, "failed to get playlist data: %s\n",
                        para_strerror(-ret));
                return 1;
        }
        if (*playlist_name) { /* skip dummy row */
                para_printf(pb, "checking playlist %s...\n", playlist_name);
                for_each_line(FELF_READ_ONLY, playlist_def.data,
-                       playlist_def.size, check_playlist_path, pb);
+                       playlist_def.size, check_playlist_path, aca);
        }
        osl_close_disk_object(&playlist_def);
        return 1;
@@ -129,49 +105,110 @@ static int check_playlist(struct osl_row *row, void *data)
 int playlist_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking playlists...\n");
-       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, aca,
                check_playlist));
 }
 
 /**
- * Close the current playlist.
+ * Free all resources of the given/current playlist.
  *
- * \sa \ref playlist_open().
+ * \param pi NULL means to unload the current playlist.
  */
-void playlist_close(void)
+void playlist_unload(struct playlist_instance *pi)
 {
+       if (pi) {
+               score_close(pi->score_table);
+               free(pi->name);
+               free(pi);
+               return;
+       }
        if (!current_playlist.name)
                return;
+       score_clear();
        free(current_playlist.name);
        current_playlist.name = NULL;
+       current_playlist.length = 0;
 }
 
 /**
- * Open the given playlist.
+ * Populate the score table from the paths of a playlist database object.
  *
- * \param name The name of the playlist to open.
+ * This loads the blob object which corresponds to the given name from the
+ * playlist table. Each line of the blob is regarded as a path which is looked
+ * up in the audio file table. If the path lookup succeeds, a reference to the
+ * corresponding row of the audio file table is added to the score table.
  *
- * Files which are listed in the playlist, but not contained in the database
- * are ignored.  This is not considered an error.
+ * \param name The name of the playlist to load.
+ * \param result Opaque, refers to the underlying score table.
+ * \param msg Error message or playlist info is returned here.
  *
- * \return Standard.
+ * \return The length of the loaded playlist on success, negative error code
+ * else. Files which are listed in the playlist, but are not contained in the
+ * database are ignored. This is not considered an error.
  */
-int playlist_open(const char *name)
+int playlist_load(const char *name, struct playlist_instance **result, char **msg)
 {
-       struct osl_object obj;
        int ret;
-       struct osl_row *row;
+       struct playlist_instance *pi;
+       struct osl_object playlist_def;
 
-       obj.data = (char *)name;
-       obj.size = strlen(obj.data);
-       ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
-       if (ret < 0) {
-               PARA_NOTICE_LOG("failed to load playlist %s\n", name);
-               return ret;
+       if (!name || !*name) {
+               if (msg)
+                       *msg = make_message("empty playlist name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = pl_get_def_by_name(name, &playlist_def);
+       if (ret < 0)
+               goto err;
+       pi = zalloc(sizeof(*pi));
+       if (result)
+               score_open(&pi->score_table);
+       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               playlist_def.size, add_playlist_entry, pi);
+       osl_close_disk_object(&playlist_def);
+       if (ret < 0)
+               goto close_score_table;
+       ret = -E_PLAYLIST_EMPTY;
+       if (pi->length == 0)
+               goto close_score_table;
+       /* success */
+       if (msg)
+               *msg = make_message("loaded playlist %s (%u files)\n", name,
+                       pi->length);
+       pi->name = para_strdup(name);
+       if (result)
+               *result = pi;
+       else {
+               playlist_unload(NULL);
+               current_playlist = *pi;
        }
-       playlist_close();
-       ret = load_playlist(row, &current_playlist);
-       return (ret == -E_PLAYLIST_LOADED)? current_playlist.length : ret;
+       return pi->length;
+close_score_table:
+       if (result)
+               score_close(pi->score_table);
+       free(pi);
+err:
+       PARA_NOTICE_LOG("unable to load playlist %s\n", name);
+       if (msg)
+               *msg = make_message("unable to load playlist %s\n", name);
+       return ret;
+}
+
+/**
+ * Iterate over all admissible audio files of a playlist instance.
+ *
+ * This wrapper around \ref score_loop() is the playlist counterpart of \ref
+ * mood_loop().
+ *
+ * \param pi Determines the score table to iterate. Must not be NULL.
+ * \param func See \ref score_loop().
+ * \param data See \ref score_loop().
+ *
+ * \return See \ref score_loop(), \ref mood_loop().
+ */
+int playlist_loop(struct playlist_instance *pi, osl_rbtree_loop_func *func, void *data)
+{
+       return score_loop(func, pi->score_table, data);
 }
 
 static int search_path(char *path, void *data)
@@ -183,17 +220,14 @@ static int search_path(char *path, void *data)
 
 static int handle_audio_file_event(enum afs_events event, void *data)
 {
-       int ret, was_admissible = 0, is_admissible;
+       int ret;
+       bool was_admissible = false, is_admissible;
        struct osl_object playlist_def;
        char *new_path;
        const struct osl_row *row = data;
 
-       if (event == AUDIO_FILE_RENAME) {
-               ret = row_belongs_to_score_table(row, NULL);
-               if (ret < 0)
-                       return ret;
-               was_admissible = ret;
-       }
+       if (event == AUDIO_FILE_RENAME)
+               was_admissible = row_belongs_to_score_table(row);
        ret = get_audio_file_path_of_row(row, &new_path);
        if (ret < 0)
                return ret;
@@ -214,7 +248,7 @@ static int handle_audio_file_event(enum afs_events event, void *data)
        }
        /* !was_admissible && is_admissible */
        current_playlist.length++;
-       return score_add(row, 0); /* play it immediately */
+       return score_add(row, 0, NULL); /* play it immediately */
 }
 
 /**
@@ -229,7 +263,6 @@ static int handle_audio_file_event(enum afs_events event, void *data)
 int playlists_event_handler(enum afs_events event,
        __a_unused struct para_buffer *pb, void *data)
 {
-       int ret;
        struct afsi_change_event_data *aced = data;
 
        if (!current_playlist.name)
@@ -241,10 +274,7 @@ int playlists_event_handler(enum afs_events event,
        case AUDIO_FILE_ADD:
                return handle_audio_file_event(event, data);
        case AUDIO_FILE_REMOVE:
-               ret = row_belongs_to_score_table(data, NULL);
-               if (ret < 0)
-                       return ret;
-               if (!ret)
+               if (!row_belongs_to_score_table(data))
                        return 1;
                current_playlist.length--;
                return score_delete(data);
index bf28e975de2720f001be95ee06b269a1a8ecdd2c..72cb3f62ea766ee7ed94e3a68ed5a17591f2f133 100644 (file)
@@ -220,7 +220,7 @@ static int resample_post_monitor(__a_unused struct sched *s, void *context)
        }
        btr_merge(btrn, fn->min_iqs);
        in_bytes = btr_next_buffer(btrn, (char **)&in);
-       ret = -E_RESAMPLE_EOF;
+       ret = -E_EOF;
        num_frames = in_bytes / 2 / ctx->channels;
        if (num_frames == 0)
                goto out;
diff --git a/sched.c b/sched.c
index c786c9f26f9b5fc47cfd3c8d883671c5433bd048..20822038b81aff04d8ee8c21f7f8d7773831ca37 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -62,10 +62,6 @@ static void sched_pre_monitor(struct sched *s)
 
 static void unlink_and_free_task(struct task *t)
 {
-       PARA_INFO_LOG("freeing task %s (%s)\n", t->name, t->status < 0?
-               para_strerror(-t->status) :
-               (t->status == TS_DEAD? "[dead]" : "[running]"));
-
        list_del(&t->node);
        free(t->name);
        free(t);
@@ -183,6 +179,7 @@ int task_reap(struct task **tptr)
        if (t->status >= 0)
                return 0;
        ret = t->status;
+       PARA_INFO_LOG("reaping %s: %s\n", t->name, para_strerror(-ret));
        /*
         * With list_for_each_entry_safe() it is only safe to remove the
         * _current_ list item. Since we are being called from the loop in
diff --git a/score.c b/score.c
index 54af56f7c60100ad65ac09cafb52e7769514c958..c03e3472da306a1d535b201ea2d23c90ccf8f90a 100644 (file)
--- a/score.c
+++ b/score.c
@@ -76,23 +76,11 @@ static struct osl_table_description score_table_desc = {
        .column_descriptions = score_cols
 };
 
-/**
- * Compute the number of files in score table.
- *
- * \param num Result is returned here.
- *
- * \return Positive on success, negative on errors.
- */
-int get_num_admissible_files(unsigned *num)
-{
-       return osl(osl_get_num_rows(score_table, num));
-}
-
 /* On errors (negative return value) the content of score is undefined. */
-static int get_score_of_row(void *score_row, long *score)
+static int get_score_of_row(struct osl_table *t, void *score_row, long *score)
 {
        struct osl_object obj;
-       int ret = osl(osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj));
+       int ret = osl(osl_get_object(t, score_row, SCORECOL_SCORE, &obj));
 
        if (ret >= 0)
                *score = *(long *)obj.data;
@@ -100,14 +88,15 @@ static int get_score_of_row(void *score_row, long *score)
 }
 
 /**
- * Add an entry to the table of admissible files.
+ * Add a (row, score) pair to the score table.
  *
- * \param aft_row The audio file to be added.
- * \param score The score for this file.
+ * \param aft_row Identifies the audio file to be added.
+ * \param score The score value of the audio file.
+ * \param t NULL means to operate on the currently active table.
  *
  * \return The return value of the underlying call to osl_add_row().
  */
-int score_add(const struct osl_row *aft_row, long score)
+int score_add(const struct osl_row *aft_row, long score, struct osl_table *t)
 {
        int ret;
        struct osl_object score_objs[NUM_SCORE_COLUMNS];
@@ -124,7 +113,7 @@ int score_add(const struct osl_row *aft_row, long score)
        *(long *)(score_objs[SCORECOL_SCORE].data) = score;
 
 //     PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
-       ret = osl(osl_add_row(score_table, score_objs));
+       ret = osl(osl_add_row(t? t : score_table, score_objs));
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                free(score_objs[SCORECOL_SCORE].data);
@@ -132,16 +121,6 @@ int score_add(const struct osl_row *aft_row, long score)
        return ret;
 }
 
-static int get_nth_score(unsigned n, long *score)
-{
-       struct osl_row *row;
-       int ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row));
-
-       if (ret < 0)
-               return ret;
-       return get_score_of_row(row, score);
-}
-
 /**
  * Replace a row of the score table.
  *
@@ -156,7 +135,7 @@ static int get_nth_score(unsigned n, long *score)
  */
 int score_update(const struct osl_row *aft_row, long percent)
 {
-       struct osl_row *row;
+       struct osl_row *row, *rrow; /* score row, reference row */
        long new_score;
        unsigned n, new_pos;
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
@@ -167,11 +146,14 @@ int score_update(const struct osl_row *aft_row, long percent)
                return 1;
        if (ret < 0)
                return ret;
-       ret = get_num_admissible_files(&n);
+       ret = osl(osl_get_num_rows(score_table, &n));
        if (ret < 0)
                return ret;
        new_pos = 1 + (n - 1) * percent / 100;
-       ret = get_nth_score(new_pos, &new_score);
+       ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
+       if (ret < 0)
+               return ret;
+       ret = get_score_of_row(score_table, rrow, &new_score);
        if (ret < 0)
                return ret;
        new_score--;
@@ -195,7 +177,7 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score,
                struct osl_row **aft_row)
 {
        struct osl_object obj;
-       int ret = get_score_of_row(score_row, score);
+       int ret = get_score_of_row(score_table, score_row, score);
 
        if (ret < 0)
                return ret;
@@ -206,28 +188,28 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score,
        return 1;
 }
 
-static int get_score_row_from_aft_row(const struct osl_row *aft_row,
-               struct osl_row **score_row)
+static int get_score_row_from_aft_row(struct osl_table *t,
+               const struct osl_row *aft_row, struct osl_row **score_row)
 {
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
                .size = sizeof(aft_row)};
-       return osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row));
+       return osl(osl_get_row(t, SCORECOL_AFT_ROW, &obj, score_row));
 }
 
 /**
- * Loop over all files in the score table.
- *
- * \param data A pointer to arbitrary data.
- * \param func Function to be called for each admissible file.
+ * Call the given function for each row of the score table.
  *
- * \return The return value of the underlying call to osl_rbtree_loop().
+ * \param func Callback, called once per row.
+ * \param t NULL means to use the currently active score table.
+ * \param data Passed verbatim to the callback.
  *
- * This is used for the ls command. The \a data parameter is passed as the
- * second argument to \a func.
+ * \return The return value of the underlying call to osl_rbtree_loop(). The
+ * loop terminates early if the callback returns negative.
  */
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
+int score_loop(osl_rbtree_loop_func *func, struct osl_table *t, void *data)
 {
-       return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func));
+       return osl(osl_rbtree_loop(t? t : score_table, SCORECOL_SCORE, data,
+               func));
 }
 
 /**
@@ -250,7 +232,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
        if (ret < 0)
                return ret;
        *aft_row = obj.data;
-       return get_score_of_row(row, score);
+       return get_score_of_row(score_table, row, score);
 }
 
 /**
@@ -265,7 +247,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
 int score_delete(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
-       int ret = get_score_row_from_aft_row(aft_row, &score_row);
+       int ret = get_score_row_from_aft_row(score_table, aft_row, &score_row);
 
        if (ret < 0)
                return ret;
@@ -276,67 +258,72 @@ int score_delete(const struct osl_row *aft_row)
  * Find out whether an audio file is contained in the score table.
  *
  * \param aft_row The row of the audio file table.
- * \param rank Result pointer
  *
- * \return Positive, if \a aft_row belongs to the audio file table,
- * zero if not, negative on errors. If \a aft_row was found, and \a rank
- * is not \p NULL, the rank of \a aft_row is returned in \a rank.
+ * \return If the lookup operation fails for any other reason than "not found",
+ * the function aborts the current process (afs), since this is considered a
+ * fatal error that should never happen.
  */
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
+bool row_belongs_to_score_table(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
-       int ret = get_score_row_from_aft_row(aft_row, &score_row);
+       int ret = get_score_row_from_aft_row(score_table, aft_row, &score_row);
 
        if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
-               return 0;
-       if (ret < 0)
-               return ret;
-       if (!rank)
-               return 1;
-       ret = osl(osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank));
-       if (ret < 0)
-               return ret;
-       return 1;
+               return false;
+       assert(ret >= 0);
+       return true;
 }
 
-static void score_close(void)
+/**
+ * Free all volatile objects, then close the table.
+ *
+ * \param t As returned from \ref score_open().
+ *
+ * This either succeeds or terminates the calling process.
+ */
+void score_close(struct osl_table *t)
 {
-       osl_close_table(score_table, OSL_FREE_VOLATILE);
-       score_table = NULL;
+       assert(osl_close_table(t? t : score_table, OSL_FREE_VOLATILE) >= 0);
 }
 
-static int score_open(__a_unused const char *dir)
+static void close_global_table(void)
 {
-       score_table_desc.dir = NULL; /* this table has only volatile columns */
-       return osl(osl_open_table(&score_table_desc, &score_table));
+       score_close(NULL);
 }
 
-/**
- * Remove all entries from the score table, but keep the table open.
- *
- * \return Standard.
- */
-int clear_score_table(void)
+static int open_global_table(__a_unused const char *dir)
 {
-       score_close();
-       return score_open(NULL);
+       assert(osl(osl_open_table(&score_table_desc, &score_table)) >= 0);
+       return 1;
 }
 
-static int score_event_handler(__a_unused enum afs_events event,
-               __a_unused struct para_buffer *pb, __a_unused void *data)
+/**
+ * Allocate a score table instance.
+ *
+ * \param result NULL means to open the currently active score table.
+ *
+ * Since the score table does no filesystem I/O, this function always succeeds.
+ * \sa \ref score_close().
+ */
+void score_open(struct osl_table **result)
 {
-       return 1;
+       if (result)
+               assert(osl(osl_open_table(&score_table_desc, result)) >= 0);
+       else
+               open_global_table(NULL);
 }
 
 /**
- * Initialize the scoring subsystem.
- *
- * \param t The members of \a t are filled in by the function.
+ * Remove all entries from the score table, but keep the table open.
  */
-void score_init(struct afs_table *t)
+void score_clear(void)
 {
-       t->name = score_table_desc.name;
-       t->open = score_open;
-       t->close = score_close;
-       t->event_handler = score_event_handler;
+       close_global_table();
+       open_global_table(NULL);
 }
+
+/** The score table stores (aft row, score) pairs in memory. */
+const struct afs_table_operations score_ops = {
+       .open = open_global_table,
+       .close = close_global_table,
+};
diff --git a/send.h b/send.h
index dec5b0db1381694fcb35a98785a50068cb1f6330..3407bc5c28a876f1fb5aca6e748368deb58afe0b 100644 (file)
--- a/send.h
+++ b/send.h
@@ -196,7 +196,7 @@ void init_sender_status(struct sender_status *ss,
                const struct lls_opt_result *listen_address_opt_result,
                int default_port, int max_clients, int default_deny);
 void free_sender_status(const struct sender_status *ss);
-char *generic_sender_status(struct sender_status *ss, const char *name);
+__malloc 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,
@@ -204,7 +204,7 @@ void generic_com_deny(struct sender_command_data *scd,
 void generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_acl_deplete(struct list_head *acl);
 void generic_com_off(struct sender_status *ss);
-char *generic_sender_help(void);
+__malloc char *generic_sender_help(void);
 struct sender_client *accept_sender_client(struct sender_status *ss);
 int send_queued_chunks(int fd, struct chunk_queue *cq);
 int parse_fec_url(const char *arg, struct sender_command_data *scd);
index 26502cabd08376673b2c46b61ccffae400344274..8dc82e9cba42aecf027761c7682289fb0a9b6797 100644 (file)
@@ -181,7 +181,7 @@ void free_sender_status(const struct sender_status *ss)
  *
  * \return The string printed in the "si" command.
  */
-char *generic_sender_status(struct sender_status *ss, const char *name)
+__malloc char *generic_sender_status(struct sender_status *ss, const char *name)
 {
        char *clnts = NULL, *ret, *addr = NULL;
        struct sender_client *sc, *tmp_sc;
@@ -413,7 +413,7 @@ warn:
  * \return A dynamically allocated string containing the help text for
  * a paraslash sender.
  */
-char *generic_sender_help(void)
+__malloc char *generic_sender_help(void)
 {
        return make_message(
                "usage: {on|off}\n"
index bdafeaf296e026508698c306e4b498e0710a9cee..d9a6aa37057415117c32a6a18c33d81977499392 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -72,10 +72,13 @@ static void generic_signal_handler(int s)
                errno = save_errno;
                return;
        }
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", strerror(errno));
-       else
-               PARA_EMERG_LOG("short write to signal pipe\n");
+       /*
+        * This is a fatal error which should never happen. We must not call
+        * PARA_XXX_LOG() here because this might acquire the log mutex which
+        * is already taken by the main program if the interrupt occurs while a
+        * log message is being printed. The mutex will not be released as long
+        * as this signal handler is running, so a deadlock ensues.
+        */
        exit(EXIT_FAILURE);
 }
 
index 46b346235f17ffb696f43d22d3dcfde4c75ba4ca..d8bd027b7010a149be59b8883b3e5ee685fb3c01 100644 (file)
--- a/string.c
+++ b/string.c
@@ -308,15 +308,32 @@ __must_check __malloc char *para_logname(void)
 }
 
 /**
- * Get the home directory of the current user.
+ * Get the home directory of the calling user.
  *
  * \return A dynamically allocated string that must be freed by the caller. If
- * the home directory could not be found, this function returns "/tmp".
+ * no entry is found which matches the UID of the calling process, or any other
+ * error occurs, the function prints an error message and aborts.
+ *
+ * \sa getpwuid(3), getuid(2).
  */
 __must_check __malloc char *para_homedir(void)
 {
-       struct passwd *pw = getpwuid(getuid());
-       return para_strdup(pw? pw->pw_dir : "/tmp");
+       struct passwd *pw;
+
+       /*
+        * To distinguish between the error case and the "not found" case we
+        * have to check errno after getpwuid(3). The manual page recommends to
+        * set it to zero before the call.
+        */
+       errno = 0;
+       pw = getpwuid(getuid());
+       if (pw)
+               return para_strdup(pw->pw_dir);
+       if (errno != 0)
+               PARA_EMERG_LOG("getpwuid error: %s\n", strerror(errno));
+       else
+               PARA_EMERG_LOG("no pw entry for uid %u\n", (unsigned)getuid());
+       exit(EXIT_FAILURE);
 }
 
 /**
@@ -739,13 +756,11 @@ void free_argv(char **argv)
 static int create_argv_offset(int offset, const char *buf, const char *delim,
                char ***result)
 {
-       char *word, **argv = arr_alloc(offset + 1, sizeof(char *));
+       char *word, **argv = arr_zalloc(offset + 1, sizeof(char *));
        const char *p;
        int i, ret;
 
-       for (i = 0; i < offset; i++)
-               argv[i] = NULL;
-       for (p = buf; p && *p; p += ret, i++) {
+       for (p = buf, i = offset; p && *p; p += ret, i++) {
                ret = get_next_word(p, delim, &word);
                if (ret < 0)
                        goto err;
index b4710f6704ce7a4fbd03567d806ce633df164008..20db1b1d1136066a7a12b88b0f4b8cc539daea62 100644 (file)
@@ -365,7 +365,7 @@ success:
        ret = -E_SYNC_COMPLETE; /* success */
        goto out;
 fail:
-       if (ret != -E_BTR_EOF)
+       if (ret != -E_EOF)
                PARA_WARNING_LOG("%s\n", para_strerror(-ret));
 out:
        sync_close_buddies(ctx);
diff --git a/t/audio_files/short-44100-2.mp3 b/t/audio_files/short-44100-2.mp3
new file mode 100644 (file)
index 0000000..917d59d
Binary files /dev/null and b/t/audio_files/short-44100-2.mp3 differ
index 9e681107646e85e0fdcc3e12ec1ed13694376628..f7a407dc9e4bd99a71a3b3e364a6386fc55f1d44 100755 (executable)
@@ -37,6 +37,18 @@ cmdline[$i]="init"
 good[$i]='^successfully'
 bad[$i]='!^successfully'
 
+let i++
+commands[$i]='add_dir'
+required_objects[$i]='ogg_afh'
+cmdline[$i]="add -v $test_audio_file_dir"
+good[$i]='^adding'
+
+let i++
+commands[$i]='rm'
+required_objects[$i]='ogg_afh'
+cmdline[$i]="rm -v $test_audio_file_dir/*"
+good[$i]='^removing'
+
 let i++
 commands[$i]="add_ogg"
 required_objects[$i]='ogg_afh'
index 75249fe32b0b0e159b4b6ad1773efa2673b2cd22..1ba70632a95d585de16eb5b0e961291fe4ca83f1 100644 (file)
@@ -17,9 +17,12 @@ get_audio_file_paths()
 
 say_color()
 {
+       local severity=$1
+
+       shift
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                export TERM=$ORIGINAL_TERM
-               case "$1" in
+               case "$severity" in
                        error) tput $C_BOLD; tput $C_SETAF 1;;
                        skip)  tput $C_SETAF 5;;
                        ok)
@@ -32,8 +35,11 @@ say_color()
                                tput $C_SETAF 6;;
                esac
        fi
-       shift
-       printf "%s\n" "$*"
+       if [[ "$severity" == 'error' ]]; then
+               printf "%s\n" "$*" 1>&2
+       else
+               printf "%s\n" "$*"
+       fi
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                tput $C_SGR0
                export TERM=dumb
@@ -279,7 +285,7 @@ fixup_dirs()
        [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
        [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
 
-       # we want alsolute paths because relative paths become invalid
+       # we want absolute paths because relative paths become invalid
        # after changing to the trash dir
        [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
        [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
index 8d1274bc1e919e703f3856cc8159269d33756f2c..f98a9664aec2d1ee3d8129e70442468423fa79a8 100644 (file)
@@ -40,14 +40,14 @@ static int udp_check_eof(size_t sz, struct iovec iov[2])
                if (memcmp(iov[0].iov_base, FEC_EOF_PACKET,
                                FEC_EOF_PACKET_LEN) != 0)
                        return 0;
-               return -E_RECV_EOF;
+               return -E_EOF;
        }
        if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, iov[0].iov_len) != 0)
                return 0;
        if (memcmp(iov[1].iov_base, &FEC_EOF_PACKET[iov[0].iov_len],
                        FEC_EOF_PACKET_LEN - iov[0].iov_len) != 0)
                return 0;
-       return -E_RECV_EOF;
+       return -E_EOF;
 }
 
 static int udp_recv_post_monitor(__a_unused struct sched *s, void *context)
@@ -168,7 +168,7 @@ static int udp_recv_open(struct receiver_node *rn)
        uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
+       ret = makesock(IPPROTO_UDP, true /* passive */, host, port, NULL);
        if (ret < 0)
                return ret;
        rn->fd = ret;
index 289479878592f6b50e5e31bb30a1098e71588865..fe001025bc1ad6c73c0e64c8589977db1e3203af 100644 (file)
@@ -187,7 +187,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
                return ret;
        port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
+       ret = para_connect(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
                return ret;
 
@@ -336,7 +336,7 @@ static int udp_com_add(struct sender_command_data *scd)
 
        sc->private_data = ut;
        sc->fd = -1;
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, scd->port);
+       ret = para_connect(IPPROTO_UDP, scd->host, scd->port);
        if (ret < 0)
                goto err;
        sc->fd = ret;
index f2436c9e5bf7a835db2553c4c54db112d628b40d..46770edf701e983d424e8869e7c9ccd4730c3cf9 100644 (file)
@@ -91,7 +91,7 @@ void user_list_init(const char *user_list_file)
                        continue;
                if (strcmp(w, "user"))
                        continue;
-               PARA_DEBUG_LOG("found entry for user %s\n", n);
+               PARA_INFO_LOG("loading pubkey %s for user %s\n", k, n);
                ret = apc_get_pubkey(k, &pubkey);
                if (ret < 0) {
                        PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
diff --git a/vss.c b/vss.c
index dd4ac2fbc96f54064f0dacd38ef2c9c39fa4f065..cd55851c366cec61c0bf7bc9501609fc2fa8a6c5 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -1014,13 +1014,8 @@ err:
 }
 
 /**
- * Main sending function.
- *
- * This function gets called from vss_post_monitor(). It checks whether the next
- * chunk of data should be pushed out. It obtains a pointer to the data to be
- * sent out as well as its length from mmd->afd.afhi. This information is then
- * passed to each supported sender's send() function as well as to the send()
- * functions of each registered fec client.
+ * If the next chunk needs to be sent, pass a pointer to the chunk data to all
+ * registered fec clients and to each sender's ->send() method.
  */
 static void vss_send(struct vss_task *vsst)
 {
index 7d6c371433b13ae6f6d3d63e8de47f69fe133a17..de4a3e6aafc76b83de2d9e2f4112c39c6e038d06 100644 (file)
@@ -78,7 +78,7 @@ static int wav_post_monitor(__a_unused struct sched *s, void *context)
        int32_t rate, ch;
 
        if (iqs == 0) {
-               ret = -E_WAV_EOF;
+               ret = -E_EOF;
                if (btr_no_parent(btrn))
                        goto err;
                return 0;
index f188ff2566ad30645365e73aa62e399439dfbf5e..452fa3574f4e051627d62ecd24242d63fa0c7350 100644 (file)
@@ -19,7 +19,7 @@ Unix systems. It is written in C and released under the GPLv2. </p>
 <p> Author: Andre Noll, <a
 href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
 Homepage: <a
-href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
+href="https://people.tuebingen.mpg.de/maan/">https://people.tuebingen.mpg.de/maan/</a>
 </p>
 
 <p> Comments and bug reports are welcome. Please provide the version
index 2b31e5d76c7f9f7c348ba08f852dc6c5a5780f73..1287597e20cdb4bc3c002e9e28447626819e8401 100644 (file)
@@ -11,7 +11,7 @@ source: </p>
                Clone the git repository by executing
 
                <pre> <kbd>
-                       git clone git://git.tuebingen.mpg.de/paraslash.git
+                       git clone https://git.tuebingen.mpg.de/paraslash.git
                </kbd> </pre>
 
                <p> The repository contains the full history of the
@@ -66,7 +66,7 @@ source: </p>
 
                The
 
-                       <a href="http://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
+                       <a href="https://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
 
                page contains a snapshot link for each revision. This
                allows getting a specific revision without downloading
index 9524182c292882b12c3efceccfc2a8e57da7a610..c52fd4a2fa2d580b46e7a4dad1d989509604b6c1 100644 (file)
Binary files a/web/images/signature.png and b/web/images/signature.png differ
index 4b1b472d1624129c109f0aa4688418f4b11b9bb2..596b53a1267d3be6cbe05f241837f7a2649b3fa0 100644 (file)
Binary files a/web/images/tar-icon.png and b/web/images/tar-icon.png differ
index b5329ea07f3d8a2f9553f384c9129c4ad1a262e3..0db1e22c26fdee83b3df4aaf60bad0537e40a0f1 100644 (file)
@@ -14,7 +14,7 @@ Introduction
 
 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.
+[brief descriptions](#The-paraslash-executables) of all executables.
 
 Overview
 --------
@@ -135,7 +135,7 @@ fine-grained selection based on various properties of the audio file,
 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)
+The section on the [audio file selector](#The-audio-file-selector)
 discusses this topic in more detail.
 
 Another component of para_server is the virtual streaming system,
@@ -287,9 +287,9 @@ Requirements
 ------------
 <h3> For the impatient </h3>
 
-       git clone git://git.tuebingen.mpg.de/lopsub
+       git clone https://git.tuebingen.mpg.de/lopsub
        cd lopsub && make && sudo make install
-       git clone git://git.tuebingen.mpg.de/osl
+       git clone https://git.tuebingen.mpg.de/osl
        cd osl && make && sudo make install && sudo ldconfig
        sudo apt-get install autoconf libssl-dev m4 \
               libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
@@ -306,7 +306,7 @@ 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/lopsub
+               git clone https://git.tuebingen.mpg.de/lopsub
 
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
 [clang](http://clang.llvm.org). All gcc versions >= 5.4 are currently
@@ -324,11 +324,11 @@ from templates by the m4 macro processor.
 
 Optional:
 
-- [libosl](http://people.tuebingen.mpg.de/maan/osl/). The _object
+- [libosl](https://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
+               git clone https://git.tuebingen.mpg.de/osl
 
 - [openssl](https://www.openssl.org/) or
 [libgcrypt](ftp://ftp.gnupg.org/gcrypt/libgcrypt/).  At least one
@@ -339,7 +339,7 @@ to install the development package (`libssl-dev` or `libgcrypt-dev`
 on debian systems) as well.
 
 - [flex](https://github.com/westes/flex) and
-[bison](https://www.gnu.org/software/bison) are needed to build the
+[bison](https://www.gnu.org/software/bison/) are needed to build the
 mood parser of para_server. The build system will skip para_server
 if these tools are not installed.
 
@@ -385,7 +385,7 @@ the ao writer (ESD, PulseAudio,...).  Debian package: `libao-dev`.
 para_gui. Debian package: `libncurses-dev`.
 
 - [GNU
-Readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html). If
+Readline](https://www.gnu.org/software/readline/). If
 this library (`libreadline-dev`) is installed, para_client, para_audioc
 and para_play support interactive sessions.
 
@@ -523,7 +523,7 @@ starts streaming. To activate streaming, execute
 
 Since no playlist has been specified yet, the "dummy" mode which
 selects all known audio files is activated automatically. See the
-section on the [audio file selector](#The.audio.file.selector) for how
+section on the [audio file selector](#The-audio-file-selector) for how
 to use playlists and moods to specify which files should be streamed
 in which order.
 
@@ -555,12 +555,12 @@ authentication method. Authenticated connections are encrypted using
 the AES stream cipher in integer counter mode.
 
 In this chapter we briefly describe RSA and AES, and sketch the
-[authentication handshake](#Client-server.authentication)
+[authentication handshake](#Client-2d-server-authentication)
 between para_client and para_server. User management is discussed
-in the section on [the user_list file](#The.user_list.file).
+in the section on [the user_list file](#The-user_list-file).
 These sections are all about communication between the client and the
 server. Connecting para_audiod is a different matter and is described
-in a [separate section](#Connecting.para_audiod).
+in a [separate section](#Connecting-para_audiod).
 
 RSA and AES
 -----------
@@ -635,8 +635,8 @@ infeasibility to invert the hash function.
 
 Neither para_server or para_client create RSA keys on their
 own. This has to be done once for each user as sketched in
-[Quick start](#Quick.start) and discussed in more detail
-[below](#The.user_list.file).
+[Quick start](#Quick-start) and discussed in more detail
+[below](#The-user_list-file).
 
 The user_list file
 ------------------
@@ -708,13 +708,13 @@ maintains tables containing images (e.g. album cover art) and lyrics
 that can be associated with one or more audio files.
 
 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)
+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 practical examples. The way [file renames and content
-changes](#File.renames.and.content.changes) are detected is discussed
-briefly before the [Troubleshooting](#Troubleshooting) section
+changes](#File-renames-and-content-changes) are detected is discussed
+briefly before the [Troubleshooting](#Common-problems) section
 concludes the chapter.
 
 The AFS process
@@ -774,7 +774,7 @@ 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
+[libosl](https://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.
@@ -1166,7 +1166,7 @@ data remains as before.
 It is possible to change the behaviour of the add command by using the
 "-l" (lazy add) or the "-f" (force add) option.
 
-Troubleshooting
+Common problems
 ---------------
 
 Use the debug loglevel (-l debug) to show debugging info. All paraslash
@@ -1349,12 +1349,12 @@ used. For these data connections, a variety of transports (UDP, DCCP,
 HTTP) can be chosen.
 
 The chapter starts with the [control
-service](#The.paraslash.control.service), followed by a section
-on the various [streaming protocols](#Streaming.protocols)
+service](#The-paraslash-control-service), followed by a section
+on the various [streaming protocols](#Streaming-protocols)
 in which the data connections are described. The way
 audio file headers are embedded into the stream is discussed
-[briefly](#Streams.with.headers.and.headerless.streams) before the
-[example section](#Networking.examples) which illustrates typical
+[briefly](#Streams-with-headers-and-headerless-streams) before the
+[example section](#Networking-examples) which illustrates typical
 commands for real-life scenarios.
 
 Both IPv4 and IPv6 are supported.
@@ -1374,7 +1374,7 @@ is unaffected if the child dies or goes crazy for whatever reason. In
 fact, the child process can not change address space of server process.
 
 The section on [client-server
-authentication](#Client-server.authentication) above described the
+authentication](#Client-2d-server-authentication) above described the
 early connection establishment from the crypto point of view. Here
 it is described what happens after the connection (including crypto
 setup) has been established.  There are four processes involved during
@@ -1442,7 +1442,7 @@ only for Linux.
 
 - UDP. Recommended for multicast LAN streaming.
 
-See the Appendix on [network protocols](#Network.protocols)
+See the Appendix on [network protocols](#Network-protocols)
 for brief descriptions of the various protocols relevant for network
 audio streaming with paraslash.
 
@@ -1513,7 +1513,7 @@ be configured via the FEC parameters which are dictated by server
 and may also be configured through the "sender" command.  The FEC
 parameters are encoded in the header of each network packet, so no
 configuration is necessary on the receiver side. See the section on
-[FEC](#Forward.error.correction) below.
+[FEC](#Forward-error-correction) below.
 
 Streams with headers and headerless streams
 -------------------------------------------
@@ -1653,7 +1653,7 @@ executables. For example, the mp3dec filter depends on the mad library.
 Forward error correction
 ------------------------
 
-As already mentioned [earlier](#Streaming.protocols), paraslash
+As already mentioned [earlier](#Streaming-protocols), paraslash
 uses forward error correction (FEC) for the unreliable UDP and
 DCCP transports. FEC is a technique which was invented already in
 1960 by Reed and Solomon and which is widely used for the parity
@@ -1884,8 +1884,8 @@ shown by this theme. See gui_theme.c for examples.
 
 The "." and "," keys are used to switch between themes.
 
-Examples
---------
+Gui examples
+------------
 
 -> Show server info:
 
@@ -1953,8 +1953,8 @@ from tar balls) and for contributing non-trivial changes to the
 paraslash project, some additional tools should be installed on a
 developer machine.
 
-- [git](http://git.or.cz/). As described in more detail
-[below](#Git.branches), the git source code management tool is used for
+- [git](https://git-scm.com/). As described in more detail
+[below](#Git-branches), the git source code management tool is used for
 paraslash development. It is necessary for cloning the git repository
 and for getting updates.
 
@@ -1967,7 +1967,7 @@ HTML version of this manual and some of the paraslash web pages are
 written in the Markdown markup language and are translated into html
 with the converter of the *Discount* package.
 
-- [doxygen](http://www.stack.nl/~dimitri/doxygen/). The documentation
+- [doxygen](https://www.doxygen.nl/). The documentation
 of paraslash's C sources uses the doxygen documentation system. The
 conventions for documenting the source code is described in the
 [Doxygen section](#Doxygen).
@@ -2294,54 +2294,42 @@ and contributed significant improvements.
 References
 ==========
 
-Articles
---------
-- [Polynomial Codes over Certain Finite
-Fields](http://kom.aau.dk/~heb/kurser/NOTER/KOFA01.PDF) by Reed, Irving
-S.; Solomon, Gustave (1960), Journal of the Society for Industrial
-and Applied Mathematics (SIAM) 8 (2): 300-304, doi:10.1137/0108018)
-
 RFCs
 ----
 
-- [RFC 768](http://www.ietf.org/rfc/rfc768.txt) (1980): User Datagram
+- [RFC 768](https://www.ietf.org/rfc/rfc768.txt) (1980): User Datagram
 Protocol
 
-- [RFC 791](http://www.ietf.org/rfc/rfc791.txt) (1981): Internet
+- [RFC 791](https://www.ietf.org/rfc/rfc791.txt) (1981): Internet
 Protocol
 
-- [RFC 2437](http://www.ietf.org/rfc/rfc2437.txt) (1998): RSA
+- [RFC 2437](https://www.ietf.org/rfc/rfc2437.txt) (1998): RSA
 Cryptography Specifications
 
-- [RFC 4340](http://www.ietf.org/rfc/rfc4340.txt) (2006): Datagram
+- [RFC 4340](https://www.ietf.org/rfc/rfc4340.txt) (2006): Datagram
 Congestion Control Protocol (DCCP)
 
-- [RFC 4341](http://www.ietf.org/rfc/rfc4341.txt) (2006): Congestion
+- [RFC 4341](https://www.ietf.org/rfc/rfc4341.txt) (2006): Congestion
 Control ID 2: TCP-like Congestion Control
 
-- [RFC 4342](http://www.ietf.org/rfc/rfc4342.txt) (2006): Congestion
+- [RFC 4342](https://www.ietf.org/rfc/rfc4342.txt) (2006): Congestion
 Control ID 3: TCP-Friendly Rate Control (TFRC)
 
-- [RFC 6716](http://www.ietf.org/rfc/rfc6716.txt) (2012): Definition
+- [RFC 6716](https://www.ietf.org/rfc/rfc6716.txt) (2012): Definition
 of the Opus Audio Codec
 
 Application web pages
 ---------------------
 
-- [paraslash](http://people.tuebingen.mpg.de/maan/paraslash/)
-- [xmms](https://xmms2.org/wiki/Main_Page)
+- [paraslash](https://people.tuebingen.mpg.de/maan/paraslash/)
 - [mpg123](http://www.mpg123.de/)
 - [gstreamer](https://gstreamer.freedesktop.org/)
-- [icecast](http://www.icecast.org/)
-- [Audio Compress](https://beesbuzz.biz/code/audiocompress.php)
+- [icecast](https://www.icecast.org/)
+- [Audio Compress](https://github.com/fluffy-critter/audiocompress)
 
 External documentation
 ----------------------
 
-- [The mathematics of
-Raid6](https://www.kernel.org/pub/linux/kernel/people/hpa/raid6.pdf)
-by H. Peter Anvin
-
 - [Effective Erasure Codes for reliable Computer Communication
 Protocols](http://info.iet.unipi.it/~luigi/fec_ccr.ps.gz) by Luigi
 Rizzo
index 2043fbb70b2afdf9ef243200920910bf564f2a1e..3a928c058a53aa4156d4fb4473b5d4d8f6cc0b3e 100644 (file)
@@ -2,6 +2,8 @@ body {
        font-family: sans-serif;
        background-color: black;
        color: #bbbbbb;
+       text-align: justify;
+       padding: 0px 40px 40px 40px;
        margin: 20px;
 }
 
index f7ee2c4dbd1281e49792693afd40e83f275cd919..f2ca273cd08979f33b42ab0a7f84b533fe29a583 100644 (file)
@@ -1176,7 +1176,7 @@ next_buffer:
                return 0;
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &in);
-       ret = -E_WMADEC_EOF;
+       ret = -E_EOF;
        if (len < fn->min_iqs)
                goto err;
        if (!pwd) {
diff --git a/write.c b/write.c
index f7018aa115a632f345f5136c734f44e2b79d87a5..cb32d391eaa0f856414aa23318a7573def0d9eef 100644 (file)
--- a/write.c
+++ b/write.c
@@ -104,7 +104,7 @@ static int setup_and_schedule(struct lls_parse_result *lpr)
                        struct writer_node *wn = wns + j;
                        ts = task_status(wn->task);
                        assert(ts < 0);
-                       if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) {
+                       if (ts != -E_EOF) {
                                const char *name = writer_name(wn->wid);
                                PARA_ERROR_LOG("%s: %s\n", name,
                                        para_strerror(-ts));