]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
paraslash 0.7.3 master v0.7.3
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 12 Mar 2024 18:02:39 +0000 (19:02 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 12 Mar 2024 18:02:39 +0000 (19:02 +0100)
64 files changed:
Doxyfile
Makefile.real
NEWS.md
afh_recv.c
afs.c
afs.h
aft.c
alsa_write.c
amp_filter.c
ao_write.c
audioc.c
audiod.c
audiod.h
audiod_command.c
buffer_tree.c
client.c
client_common.c
command.c
compress_filter.c
configure.ac
crypt.h
crypt_common.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
m4/lls/server_cmd.suite.m4
mood.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
play.c
playlist.c
resample_filter.c
sched.c
score.c
send.h
send_common.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
user_list.c
wav_filter.c
wmadec_filter.c
write.c

index e147548f31f090b1fc351a537caa894d3fc87f1f..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
@@ -2086,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
 #---------------------------------------------------------------------------
@@ -2105,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.
@@ -2341,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.
 
index 95b6863010b5e0281a5d6cb41a784e78f0305857..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 := 2023
+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 fff7a242bba99d3a322b47c0d101d36b93e8d8d5..d5812289640d71c52a60bd850928c72b3c5ba0b8 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,9 +1,54 @@
 NEWS
 ====
 
-------------------------------------------
-0.7.2 (to be announced) "optical friction"
-------------------------------------------
+---------------------------------------------
+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
@@ -14,9 +59,12 @@ NEWS
 - 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-git.tar.xz)
+[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"
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 b15f83852e6919c4f0867065d13776c211416087..445d5871097b79cdcd14170c2a1f59998354dc98 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -437,18 +437,18 @@ static int activate_mood_or_playlist(const char *arg, struct para_buffer *pb)
        int ret;
        char *msg;
 
-       if (!arg) {
-               ret = mood_load(NULL, &msg);
+       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, &msg);
+               ret = playlist_load(arg + 2, NULL, &msg);
                mode = PLAY_MODE_PLAYLIST;
        } else if (!strncmp(arg, "m/", 2)) {
-               ret = mood_load(arg + 2, &msg);
+               ret = mood_load(arg + 2, NULL, &msg);
                mode = PLAY_MODE_MOOD;
        } else {
                ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-               msg = make_message("%s: parse error", arg);
+               msg = make_message("%s: parse error\n", arg);
        }
        if (pb)
                para_printf(pb, "%s", msg);
@@ -529,8 +529,8 @@ static void init_admissible_files(const char *arg)
 {
        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);
        }
@@ -580,17 +580,6 @@ 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;
@@ -962,7 +951,8 @@ __noreturn void afs_init(int socket_fd)
        }
        ret = schedule(&s);
        sched_shutdown(&s);
-       mood_unload();
+       mood_unload(NULL);
+       playlist_unload(NULL);
 out_close:
        close_afs_tables();
 out:
@@ -980,29 +970,32 @@ 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();
+               mood_unload(NULL);
        else
-               playlist_unload();
-       ret = activate_mood_or_playlist(arg, &aca->pbout);
+               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, &aca->pbout);
+               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, &aca->pbout);
+       activate_mood_or_playlist(NULL, pbout);
 free_lpr:
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
@@ -1018,7 +1011,8 @@ static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
                send_errctx(cc, errctx);
                return ret;
        }
-       return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
+       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);
 
@@ -1060,7 +1054,8 @@ 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) {
diff --git a/afs.h b/afs.h
index 9a1d7d9ca0f130c4590f55c23abc181b632c6483..e8b8c865bda36b9f905b2c1cb435d03bc12d3cbd 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -238,10 +238,12 @@ int for_each_matching_row(struct pattern_match_data *pmd);
 
 /* score */
 extern const struct afs_table_operations score_ops;
-int score_loop(osl_rbtree_loop_func *func, void *data);
+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 score_delete(const struct osl_row *aft_row);
 void score_clear(void);
@@ -268,13 +270,17 @@ int aft_check_callback(struct afs_callback_arg *aca);
 void free_status_items(void);
 
 /* mood */
-int mood_load(const char *mood_name, char **msg);
-void mood_unload(void);
+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_load(const char *name, char **msg);
-void playlist_unload(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 */
diff --git a/aft.c b/aft.c
index 0009a54f3c5b1f169381ff631f20b141da71f75b..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 = score_loop(prepare_ls_row, opts);
-       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);
@@ -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;
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 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 743d73a9277802fbb966b373c2af357c14a88155..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.
- *
- * Only those status items having the bit set in \a mask will be
- * sent to the client.
+/*
+ * Add a status client to the global client list and increment num_clients.
  *
- * \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,7 +164,6 @@ 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);
@@ -284,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};
@@ -296,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);
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..84b7580cf9554c433651da1f3eed28a6f1f39a74 100644 (file)
--- a/client.c
+++ b/client.c
@@ -666,8 +666,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 95e59fd29de2015fcc65443a4ffc6442387ec0f8..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;
@@ -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 c56a15822dfaf8a7b165b44480a6adcf90d5e1a3..60c2aeba4300124c51af13d03c522fe2c2bbb1f1 100644 (file)
--- a/command.c
+++ b/command.c
@@ -710,7 +710,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) {
@@ -718,21 +718,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)
@@ -824,19 +811,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..92560e00ec50b7fa7ae4fcfd7d8966bdca2c63bf 100644 (file)
@@ -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;
diff --git a/error.h b/error.h
index 7c146da2076ae74d896bb82a0e931d8b5b6f49f5..8805c9c7a4c0de6fe958e1523df883ad47effaad 100644 (file)
--- a/error.h
+++ b/error.h
        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"), \
@@ -36,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"), \
@@ -51,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"), \
@@ -64,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"), \
@@ -72,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"), \
@@ -83,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"), \
@@ -96,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(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
@@ -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 32c9e7b9af6b24ca0fc93f3d7d65fce15c32e03a..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;
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';
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/mood.c b/mood.c
index 9cdfd011a6ef4f2ab9fb180892f6412247eb68fe..1e15ef0e081480381fcbc4fdad2179848f429c1f 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -59,6 +59,8 @@ struct mood_instance {
        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;
 };
 
 /*
@@ -132,6 +134,8 @@ 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);
 }
@@ -437,7 +441,7 @@ static void update_afs_statistics(struct afs_info *old_afsi,
 }
 
 static int add_to_score_table(const struct osl_row *aft_row,
-               const struct afs_statistics *stats)
+               struct mood_instance *m)
 {
        long score;
        struct afs_info afsi;
@@ -445,8 +449,8 @@ static int add_to_score_table(const struct osl_row *aft_row,
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi, stats);
-       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)
@@ -504,7 +508,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                ret = add_afs_statistics(aft_row, &current_mood->stats);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row, &current_mood->stats);
+               return add_to_score_table(aft_row, current_mood);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
@@ -529,24 +533,37 @@ static char *get_statistics(struct mood_instance *m, int64_t sse)
        unsigned n = m->stats.num;
        int mean_days, sigma_days;
 
+       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)
+               int_sqrt(m->stats.num_played_qd / n),
+               86400.0 * m->stats.last_played_correction /
+                       m->stats.num_played_correction
        );
 }
 
-/** Free all resources of the current mood, if any. */
-void mood_unload(void)
+/**
+ * Free all resources of a mood instance.
+ *
+ * \param m As obtained by \ref mood_load(). If NULL, unload the current mood.
+ *
+ * It's OK to call this with m == NULL even if no current mood is loaded.
+ */
+void mood_unload(struct mood_instance *m)
 {
+       if (m)
+               return destroy_mood(m);
        destroy_mood(current_mood);
        current_mood = NULL;
 }
@@ -568,23 +585,42 @@ static void compute_correction_factors(int64_t sse, struct afs_statistics *s)
 }
 
 /**
- * Change the current mood.
+ * Populate a score table with admissible files for the given mood.
+ *
+ * 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.
  *
  * \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.
  *
- * 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.
+ * If the mood name is NULL, the dummy mood is loaded. This mood regards every
+ * audio file as admissible.
+ *
+ * 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.
+ *
+ * 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.
  *
  * 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. It is
+ * \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 struct \ref afs_info::last_played, \ref mp_eval_row().
+ * \sa \ref mp_eval_row().
  */
-int mood_load(const char *mood_name, char **msg)
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg)
 {
        int i, ret;
        struct admissible_array aa = {.size = 0};
@@ -609,14 +645,10 @@ int mood_load(const char *mood_name, char **msg)
        }
        clock_get_realtime(&rnow);
        compute_correction_factors(rnow.tv_sec, &aa.m->stats);
-       if (aa.m->stats.num == 0) {
-               if (msg)
-                       *msg = make_message("no admissible files\n");
-               ret = 0;
-               goto out;
-       }
+       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->stats);
+               ret = add_to_score_table(aa.array[i], aa.m);
                if (ret < 0) {
                        if (msg)
                                *msg = make_message(
@@ -628,21 +660,43 @@ int mood_load(const char *mood_name, char **msg)
        if (msg)
                *msg = get_statistics(aa.m, rnow.tv_sec);
        ret = aa.m->stats.num;
-       mood_unload();
-       current_mood = aa.m;
+       if (result)
+               *result = aa.m;
+       else {
+               mood_unload(NULL);
+               current_mood = aa.m;
+       }
+       ret = 1;
 out:
        free(aa.array);
-       if (ret < 0)
+       if (ret <= 0) /* error, or no admissible files */
                destroy_mood(aa.m);
        return ret;
 }
 
+/**
+ * Iterate over the admissible files of a mood instance.
+ *
+ * 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.
  *
- * 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 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)
 {
@@ -655,8 +709,8 @@ static int reload_current_mood(void)
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
-       mood_unload();
-       ret = mood_load(mood_name, NULL);
+       mood_unload(NULL);
+       ret = mood_load(mood_name, NULL, NULL);
        free(mood_name);
        return ret;
 }
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..5ca1307f680d366c155b2e98257ba807c5a4fb92 100644 (file)
--- a/mp4.c
+++ b/mp4.c
@@ -1044,7 +1044,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 a24081f5123bd5a62236ff6d8202afc4e16c009b..9b3624428d5e8a0f63df7d814783df20afa54385 100644 (file)
--- a/net.c
+++ b/net.c
@@ -190,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;
diff --git a/net.h b/net.h
index d206881c587b8ec185856e23e84bb26ecf032f45..33acfc890495087991c964397371e4edf9b574f1 100644 (file)
--- a/net.h
+++ b/net.h
@@ -25,7 +25,7 @@ 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);
-char *format_url(const char *url, int default_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,
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/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 d02ade3ba911a186f0f7e5a57f419ce9074092d0..c145b0fd80ce0520bda58f0949c46211eeee5fdd 100644 (file)
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
-/** Structure used for adding entries to a playlist. */
+/**
+ * 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_instance current_playlist;
 
@@ -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_instance *playlist = data;
+       struct playlist_instance *pi = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
@@ -46,12 +52,12 @@ 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++;
+       pi->length++;
        return 1;
 }
 
@@ -103,11 +109,22 @@ int playlist_check_callback(struct afs_callback_arg *aca)
                check_playlist));
 }
 
-/** Free all resources of the current playlist, if any. */
-void playlist_unload(void)
+/**
+ * Free all resources of the given/current playlist.
+ *
+ * \param pi NULL means to unload the current playlist.
+ */
+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;
@@ -122,43 +139,78 @@ void playlist_unload(void)
  * corresponding row of the audio file table is added to the score table.
  *
  * \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 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_load(const char *name, char **msg)
+int playlist_load(const char *name, struct playlist_instance **result, char **msg)
 {
        int ret;
-       struct playlist_instance *playlist = &current_playlist;
+       struct playlist_instance *pi;
        struct osl_object playlist_def;
 
-       ret = pl_get_def_by_name(name, &playlist_def);
-       if (ret < 0) {
-               *msg = make_message("could not read playlist %s\n", name);
-               return ret;
+       if (!name || !*name) {
+               if (msg)
+                       *msg = make_message("empty playlist name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        }
-       playlist_unload();
+       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, playlist);
+               playlist_def.size, add_playlist_entry, pi);
        osl_close_disk_object(&playlist_def);
        if (ret < 0)
-               goto err;
+               goto close_score_table;
        ret = -E_PLAYLIST_EMPTY;
-       if (!playlist->length)
-               goto err;
-       playlist->name = para_strdup(name);
-       *msg = make_message("loaded playlist %s (%u files)\n", playlist->name,
-               playlist->length);
+       if (pi->length == 0)
+               goto close_score_table;
        /* success */
-       return current_playlist.length;
+       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;
+       }
+       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);
-       *msg = make_message("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)
 {
        if (strcmp(path, data))
@@ -196,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 */
 }
 
 /**
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 10cd254a8dba2926614d0a76ffa29cad90e74792..c03e3472da306a1d535b201ea2d23c90ccf8f90a 100644 (file)
--- a/score.c
+++ b/score.c
@@ -77,10 +77,10 @@ static struct osl_table_description score_table_desc = {
 };
 
 /* 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;
@@ -88,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];
@@ -112,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);
@@ -152,7 +153,7 @@ int score_update(const struct osl_row *aft_row, long percent)
        ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
        if (ret < 0)
                return ret;
-       ret = get_score_of_row(rrow, &new_score);
+       ret = get_score_of_row(score_table, rrow, &new_score);
        if (ret < 0)
                return ret;
        new_score--;
@@ -176,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;
@@ -187,26 +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));
 }
 
 /**
  * Call the given function for each row of the score table.
  *
  * \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.
  *
  * \return The return value of the underlying call to osl_rbtree_loop(). The
  * loop terminates early if the callback returns negative.
  */
-int score_loop(osl_rbtree_loop_func *func, void *data)
+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));
 }
 
 /**
@@ -229,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);
 }
 
 /**
@@ -244,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;
@@ -263,7 +266,7 @@ int score_delete(const struct osl_row *aft_row)
 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 false;
@@ -271,29 +274,56 @@ bool row_belongs_to_score_table(const struct osl_row *aft_row)
        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)
+{
+       assert(osl_close_table(t? t : score_table, OSL_FREE_VOLATILE) >= 0);
+}
+
+static void close_global_table(void)
 {
-       osl_close_table(score_table, OSL_FREE_VOLATILE);
-       score_table = NULL;
+       score_close(NULL);
 }
 
-static int score_open(__a_unused const char *dir)
+static int open_global_table(__a_unused const char *dir)
 {
-       assert(osl_open_table(&score_table_desc, &score_table) >= 0);
+       assert(osl(osl_open_table(&score_table_desc, &score_table)) >= 0);
        return 1;
 }
 
+/**
+ * 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)
+{
+       if (result)
+               assert(osl(osl_open_table(&score_table_desc, result)) >= 0);
+       else
+               open_global_table(NULL);
+}
+
 /**
  * Remove all entries from the score table, but keep the table open.
  */
 void score_clear(void)
 {
-       score_close();
-       score_open(NULL);
+       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 = score_open,
-       .close = score_close,
+       .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 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 365b6863087ca78c1bdc446099ff7f17c8b15c6c..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)
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,
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 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));