]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge commit 'meins/master'
authorAndre Noll <maan@systemlinux.org>
Fri, 9 Jan 2009 00:16:44 +0000 (01:16 +0100)
committerAndre Noll <maan@systemlinux.org>
Fri, 9 Jan 2009 00:16:44 +0000 (01:16 +0100)
91 files changed:
INSTALL
Makefile.in
NEWS
README.afs
REQUIREMENTS
aac_afh.c
aacdec.c [deleted file]
aacdec_filter.c [new file with mode: 0644]
afh.c
afh_common.c
afs.c
aft.c
alsa_write.c
alsa_write.ggo
amp_filter.c
amp_filter.ggo
attribute.c
audiod.c
audiod.ggo
audiod_command.c
blob.c
client.c
client.ggo
client_common.c
compress.c [deleted file]
compress_filter.c [new file with mode: 0644]
compress_filter.ggo
configure.ac
dccp_recv.c
dccp_recv.ggo
error.h
exec.c
fade.c
fade.ggo
fd.c
file_write.c
file_write.ggo
filter.c
filter.ggo
filter.h
filter_chain.c [deleted file]
filter_common.c [new file with mode: 0644]
ggo.c [new file with mode: 0644]
ggo.h [new file with mode: 0644]
grab_client.c
gui.c
gui_theme.c
hash.h
http_recv.c
http_recv.ggo
mood.c
mp3_afh.c
mp3dec.c [deleted file]
mp3dec_filter.c [new file with mode: 0644]
mp3dec_filter.ggo [new file with mode: 0644]
net.c
ogg_afh.c
oggdec.c [deleted file]
oggdec_filter.c [new file with mode: 0644]
oggdec_filter.ggo
ortp_recv.c
ortp_recv.ggo
osl.c
osl_core.h
osx_write.c
para.h
rbtree.c
recv.c
recv.ggo
recv.h
recv_common.c
send.h
send_common.c
server.c
server.ggo
signal.c
stdin.c
stdin.h
string.c
time.c
versions/paraslash-0.3.3.tar.bz2 [new file with mode: 0644]
versions/paraslash-0.3.3.tar.bz2.asc [new file with mode: 0644]
vss.c
wav.c [deleted file]
wav_filter.c [new file with mode: 0644]
web/index.in.html
write.c
write.ggo
write.h
write_common.c
write_common.h

diff --git a/INSTALL b/INSTALL
index 3c059058110bdc19cfe9b2c0ccbd6bbc798ec909..9cc36f41aca382ddcfde6dc45566cf33aa4cba92 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -92,6 +92,14 @@ Finally, tell para_client to connect to server_host:
 Start para_server
 -----------------
 
+Before starting the server make sure you have write permissions to
+the directory /var/paraslash.
+
+       sudo chown $user /var/paraslash
+
+Alternatively, use the --afs_socket Option to specify a different
+location for the afs command socket.
+
 For this first try, we'll use a debug level of two to make the
 output of para_server more verbose.
 
index 506b2af049a2b90443fd79cb915c0c66e24b80f2..4e6ef96258b4f7c2fcf4ee324d0a6ac4b4ae7b01 100644 (file)
@@ -12,7 +12,7 @@ build_date := $(shell date)
 uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
-codename := axiomatic perspectivity
+codename := elliptic inheritance
 
 DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
 DEBUG_CPPFLAGS += -Wredundant-decls
@@ -23,6 +23,7 @@ DEBUG_CPPFLAGS += -Wredundant-decls
 # invalid option for gcc-3.3.3
 # DEBUG_CPPFLAGS += -Wextra
 # DEBUG_CPPFLAGS += -Wold-style-definition
+# DEBUG_CPPFLAGS += -Wdeclaration-after-statement
 
 # many warnings about trivial stuff
 # CPPFLAGS += -Wconversion
@@ -45,6 +46,7 @@ CPPFLAGS += -DCODENAME='"$(codename)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -Werror-implicit-function-declaration
 CPPFLAGS += -Wmissing-format-attribute
+CPPFLAGS += -Wmissing-noreturn
 CPPFLAGS += -Wunused-macros
 CPPFLAGS += -Wbad-function-cast
 CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
@@ -114,6 +116,10 @@ grab_client.cmdline.h grab_client.cmdline.c: grab_client.ggo
                audioc.ggo) O="--unamed-opts=command";; \
                fsck.ggo) O="--unamed-opts=table";; \
                afh.ggo) O="--unamed-opts=audio_file";; \
+               recv.ggo) O="--no-handle-help";; \
+               filter.ggo) O="--no-handle-help";; \
+               write.ggo) O="--no-handle-help";; \
+               audiod.ggo) O="--no-handle-help";; \
        esac; \
        if test $< != fsck.ggo; then O="$$O --conf-parser "; fi; \
        gengetopt $$O \
@@ -141,6 +147,18 @@ man/man1/para_audiod.1: para_audiod audiod_command_list.man
        mkdir -p man/man1
        help2man -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@
 
+man/man1/para_filter.1: para_filter
+       mkdir -p man/man1
+       help2man -h --detailed-help -N ./$< > $@
+
+man/man1/para_write.1: para_write
+       mkdir -p man/man1
+       help2man -h --detailed-help -N ./$< > $@
+
+man/man1/para_recv.1: para_recv
+       mkdir -p man/man1
+       help2man -h --detailed-help -N ./$< > $@
+
 man/man1/%.1: %
        mkdir -p man/man1
        help2man -N ./$< > $@
@@ -159,15 +177,15 @@ ortp_recv.o: ortp_recv.c
 ortp_send.o: ortp_send.c
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ortp_cppflags@ $<
 
-oggdec.o: oggdec.c
+oggdec_filter.o: oggdec_filter.c
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $<
 ogg_afh.o: ogg_afh.c
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $<
 
-mp3dec.o: mp3dec.c
+mp3dec_filter.o: mp3dec_filter.c
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $<
 
-aacdec.o: aacdec.c
+aacdec_filter.o: aacdec_filter.c
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
 
 aac_common.o: aac_common.c
@@ -238,7 +256,7 @@ install: all man
 
 @PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.bz2: $(tarball_add)
        rm -rf $(tarball_pfx).tar.bz2 $(tarball_pfx)
-       git-archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
+       git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
                | tar --delete $(tarball_delete) > $(tarball_pfx).tar
        mkdir $(tarball_pfx)
        cp -r $(tarball_add) $(tarball_pfx)
diff --git a/NEWS b/NEWS
index 684485cf0bef753424750e45128dbe866439c093..8418cd8a13ea75e6eec656c0beafd15f7f702a20 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,18 @@
 NEWS
 ====
 
--------------------------------------------------
-0.3.3 (to be announced) "axiomatic perspectivity"
--------------------------------------------------
+----------------------------------------------
+0.3.4 (to be announced) "elliptic inheritance"
+----------------------------------------------
+       - new options for mp3dec: --ignore-crc, --bufsize
+       - Improved help/man pages: The documentation of para_audiod,
+         para_recv, para_filter and para_write now also contains
+         all options of the available receivers/filters/writers.
+       - new audiod option: --config-file.
+
+--------------------------------------------
+0.3.3 (2008-12-01) "axiomatic perspectivity"
+--------------------------------------------
 
 Internal code cleanups, bug fixes, improved tag handling and the new
 amplification filter.
@@ -12,11 +21,14 @@ amplification filter.
        - overhaul of the virtual streaming system.
        - mp3: id3 version 2 support via libid3tag (optional)
        - ogg: vorbis comment support.
+       - aac meta info support.
        - mp3 audio format handler cleanups.
        - new filter: "amp" to amplify the amplitude of the audio stream
        - new status item/database entry: amplification. It is
          used by the amp filter to pre-amplify the audio stream.
        - fix a close-without-open bug in para_write.
+       - fix a bug in com_init() which was introduced in 0.3.2.
+       - better error diagnostics for para_client.
 
 -----------------------------------------
 0.3.2 (2008-04-11) "probabilistic parity"
index d77f6bfa39a84fdb35304f1f55d4344d7160ba86..9e588a6a6c7a888168bf61af239823e81c98f560 100644 (file)
@@ -1,7 +1,7 @@
 The audio file selector
 =======================
 
-Paraslash comes with a sophisticated audio file selector called afs.
+Paraslash comes with a sophisticated audio file selector called *afs*.
 In the
 <<
 <a href="INSTALL.html">installation notes</a>,
@@ -31,12 +31,12 @@ an audio file by executing
 
        para_client setatt test+ /path/to/the/audio/file
 
-Similarly, the "test" bit can be removed from a audio file with
+Similarly, the "test" bit can be removed from an audio file with
 
        para_client setatt test- /path/to/the/audio/file
 
-Instead of a path you can also use a pattern, and the attribute is
-applied to all audio files matching that pattern:
+Instead of a path you may use a shell wildcard pattern. The attribute
+is applied to all audio files matching that pattern:
 
        para_client setatt test+ '/test/directory/*'
 
@@ -53,9 +53,9 @@ you find this annoying, just say
 
        alias para='para_client --'
 
-and be happy. In the remainder part this alias is being used.
+and be happy. In what follows we shall use this alias.
 
-Drop the test attribute entirely from the database with
+The "test" attribute can be dropped from the database with
 
        para rmatt test
 
@@ -74,13 +74,14 @@ Abstract mood nonsense
 
 [skip this part if you don't like formal definitions]
 
-A mood consists of a unique name and its *mood definition*, which is a set of
-*mood lines* containing expressions in terms of attributes and other data
-contained in the database.
+A mood consists of a unique name and its *mood definition*, which is
+a set of *mood lines* containing expressions in terms of attributes
+and other data contained in the database.
 
-A mood defines a subset of audio files called the *admissible audio files*
-for that mood. A mood can be *active* which means that para_server
-is going to select only files from that subset of admissible files.
+A mood defines a subset of audio files called the *admissible audio
+files* for that mood. At any time, at most one mood can be *active*
+which means that para_server is going to select only files from that
+subset of admissible files.
 
 So in order to create a mood definition one has to write a set of
 mood lines. Mood lines come in three flavours: Accept lines, deny
@@ -99,7 +100,7 @@ a random score to all matching files. The score value changes the
 order in which admissible files are going to be selected, but is of
 minor importance for this introduction.
 
-So we concentrate on the first two forms, that is accept and deny
+So we concentrate on the first two forms, i.e. accept and deny
 lines. As usual, everything in square brackets is optional, i.e.
 accept/deny lines take the following form when ignoring scores:
 
@@ -121,12 +122,12 @@ or
 
 The set of admissible files for the whole mood is now defined as those
 files which match at least one accept mood line, but no deny mood line.
-More formally, a file is admissible if and only if
+More formally, an audio file F is admissible if and only if
 
        (F ~ AL1 or F ~ AL2...) and not (F ~ DL1 or F ~ DN2 ...)
 
-where F is the file, AL1, AL2... are the accept lines, DL1, DL2... are
-the deny lines and "~" means "matches".
+where AL1, AL2... are the accept lines, DL1, DL2... are the deny
+lines and "~" means "matches".
 
 The cases where no mood lines of accept/deny type are defined need
 special treatment:
@@ -192,7 +193,7 @@ the catmood command to get it back:
 
 A mood can be activated by executing
 
-       para chmood my_mood
+       para select m/my_mood
 
 Once active, the list of admissible files is shown by the ls command
 if the "-a" switch is given:
@@ -226,14 +227,14 @@ Troubles?
 
 Use loglevel one (option -l 1 for most commands) to show debugging
 info. Almost all paraslash executables have a brief online help which
-is displayed by using the -h switch.
+is displayed by using the -h switch. The --detailed-help option prints
+the full help text.
 
 para_fsck tries to fix your database. Use --force (even if your name
-isn't Luke), to clean up after a crash. However, first make sure
+isn't Luke) to clean up after a crash. However, first make sure
 para_server isn't running before executing para_fsck if para_fsck
 complains about busy (dirty) tables. para_fsck also contains an option
-to dump the contents of your the contents of the database to the file
-system.
+to dump the contents of the database to the file system.
 
 If you don't mind to recreate your database you can start
 from scratch by removing the entire database directory, i.e.
index a83c4f39b9571e108200a992852cf73cee4650fc..b03c4c3f98a2fcef432389daa329893cfb7ca490 100644 (file)
@@ -19,17 +19,19 @@ In any case you need
 Optional features:
 
        - *mp3*: The mp3 decoder of para_filter is based on libmad:
-         http://www.underbit.com/products/mad/. If you prefer to use
-         the libmad package provided by your distributor, make sure
-         to install the corresponding development package as well.
-
-         For version 2 id3 tag support, you'll need libid3tag which
-         is also available through the above link. Without libid3tag,
-         only version 1 tags are recognized.
-
+         http://www.underbit.com/products/mad/. If you prefer to
+         use the libmad package provided by your distributor, make
+         sure to install the corresponding development package as
+         well. It is called libmad0-dev on debian-based systems.
          Note that libmad is not necessary for the server side,
          i.e. for sending mp3 files.
 
+       - *id3 tags*:
+         For version-2 id3 tag support, you'll need libid3tag which
+         is also available through the above link (alternatively:
+         install package libid3tag0-dev). Without libid3tag, only
+         version one tags are recognized.
+
        - *ogg vorbis*: For ogg vorbis streams you'll need libogg,
          libvorbis, libvorbisfile: http://www.xiph.org/downloads/.
          The corresponding Debian packages are called libogg-dev
@@ -37,7 +39,8 @@ Optional features:
 
        - *aac*:
          For aac files (m4a) you'll need libfaad. Get it at
-         http://www.audiocoding.com/modules/wiki/?page=AAC
+         http://www.audiocoding.com/.
+         Debian package: libfaad-dev.
 
        - *ortp*:
          If you intend to use the optional ortp streamer, you'll
index a4166d1aeedd2b2cf761b7b81180860a9eac781f..80b50a4d55b6645aba8331c562b3667dc42663f7 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
@@ -42,6 +42,135 @@ static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip)
        return -E_STSZ;
 }
 
+static int atom_cmp(unsigned char *buf1, char *buf2)
+{
+       unsigned char *b2 = (unsigned char *)buf2;
+
+       if (buf1[0] != b2[0])
+               return 1;
+       if (buf1[1] != b2[1])
+               return 1;
+       if (buf1[2] != b2[2])
+               return 1;
+       if (buf1[3] != b2[3])
+               return 1;
+       return 0;
+}
+
+static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char type[5])
+{
+       int i;
+       uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+
+       memcpy(type, buf + 4, 4);
+       type[4] = '\0';
+
+       PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
+       if (size != 1) {
+               *subsize = size;
+               return 8;
+       }
+       buf += 4;
+       size = 0;
+       for (i = 0; i < 8; i++)
+               size |= ((uint64_t)buf[i]) << ((7 - i) * 8);
+       *subsize = size;
+       return 16;
+}
+
+static char *get_tag(unsigned char *p, int size)
+{
+       char *buf;
+
+       assert(size > 0);
+       buf = para_malloc(size + 1);
+
+       memcpy(buf, p, size);
+       buf[size] = '\0';
+       PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
+       return buf;
+}
+
+static char *read_tags(unsigned char *buf, size_t buflen)
+{
+       unsigned char *p = buf;
+       char *title = NULL, *artist = NULL, *album = NULL, *year = NULL,
+               *comment = NULL, *result;
+
+       while (p + 32 < buf + buflen) {
+               unsigned char *q, type1[5], type2[5];
+               uint64_t size1, size2;
+               int ret, ret2;
+
+               ret = read_atom_header(p, &size1, type1);
+               ret2 = read_atom_header(p + ret, &size2, type2);
+
+               if (size2 <= 16 || atom_cmp(type2, "data")) {
+                       p += size1;
+                       continue;
+               }
+               size2 -= 16;
+               q = p + ret + ret2 + 8;
+               if (q + size2 > buf + buflen)
+                       break;
+               if (!atom_cmp(type1, "©ART"))
+                       artist = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©alb"))
+                       album = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©nam"))
+                       title = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©cmt"))
+                       comment = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©day"))
+                       year = get_tag(q, size2);
+               p += size1;
+       }
+       result = make_taginfo(title, artist, album, year, comment);
+       free(title);
+       free(artist);
+       free(album);
+       free(year);
+       free(comment);
+       return result;
+}
+
+static char *read_meta(unsigned char *buf, size_t buflen)
+{
+       unsigned char *p = buf;
+
+       while (p + 4 < buf + buflen) {
+
+               if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
+                       p++;
+                       continue;
+               }
+               p += 4;
+               return read_tags(p, buflen - (p - buf));
+       }
+       return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
+static char *aac_get_taginfo(unsigned char *buf, size_t buflen)
+{
+       int i;
+       uint64_t subsize;
+       unsigned char type[5];
+
+       for (i = 0; i + 24 < buflen; i++) {
+               unsigned char *p = buf + i;
+               if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
+                       continue;
+               PARA_INFO_LOG("found metadata at offset %d\n", i);
+               i += 8;
+               p = buf + i;
+               i += read_atom_header(p, &subsize, type);
+               p = buf + i;
+               return read_meta(p, buflen - i);
+       }
+       PARA_INFO_LOG("no meta data\n");
+       return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
 static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
                unsigned char *map, size_t numbytes)
 {
@@ -103,10 +232,12 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        mp4AudioSpecificConfig mp4ASC;
        NeAACDecHandle handle = NULL;
        unsigned char *umap = (unsigned char *) map;
+       char *taginfo;
 
        ret = aac_find_esds(umap, numbytes, &skip, &decoder_len);
        if (ret < 0)
                goto out;
+       taginfo = aac_get_taginfo(umap, numbytes);
        handle = aac_open();
        ret = -E_AAC_AFH_INIT;
        if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels))
@@ -137,11 +268,10 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
        ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
        afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
-       afhi->info_string = make_message("%s:\n%s:\n%s:\n",
+       afhi->info_string = make_message("%s:\n%s",
                status_item_list[SI_AUDIO_FILE_INFO],
-               status_item_list[SI_TAGINFO1],
-               status_item_list[SI_TAGINFO2]
-       );
+               taginfo);
+       free(taginfo);
        tv_scale(20, &afhi->chunk_tv, &afhi->eof_tv);
        ret = 1;
 out:
diff --git a/aacdec.c b/aacdec.c
deleted file mode 100644 (file)
index 235b292..0000000
--- a/aacdec.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/*
- * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
- * Ahead Software AG
- */
-
-/** \file aacdec.c paraslash's aac (m4a) decoder */
-
-#include "para.h"
-
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-#include "aac.h"
-
-/** the output buffer size */
-#define AAC_OUTBUF_SIZE (32 * 1024)
-
-/** give up decoding after that many errors */
-#define MAX_ERRORS 20
-
-/**
- * data specific to the aacdec filter
- *
- * \sa filter, filter_node
- */
-struct private_aacdec_data {
-       /** the return value of aac_open */
-       NeAACDecHandle handle;
-       /** info about the currently decoded frame */
-       NeAACDecFrameInfo frame_info;
-       /** whether this instance of the aac decoder is already initialized */
-       int initialized;
-       /**
-        * return value of aac_find_esds(). Used to call the right aacdec
-        * init function
-        */
-       unsigned long decoder_length;
-       /** number of times the decoder returned an error */
-       unsigned error_count;
-       /** number of bytes already consumed from the imput stream */
-       size_t consumed_total;
-       /** return value of aac_find_entry_point */
-       size_t entry;
-};
-
-static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn)
-{
-       struct private_aacdec_data *padd = fn->private_data;
-       struct filter_chain *fc = fn->fc;
-       int i, ret;
-       unsigned char *p, *outbuffer;
-       unsigned char *inbuf = (unsigned char*)input_buffer;
-       size_t skip, consumed = 0;
-
-       if (fn->loaded > fn->bufsize * 3 / 5)
-               return 0;
-       if (len < 2048 && !*fc->input_error)
-               return 0;
-
-       if (!padd->initialized) {
-               unsigned long rate = 0;
-               unsigned char channels = 0;
-               ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
-               if (ret < 0) {
-                       PARA_INFO_LOG("%s\n", para_strerror(-ret));
-                       ret = NeAACDecInit(padd->handle, inbuf,
-                               len, &rate, &channels);
-                       PARA_INFO_LOG("decoder init: %d\n", ret);
-                       if (ret < 0) {
-                               ret = -E_AACDEC_INIT;
-                               goto out;
-                       }
-                       consumed = ret;
-               } else {
-                       PARA_INFO_LOG("decoder len: %lu\n",
-                               padd->decoder_length);
-                       consumed += skip;
-                       p = inbuf + consumed;
-                       ret = -E_AACDEC_INIT;
-                       if (NeAACDecInit2(padd->handle, p,
-                                       padd->decoder_length, &rate,
-                                       &channels) < 0)
-                               goto out;
-               }
-               fc->samplerate = rate;
-               fc->channels = channels;
-               PARA_INFO_LOG("rate: %u, channels: %d\n",
-                       fc->samplerate, fc->channels);
-               padd->initialized = 1;
-       }
-       if (padd->decoder_length > 0) {
-               consumed = 0;
-               if (!padd->entry) {
-                       ret = aac_find_entry_point(inbuf + consumed,
-                               len - consumed, &skip);
-                       if (ret < 0) {
-                               ret = len;
-                               goto out;
-                       }
-                       consumed += skip;
-                       padd->entry = ret;
-                       PARA_INFO_LOG("entry: %zu\n", padd->entry);
-               }
-               ret = len;
-               if (padd->consumed_total + len < padd->entry)
-                       goto out;
-               if (padd->consumed_total < padd->entry)
-                       consumed = padd->entry - padd->consumed_total;
-       }
-       for (; consumed < len; consumed++)
-               if ((inbuf[consumed] & 0xfe) == 0x20)
-                       break;
-       if (consumed >= len)
-               goto success;
-       p = inbuf + consumed;
-       outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p,
-               len - consumed);
-       if (padd->frame_info.error) {
-               ret = -E_AAC_DECODE;
-               if (padd->error_count++ > MAX_ERRORS)
-                       goto out;
-               PARA_ERROR_LOG("frame_error: %d, consumed: %zu + %zd + %lu\n",
-                       padd->frame_info.error, padd->consumed_total,
-                       consumed, padd->frame_info.bytesconsumed);
-               PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(
-                       padd->frame_info.error));
-               consumed++; /* catch 21 */
-               goto success;
-       }
-       padd->error_count = 0;
-       consumed += padd->frame_info.bytesconsumed;
-       ret = consumed;
-       if (!padd->frame_info.samples)
-               goto out;
-       ret = -E_AAC_OVERRUN;
-       if (padd->frame_info.samples * 2 + fn->loaded > fn->bufsize)
-               goto out;
-       for (i = 0; i < padd->frame_info.samples; i++) {
-               short *s = (short *)outbuffer;
-               write_int16_host_endian(fn->buf + fn->loaded, s[i]);
-               fn->loaded += 2;
-       }
-success:
-       ret = consumed;
-out:
-       if (ret > 0)
-               padd->consumed_total += ret;
-       return ret;
-}
-
-static void aacdec_open(struct filter_node *fn)
-{
-       fn->private_data = para_calloc(sizeof(struct private_aacdec_data));
-       struct private_aacdec_data *padd = fn->private_data;
-
-       fn->bufsize = AAC_OUTBUF_SIZE;
-       fn->buf = para_calloc(fn->bufsize);
-       padd->handle = aac_open();
-}
-
-static void aacdec_close(struct filter_node *fn)
-{
-       struct private_aacdec_data *padd = fn->private_data;
-
-       NeAACDecClose(padd->handle);
-       free(fn->buf);
-       fn->buf = NULL;
-       free(padd);
-       fn->private_data = NULL;
-}
-
-/**
- * the init function of the aacdec filter
- *
- * \param f pointer to the filter struct to initialize
- *
- * \sa filter::init
- */
-void aacdec_init(struct filter *f)
-{
-       f->open = aacdec_open;
-       f->convert = aacdec;
-       f->close = aacdec_close;
-}
diff --git a/aacdec_filter.c b/aacdec_filter.c
new file mode 100644 (file)
index 0000000..8eddc0e
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+/*
+ * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
+ * Ahead Software AG
+ */
+
+/** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
+
+#include "para.h"
+
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+#include "aac.h"
+
+/** the output buffer size */
+#define AAC_OUTBUF_SIZE (32 * 1024)
+
+/** give up decoding after that many errors */
+#define MAX_ERRORS 20
+
+/**
+ * data specific to the aacdec filter
+ *
+ * \sa filter, filter_node
+ */
+struct private_aacdec_data {
+       /** the return value of aac_open */
+       NeAACDecHandle handle;
+       /** info about the currently decoded frame */
+       NeAACDecFrameInfo frame_info;
+       /** whether this instance of the aac decoder is already initialized */
+       int initialized;
+       /**
+        * return value of aac_find_esds(). Used to call the right aacdec
+        * init function
+        */
+       unsigned long decoder_length;
+       /** number of times the decoder returned an error */
+       unsigned error_count;
+       /** number of bytes already consumed from the imput stream */
+       size_t consumed_total;
+       /** return value of aac_find_entry_point */
+       size_t entry;
+};
+
+static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn)
+{
+       struct private_aacdec_data *padd = fn->private_data;
+       struct filter_chain *fc = fn->fc;
+       int i, ret;
+       unsigned char *p, *outbuffer;
+       unsigned char *inbuf = (unsigned char*)input_buffer;
+       size_t skip, consumed = 0;
+
+       if (fn->loaded > fn->bufsize * 3 / 5)
+               return 0;
+       if (len < 2048 && !*fc->input_error)
+               return 0;
+
+       if (!padd->initialized) {
+               unsigned long rate = 0;
+               unsigned char channels = 0;
+               ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
+               if (ret < 0) {
+                       PARA_INFO_LOG("%s\n", para_strerror(-ret));
+                       ret = NeAACDecInit(padd->handle, inbuf,
+                               len, &rate, &channels);
+                       PARA_INFO_LOG("decoder init: %d\n", ret);
+                       if (ret < 0) {
+                               ret = -E_AACDEC_INIT;
+                               goto out;
+                       }
+                       consumed = ret;
+               } else {
+                       PARA_INFO_LOG("decoder len: %lu\n",
+                               padd->decoder_length);
+                       consumed += skip;
+                       p = inbuf + consumed;
+                       ret = -E_AACDEC_INIT;
+                       if (NeAACDecInit2(padd->handle, p,
+                                       padd->decoder_length, &rate,
+                                       &channels) < 0)
+                               goto out;
+               }
+               fc->samplerate = rate;
+               fc->channels = channels;
+               PARA_INFO_LOG("rate: %u, channels: %d\n",
+                       fc->samplerate, fc->channels);
+               padd->initialized = 1;
+       }
+       if (padd->decoder_length > 0) {
+               consumed = 0;
+               if (!padd->entry) {
+                       ret = aac_find_entry_point(inbuf + consumed,
+                               len - consumed, &skip);
+                       if (ret < 0) {
+                               ret = len;
+                               goto out;
+                       }
+                       consumed += skip;
+                       padd->entry = ret;
+                       PARA_INFO_LOG("entry: %zu\n", padd->entry);
+               }
+               ret = len;
+               if (padd->consumed_total + len < padd->entry)
+                       goto out;
+               if (padd->consumed_total < padd->entry)
+                       consumed = padd->entry - padd->consumed_total;
+       }
+       for (; consumed < len; consumed++)
+               if ((inbuf[consumed] & 0xfe) == 0x20)
+                       break;
+       if (consumed >= len)
+               goto success;
+       p = inbuf + consumed;
+       outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p,
+               len - consumed);
+       if (padd->frame_info.error) {
+               ret = -E_AAC_DECODE;
+               if (padd->error_count++ > MAX_ERRORS)
+                       goto out;
+               PARA_ERROR_LOG("frame_error: %d, consumed: %zu + %zd + %lu\n",
+                       padd->frame_info.error, padd->consumed_total,
+                       consumed, padd->frame_info.bytesconsumed);
+               PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(
+                       padd->frame_info.error));
+               consumed++; /* catch 21 */
+               goto success;
+       }
+       padd->error_count = 0;
+       consumed += padd->frame_info.bytesconsumed;
+       ret = consumed;
+       if (!padd->frame_info.samples)
+               goto out;
+       ret = -E_AAC_OVERRUN;
+       if (padd->frame_info.samples * 2 + fn->loaded > fn->bufsize)
+               goto out;
+       for (i = 0; i < padd->frame_info.samples; i++) {
+               short *s = (short *)outbuffer;
+               write_int16_host_endian(fn->buf + fn->loaded, s[i]);
+               fn->loaded += 2;
+       }
+success:
+       ret = consumed;
+out:
+       if (ret > 0)
+               padd->consumed_total += ret;
+       return ret;
+}
+
+static void aacdec_open(struct filter_node *fn)
+{
+       struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+
+       fn->private_data = padd;
+       fn->bufsize = AAC_OUTBUF_SIZE;
+       fn->buf = para_calloc(fn->bufsize);
+       padd->handle = aac_open();
+}
+
+static void aacdec_close(struct filter_node *fn)
+{
+       struct private_aacdec_data *padd = fn->private_data;
+
+       NeAACDecClose(padd->handle);
+       free(fn->buf);
+       fn->buf = NULL;
+       free(padd);
+       fn->private_data = NULL;
+}
+
+/**
+ * the init function of the aacdec filter
+ *
+ * \param f pointer to the filter struct to initialize
+ *
+ * \sa filter::init
+ */
+void aacdec_filter_init(struct filter *f)
+{
+       f->open = aacdec_open;
+       f->convert = aacdec;
+       f->close = aacdec_close;
+}
diff --git a/afh.c b/afh.c
index 454517f40601e8e19421ed993e17b16a57ae900f..24c2bc67dcd81addb013f2f00a87a64494f5933a 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -112,6 +112,8 @@ static int cat_file(void *audio_file_data, struct afh_info *afhi)
                                        return ret;
                        }
                }
+               if (!size)
+                       continue;
                PARA_INFO_LOG("writing chunk %lu\n", i);
                ret = write_all(STDOUT_FILENO, buf, &size);
                if (ret < 0)
@@ -120,9 +122,17 @@ static int cat_file(void *audio_file_data, struct afh_info *afhi)
        return 1;
 }
 
+/**
+ * The main function of para_afh.
+ *
+ * \param argc Usual argument count.
+ * \param argv Usual argument vector.
+ *
+ * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
+ */
 int main(int argc, char **argv)
 {
-       int ret, audio_format_num, fd;
+       int i, ret, audio_format_num, fd;
        void *audio_file_data;
        size_t audio_file_size;
        struct afh_info afhi;
@@ -130,25 +140,36 @@ int main(int argc, char **argv)
        afh_cmdline_parser(argc, argv, &conf);
        HANDLE_VERSION_FLAG("afh", conf);
        ret = -E_AFH_SYNTAX;
-       if (conf.inputs_num != 1)
-               goto out;
-       afh_init();
-       ret = mmap_full_file(conf.inputs[0], O_RDONLY, &audio_file_data,
-               &audio_file_size, &fd);
-       if (ret < 0)
+       if (conf.inputs_num == 0)
                goto out;
-       ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size,
-               fd, &afhi);
-       if (ret < 0)
+       if (conf.stream_given && conf.inputs_num != 1)
                goto out;
-       audio_format_num = ret;
-       if (conf.stream_given)
-               ret = cat_file(audio_file_data, &afhi);
-       else {
-               print_info(audio_format_num, &afhi);
-               if (conf.chunk_table_given)
-                       print_chunk_table(&afhi);
-               ret = 1;
+       afh_init();
+       for (i = 0; i < conf.inputs_num; i++) {
+               int ret2;
+               ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
+                       &audio_file_size, &fd);
+               if (ret < 0)
+                       goto out;
+               ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
+                       fd, &afhi);
+               if (ret < 0)
+                       goto out;
+               audio_format_num = ret;
+               if (conf.stream_given)
+                       ret = cat_file(audio_file_data, &afhi);
+               else {
+                       printf("File %d: %s\n", i + 1, conf.inputs[i]);
+                       print_info(audio_format_num, &afhi);
+                       if (conf.chunk_table_given)
+                               print_chunk_table(&afhi);
+                       printf("\n");
+               }
+               ret2 = para_munmap(audio_file_data, audio_file_size);
+               if (ret2 < 0 && ret >= 0)
+                       ret = ret2;
+               if (ret < 0)
+                       break;
        }
 out:
        if (ret < 0)
index 07e6a78c3aa4bcaf61e8645019c262473dbca023..bec99bf4d29b6fb8b75d66e29a627eb4f5a002a3 100644 (file)
@@ -91,15 +91,14 @@ void afh_init(void)
        }
 }
 
-
 /**
  * Guess the audio format judging from filename.
  *
  * \param name The filename.
  *
- * \return This function returns -1 if it has no idea what kind of audio
- * file this might be. Otherwise the (non-negative) number of the audio format
- * is returned.
+ * \return This function returns \p -E_AUDIO_FORMAT if it has no idea what kind
+ * of audio file this might be. Otherwise the (non-negative) number of the
+ * audio format is returned.
  */
 int guess_audio_format(const char *name)
 {
@@ -119,14 +118,32 @@ int guess_audio_format(const char *name)
                        return i;
                }
        }
-       return -1;
+       return -E_AUDIO_FORMAT;
 }
 
+/**
+ * Pretty-print the given meta-info.
+ *
+ * \param title The title of the audio file.
+ * \param artist The artist.
+ * \param album The name of the album.
+ * \param year Year of release.
+ * \param comment Further comments.
+ *
+ * This function is called by each audio format handler to produce the tag info
+ * status items. Usually, the audio format handlers read this info from the
+ * audio file (id3 tags, vorbis comments, ...).
+ *
+ * It is OK to pass \p NULL pointers for any argument in which case a suitable
+ * string is inserted which indicates that this information is not available.
+ *
+ * \return The status item string. It must be freed by the caller.
+ */
 char *make_taginfo(char *title, char *artist, char *album, char *year,
                char *comment)
 {
        return make_message("%s: %s, by %s\n" /* taginfo1 */
-               "%s: A: %s, Y: %s, C: %s\n", /* taginfo 2*/
+               "%s: A: %s, Y: %s, C: %s\n", /* taginfo*/
                status_item_list[SI_TAGINFO1],
                (title && *title)? title : "(title tag not set)",
                (artist && *artist)? artist : "(artist tag not set)",
@@ -196,7 +213,18 @@ const char *audio_format_name(int i)
        return i >= 0?  afl[i].name : "(none)";
 }
 
-
+/**
+ * Get one chunk of audio data.
+ *
+ * \param chunk_num The number of the chunk to get.
+ * \param afhi Describes the audio file.
+ * \param map The memory mapped audio file.
+ * \param buf Result pointer.
+ * \param len The length of the chunk in bytes.
+ *
+ * Upon return, \a buf will point so memory inside \a map. The returned buffer
+ * must therefore not be freed by the caller.
+ */
 void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                void *map, const char **buf, size_t *len)
 {
diff --git a/afs.c b/afs.c
index 64d3b0f82fb9005b9e526d7ff3265640d3777114..5f47941e13cae9b43c454e5573fb26de6588f4e8 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -892,7 +892,7 @@ static int call_callback(int fd, int query_shmid)
        query.data = (char *)query_shm + sizeof(*cq);
        query.size = cq->query_size;
        cq->handler(fd, &query);
-       return 1;
+       return shm_detach(query_shm);
 }
 
 static int execute_server_command(void)
@@ -1105,7 +1105,7 @@ int com_init(int fd, int argc, char * const * const argv)
                                return -E_BAD_TABLE_NAME;
                }
        }
-       ret = send_callback_request(create_tables_callback, &query, NULL, NULL);
+       ret = send_callback_request(create_tables_callback, &query, &send_result, &fd);
        if (ret < 0)
                return send_va_buffer(fd, "%s\n", para_strerror(-ret));
        return ret;
diff --git a/aft.c b/aft.c
index 1b23823e77dd34bcc7acd11dba6e6eca2d56d7db..d5a355566e387df5bc6e8b1ec06fad03bb660c3e 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -20,6 +20,7 @@
 #include "vss.h"
 #include "fd.h"
 #include "ipc.h"
+#include "portable_io.h"
 
 static struct osl_table *audio_file_table;
 
@@ -588,8 +589,6 @@ int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi)
 static int save_afd(struct audio_file_data *afd)
 {
        size_t size = sizeof(*afd) + sizeof_chunk_table(&afd->afhi);
-
-       PARA_DEBUG_LOG("size: %zu\n", size);
        int shmid, ret = shm_new(size);
        void *shm_afd;
        char *buf;
@@ -1261,8 +1260,8 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        GET_NUM_DIGITS(d->afsi.num_played, &num_digits);
        w->num_played_width = PARA_MAX(w->num_played_width, num_digits);
        /* get the number of chars to print this amount of time */
-       tmp = get_duration_width(d->afhi.seconds_total);
-       w->duration_width = PARA_MAX(w->duration_width, tmp);
+       num_digits = get_duration_width(d->afhi.seconds_total);
+       w->duration_width = PARA_MAX(w->duration_width, num_digits);
        GET_NUM_DIGITS(d->afsi.amp, &num_digits);
        w->amp_width = PARA_MAX(w->amp_width, num_digits);
        if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
@@ -1599,6 +1598,7 @@ static void com_add_callback(int fd, const struct osl_object *query)
        struct afs_info default_afsi = {.last_played = 0};
        struct para_buffer msg = {.max_size = SHMMAX,
                .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+       uint16_t afhi_offset, chunks_offset;
 
        hash = (HASH_TYPE *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);;
@@ -1653,8 +1653,8 @@ static void com_add_callback(int fd, const struct osl_object *query)
                        goto out;
        }
        /* no hs or force mode, child must have sent afhi */
-       uint16_t afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
-       uint16_t chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
+       afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
+       chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
@@ -1816,6 +1816,7 @@ static int add_one_audio_file(const char *path, void *private_data)
                afhi_ptr = &afhi;
        }
        munmap(map.data, map.size);
+       close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
                send_ret = send_va_buffer(pad->fd, "adding %s\n", path);
                if (send_ret < 0)
@@ -2303,10 +2304,6 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
        };
        int ret;
        char *source_path = (char *)query->data + sizeof(cad.flags);
-
-       ret = get_afsi_of_path(source_path, &cad.source_afsi);
-       if (ret < 0)
-               goto out;
        struct pattern_match_data pmd = {
                .table = audio_file_table,
                .loop_col_num = AFTCOL_HASH,
@@ -2317,6 +2314,10 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
                .data = &cad,
                .action = copy_selector_info
        };
+
+       ret = get_afsi_of_path(source_path, &cad.source_afsi);
+       if (ret < 0)
+               goto out;
        ret = for_each_matching_row(&pmd);
 out:
        if (ret < 0)
index 8e8b693c2f99752ea2ce1d9b7481084d6dcb0150..ae471679f076f17fbce5b3d8d27ed5458d1fb4cd 100644 (file)
@@ -21,6 +21,7 @@
 #include "string.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "write.h"
 #include "alsa_write.cmdline.h"
 #include "error.h"
@@ -41,7 +42,7 @@ struct private_alsa_write_data {
        /**
         * the samplerate given by command line option or the decoder
         * of the writer node group
-        */
+        */
        unsigned samplerate;
        /**
         * the number of channels, also given by command line option or the
@@ -133,8 +134,9 @@ static int alsa_open(struct writer_node *w)
                return -E_SW_PARAMS;
        pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT)
                * pad->channels / 8;
+       PARA_INFO_LOG("bytes per frame: %zu\n", pad->bytes_per_frame);
        if (snd_pcm_nonblock(pad->handle, 1))
-               PARA_ERROR_LOG("%s\n", "failed to set nonblock mode");
+               PARA_ERROR_LOG("failed to set nonblock mode\n");
        return period_size * pad->bytes_per_frame;
 }
 
@@ -186,14 +188,16 @@ static int alsa_write_post_select(__a_unused struct sched *s,
                        frames, (int)ret);
                return -E_ALSA_WRITE;
        }
-       if (ret == -EAGAIN) { /* try again in 5ms */
-               PARA_WARNING_LOG("EAGAIN\n");
-               ms2tv(5, &tv);
-       } else {
+       if (ret == -EAGAIN)
+               PARA_DEBUG_LOG("EAGAIN\n");
+       else
                wn->written += ret * pad->bytes_per_frame;
-               ms2tv(pad->buffer_time / 4000, &tv);
+       if (ret == frames) /* we wrote everything, try again immediately */
+               pad->next_chunk = *now;
+       else {
+               ms2tv(pad->buffer_time / pad->bytes_per_frame / 1000, &tv);
+               tv_add(now, &tv, &pad->next_chunk);
        }
-       tv_add(now, &tv, &pad->next_chunk);
        return 1;
 }
 
@@ -209,10 +213,12 @@ static void alsa_close(struct writer_node *wn)
 
 __malloc static void *alsa_parse_config(const char *options)
 {
+       int ret;
        struct alsa_write_args_info *conf
                = para_calloc(sizeof(struct alsa_write_args_info));
+
        PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t"));
-       int ret = alsa_cmdline_parser_string(options, conf, "alsa_write");
+       ret = alsa_cmdline_parser_string(options, conf, "alsa_write");
        if (ret)
                goto err_out;
        PARA_INFO_LOG("help given: %d\n", conf->help_given);
@@ -231,10 +237,17 @@ err_out:
  */
 void alsa_write_init(struct writer *w)
 {
+       struct alsa_write_args_info dummy;
+
+       alsa_cmdline_parser_init(&dummy);
        w->open = alsa_open;
        w->close = alsa_close;
        w->pre_select = alsa_write_pre_select;
        w->post_select = alsa_write_post_select;
        w->parse_config = alsa_parse_config;
        w->shutdown = NULL; /* nothing to do */
+       w->help = (struct ggo_help) {
+               .short_help = alsa_write_args_info_help,
+               .detailed_help = alsa_write_args_info_detailed_help
+       };
 }
index 371ec89f259ebae40581c6031f6e3aba51dd23d3..84f49d58916bcce0ba51e1ab65347f25eb7c58a5 100644 (file)
@@ -1,28 +1,34 @@
-section "alsa options"
-######################
-
 option "device" d
 #~~~~~~~~~~~~~~~~
 "set PCM device"
-       string typestr="device"
-       default="default"
-       optional
+string typestr="device"
+default="default"
+optional
+details="
+       On systems with dmix, a better choice than the default
+       value might be to use \"plug:swmix\".
+"
 
 option "channels" c
 #~~~~~~~~~~~~~~~~~~
-"number of channels (only necessary for raw
-audio)"
-
-       int typestr="num"
-       default="2"
-       optional
+"specify number of channels"
+int typestr="num"
+default="2"
+optional
+details="
+       This option is only necessary for playing raw audio with
+       para_write.  In all other cases (plaing wav files with
+       para_write or using this writer with para_audiod), the number
+       of channels will be obtained from other resources.
+"
 
 option "samplerate" s
 #~~~~~~~~~~~~~~~~~~~~~
-
-"force given sample rate (only necessary for
-raw audio)"
-
-       int typestr="num"
-       default="44100"
-       optional
+"force given sample rate"
+int typestr="num"
+default="44100"
+optional
+details="
+       Again, it is only necessary to specify this when playing raw
+       audio with para_write.
+"
index 392fff6547c12cb7aa6c1abd62260526171be23b..a88719e33544b4a08e3d74862ee54cf13cae3d19 100644 (file)
@@ -4,12 +4,13 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file amp.c Paraslash's amplify filter. */
+/** \file amp_filter.c Paraslash's amplify filter. */
 
 #include "para.h"
 #include "amp_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
@@ -50,20 +51,23 @@ static void amp_close(struct filter_node *fn)
        free(fn->buf);
 }
 
-static void *amp_parse_config(int argc, char **argv)
+static int amp_parse_config(int argc, char **argv, void **config)
 {
-       struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
+       struct amp_filter_args_info *amp_conf = para_calloc(sizeof(*amp_conf));
+       int ret = -E_AMP_SYNTAX;
 
-       if (amp_cmdline_parser(argc, argv, conf))
+       if (amp_cmdline_parser(argc, argv, amp_conf))
                goto err;
-       if (conf->amp_arg < 0)
+       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (amp_conf->amp_arg < 0)
                goto err;
-       PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n", conf->amp_arg,
-               conf->amp_arg / 64.0 + 1.0);
-       return conf;
+       PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n",
+               amp_conf->amp_arg, amp_conf->amp_arg / 64.0 + 1.0);
+       *config = amp_conf;
+       return 1;
 err:
-       free(conf);
-       return NULL;
+       free(amp_conf);
+       return ret;
 }
 
 static void amp_open(struct filter_node *fn)
@@ -87,11 +91,17 @@ static void amp_open(struct filter_node *fn)
  *
  * \param f Pointer to the struct to initialize.
  */
-void amp_init(struct filter *f)
+void amp_filter_init(struct filter *f)
 {
+       struct amp_filter_args_info dummy;
+
+       amp_cmdline_parser_init(&dummy);
        f->open = amp_open;
        f->close = amp_close;
        f->convert = amp_convert;
-       f->print_help = amp_cmdline_parser_print_help;
        f->parse_config = amp_parse_config;
+       f->help = (struct ggo_help) {
+               .short_help = amp_filter_args_info_help,
+               .detailed_help = amp_filter_args_info_detailed_help
+       };
 }
index 1849dc3ab4fcc3e6e2ad8dbeddf93931978cbd8e..865df3ac4048b330daf9055ffb47c2b1a2999eb3 100644 (file)
@@ -1,6 +1,5 @@
-section "The amplify filter"
-
 option "amp" a
+#~~~~~~~~~~~~~
 "amplification value"
 int typestr="number"
 default="32"
@@ -10,12 +9,9 @@ details="
        which the amplitude of the audio stream is multiplied. The
        formula for the scaling factor is
 
-               factor = 1 + amp / 64
+               factor = 1 + amp / 64.
 
-       amp value       scaling factor
-       ~~~~~~~~~       ~~~~~~~~~~~~~~
-       0               1
-       32              1.5
-       64              2
-       128             3
+       For example, an amplifiction value of zero results in a
+       scaling factor of one while an amplification value of 64
+       means to double the volume.
 "
index e7fba9021d29fc19fcc7ad0d09534c65914d3a56..655c286697a6ca68c94da410ec5841276dd97ef5 100644 (file)
@@ -301,9 +301,9 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
-               len = strlen(p);
                struct addatt_event_data aed;
 
+               len = strlen(p);
                if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
                        ret2 = para_printf(&pb, "invalid attribute name: %s\n", p);
                        if (ret2 < 0)
@@ -343,7 +343,7 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
                aed.name = p;
                aed.bitnum = bitnum;
                afs_event(ATTRIBUTE_ADD, &pb, &aed);
-               greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, bitnum);
+               greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
        if (ret < 0 && ret2 >= 0)
index f0ce62587d9c6fc995fe7c99440fa1238e706b49..63d42cacb5218b92a8cea76c8a0626349437b8e6 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -14,6 +14,7 @@
 #include "audiod.cmdline.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "filter.h"
 #include "grab_client.cmdline.h"
@@ -186,7 +187,7 @@ out:
  * \param ll loglevel
  * \param fmt the format string
  */
-void para_log(int ll, const char* fmt,...)
+__printf_2_3 void para_log(int ll, const char* fmt,...)
 {
        va_list argp;
        FILE *outfd;
@@ -213,16 +214,39 @@ void para_log(int ll, const char* fmt,...)
        va_end(argp);
 }
 
-static char *configfile_exists(void)
+static void parse_config_or_die(void)
 {
-       char *home = para_homedir();
-       char *config_file = make_message("%s/.paraslash/audiod.conf",
-               home);
-       free(home);
-       if (file_exists(config_file))
-               return config_file;
+       char *config_file;
+
+       if (conf.config_file_given)
+               config_file = para_strdup(conf.config_file_arg);
+       else {
+               char *home = para_homedir();
+               config_file = make_message("%s/.paraslash/audiod.conf", home);
+               free(home);
+       }
+       if (conf.config_file_given && !file_exists(config_file)) {
+               PARA_EMERG_LOG("can not read config file %s\n", config_file);
+               goto err;
+       }
+       if (config_file) {
+               struct audiod_cmdline_parser_params params = {
+                       .override = 0,
+                       .initialize = 0,
+                       .check_required = 1,
+                       .check_ambiguity = 0,
+                       .print_errors = 1
+               };
+               if (audiod_cmdline_parser_config_file(config_file, &conf, &params)) {
+                       PARA_EMERG_LOG("parse error in config file\n");
+                       goto err;
+               }
+       }
        free(config_file);
-       return NULL;
+       return;
+err:
+       free(config_file);
+       exit(EXIT_FAILURE);
 }
 
 static void setup_signal_handling(void)
@@ -603,7 +627,7 @@ static int init_writers(void)
        struct audio_format_info *a;
 
        init_supported_writers();
-       nw = PARA_MAX(1, conf.writer_given);
+       nw = PARA_MAX(1U, conf.writer_given);
        PARA_INFO_LOG("maximal number of writers: %d\n", nw);
        FOR_EACH_AUDIO_FORMAT(i) {
                a = &afi[i];
@@ -720,7 +744,7 @@ static int init_filters(void)
        int i, ret, nf;
 
        filter_init(filters);
-       nf = PARA_MAX(1 conf.filter_given);
+       nf = PARA_MAX(1U, conf.filter_given);
        PARA_INFO_LOG("maximal number of filters: %d\n", nf);
        FOR_EACH_AUDIO_FORMAT(i) {
                afi[i].filter_conf = para_malloc(nf * sizeof(void *));
@@ -874,11 +898,6 @@ static void close_stat_pipe(void)
        stat_task->offset_seconds = 0;
        audiod_status_dump();
        stat_task->playing = 0;
-       stat_item_values[SI_BASENAME] = make_message(
-               "%s: no connection to para_server\n",
-               status_item_list[SI_BASENAME]);
-       stat_client_write(stat_item_values[SI_BASENAME],
-               SI_BASENAME);
 }
 
 /**
@@ -1035,6 +1054,12 @@ static void status_pre_select(struct sched *s, struct task *t)
                client_open(argc, argv, &st->ct);
                set_stat_task_restart_barrier(5);
        }
+       free(stat_item_values[SI_BASENAME]);
+       stat_item_values[SI_BASENAME] = make_message(
+               "%s: no connection to para_server\n",
+               status_item_list[SI_BASENAME]);
+       stat_client_write(stat_item_values[SI_BASENAME],
+               SI_BASENAME);
        st->last_status_read = *now;
 out:
        start_stop_decoders(s);
@@ -1067,6 +1092,24 @@ static void set_initial_status(void)
                PARA_WARNING_LOG("invalid mode\n");
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       int d = conf.detailed_help_given;
+       const char **p = d? audiod_args_info_detailed_help
+               : audiod_args_info_help;
+
+       printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-"
+               AUDIOD_CMDLINE_PARSER_VERSION);
+       printf_or_die("%s\n\n", audiod_args_info_usage);
+       for (; *p; p++)
+               printf_or_die("%s\n", *p);
+       print_receiver_helps(d);
+       print_filter_helps(d);
+       print_writer_helps(d);
+       exit(0);
+}
+
+
 /**
  * the main function of para_audiod
  *
@@ -1079,7 +1122,6 @@ static void set_initial_status(void)
  * */
 int main(int argc, char *argv[])
 {
-       char *config_file;
        int ret, i;
        static struct sched s;
        struct command_task command_task_struct, *cmd_task = &command_task_struct;
@@ -1095,27 +1137,17 @@ int main(int argc, char *argv[])
        audiod_cmdline_parser_ext(argc, argv, &conf, &params);
        HANDLE_VERSION_FLAG("audiod", conf);
        para_drop_privileges(conf.user_arg, conf.group_arg);
-       config_file = configfile_exists();
-       if (config_file) {
-               params.override = 0;
-               params.initialize = 0;
-               params.check_required = 1;
-               params.check_ambiguity = 0;
-               params.print_errors = 1;
-               if (audiod_cmdline_parser_config_file(config_file, &conf, &params)) {
-                       PARA_EMERG_LOG("parse error in config file\n");
-                       exit(EXIT_FAILURE);
-               }
-               free(config_file);
-       }
+       parse_config_or_die();
        if (conf.logfile_given)
                logfile = open_log(conf.logfile_arg);
-       log_welcome("para_audiod", conf.loglevel_arg);
        i = init_stream_io();
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        if (i < 0) {
                PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i));
                exit(EXIT_FAILURE);
        }
+       log_welcome("para_audiod", conf.loglevel_arg);
        server_uptime(UPTIME_SET);
        set_initial_status();
        FOR_EACH_SLOT(i)
index 41af85e9cba8c614f2f83052dc4eccf4706013f7..8f960c67c1cedae66a6fd9c47873cfc3ca3b1ffc 100644 (file)
@@ -14,6 +14,12 @@ int typestr="level"
 default="4"
 optional
 
+option "config_file" c
+#~~~~~~~~~~~~~~~~~~~~~
+"(default='~/.paraslash/audiod.conf'"
+string typestr="filename"
+optional
+
 option "logfile" L
 #~~~~~~~~~~~~~~~~~
 "where to write log output"
@@ -98,7 +104,7 @@ details="
 
 option "user_allow" -
 #~~~~~~~~~~~~~~~~~~~~
-"allow thus uid"
+"allow this uid"
 int typestr="uid"
 default="-1"
 optional
index fe8143ba928651e1aef5ccdb22d70e4200f0672f..27ef63caea22f9af45d9a951a5df39d1cb078210 100644 (file)
@@ -14,6 +14,7 @@
 #include "list.h"
 #include "close_on_fork.h"
 #include "sched.h"
+#include "ggo.h"
 #include "filter.h"
 #include "grab_client.cmdline.h"
 #include "grab_client.h"
diff --git a/blob.c b/blob.c
index cdad8d7f268330384a897f19d024fe8a6831d7ef..fd8ddc801ccce343914a2845c1b0c0c96c5b7d6c 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -55,7 +55,7 @@ enum blob_ls_flags {
 
 /** Structure passed to the \p print_blob function. */
 struct lsblob_action_data {
-       /* The flags given at the command line. */
+       /** The flags given at the command line. */
        uint32_t flags;
        /** Message buffer. */
        struct para_buffer pb;
index cf115e03c7ef639ace1f1de12c5387e1968d4058..4208579b5e756bbbee9ec03cd10613e13c9a596e 100644 (file)
--- a/client.c
+++ b/client.c
@@ -80,7 +80,7 @@ int main(int argc, char *argv[])
        s.default_timeout.tv_sec = 1;
        s.default_timeout.tv_usec = 0;
        ret = client_open(argc, argv, &ct);
-       if (ret < 0) /* can not use PARA_LOG here */
+       if (ret < 0) /* can not use PARA_LOG here because ct is NULL */
                exit(EXIT_FAILURE);
        register_task(&svt);
        ret = schedule(&s);
index 702358cb977e2fffc1863aea6bdddc5cd7a81304..1d6b410d7b56847b6b2422e8d61d619f59210d11 100644 (file)
@@ -1,4 +1,5 @@
 # file client.conf
+args "--no-handle-error"
 option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional
 option "user" u "paraslash username" string typestr="username" default="<current user>" optional
 option "server_port" p "port to connect" int typestr="port" default="2990" optional
index d8eaba66cc9b7d704f1479b80fab8709bc53f674..9886cfe5f93ef187f0f61fc0642a68b41f0b0619 100644 (file)
@@ -328,7 +328,9 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr)
 
        *ct_ptr = ct;
        ct->fd = -1;
-       ret = client_cmdline_parser(argc, argv, &ct->conf);
+       ret = -E_CLIENT_SYNTAX;
+       if (client_cmdline_parser(argc, argv, &ct->conf))
+               goto out;
        HANDLE_VERSION_FLAG("client", ct->conf);
        ret = -E_CLIENT_SYNTAX;
        if (!ct->conf.inputs_num)
@@ -353,10 +355,13 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr)
                        .override = 0,
                        .initialize = 0,
                        .check_required = 0,
-                       .check_ambiguity = 0
+                       .check_ambiguity = 0,
+                       .print_errors = 0
                };
-               client_cmdline_parser_config_file(ct->config_file,
-                       &ct->conf, &params);
+               ret = -E_BAD_CONFIG;
+               if (client_cmdline_parser_config_file(ct->config_file,
+                       &ct->conf, &params))
+                       goto out;
        }
        ret = 1;
        PARA_INFO_LOG("loglevel: %d\n", ct->conf.loglevel_arg);
@@ -374,4 +379,3 @@ out:
        }
        return ret;
 }
-
diff --git a/compress.c b/compress.c
deleted file mode 100644 (file)
index c30c998..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file compress.c Paraslash's dynamic audio range compressor. */
-
-/*
- * Uses ideas of AudioCompress, (C) 2002-2004  M. Hari Nezumi <magenta@trikuare.cx>
- */
-
-#include "para.h"
-#include "compress_filter.cmdline.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "string.h"
-
-/** The size of the output data buffer. */
-#define COMPRESS_CHUNK_SIZE 40960
-
-extern char *stat_item_values[NUM_STAT_ITEMS];
-
-/** Data specific to the compress filter. */
-struct private_compress_data {
-       /** The current multiplier. */
-       unsigned current_gain;
-       /** Points to the configuration data for this instance of the compress filter. */
-       struct compress_filter_args_info *conf;
-       /** Maximal admissible gain. */
-       unsigned max_gain;
-       /** Number of samples already seen. */
-       unsigned num_samples;
-       /** Absolute value of the maximal sample in the current block. */
-       unsigned peak;
-};
-
-static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
-{
-       size_t i, length = PARA_MIN((inbuf_len / 2) * 2,
-               (fn->bufsize - fn->loaded) / 2 * 2);
-       struct private_compress_data *pcd = fn->private_data;
-       int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded);
-       unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
-               mask = (1 << pcd->conf->blocksize_arg) - 1;
-
-       if (!length)
-               return 0;
-       for (i = 0; i < length / 2; i++) {
-               /* be careful in that heat, my dear */
-               int sample = *ip++, adjusted_sample = (PARA_ABS(sample) *
-                       pcd->current_gain) >> gain_shift;
-               if (unlikely(adjusted_sample > 32767)) { /* clip */
-                       PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
-                               sample, adjusted_sample);
-                       adjusted_sample = 32767;
-                       pcd->current_gain = (3 * pcd->current_gain +
-                               (1 << pcd->conf->inertia_arg)) / 4;
-                       pcd->peak = 0;
-               } else
-                       pcd->peak = PARA_MAX(pcd->peak, adjusted_sample);
-               *op++ = sample >= 0? adjusted_sample : -adjusted_sample;
-               if (likely(++pcd->num_samples & mask))
-                       continue;
-//             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
-//                     pcd->peak);
-               if (pcd->peak < pcd->conf->target_level_arg) {
-                       if (pcd->current_gain < pcd->max_gain)
-                               pcd->current_gain++;
-               } else
-                       pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
-                               1 << pcd->conf->inertia_arg);
-               pcd->peak = 0;
-       }
-       fn->loaded += length;
-       return length;
-}
-
-static void close_compress(struct filter_node *fn)
-{
-       free(fn->private_data);
-       free(fn->buf);
-}
-
-static void *compress_parse_config(int argc, char **argv)
-{
-       struct compress_filter_args_info *ret = para_calloc(sizeof(struct compress_filter_args_info));
-       if (!compress_cmdline_parser(argc, argv, ret))
-               return ret;
-       free(ret);
-       return NULL;
-}
-
-static void open_compress(struct filter_node *fn)
-{
-       struct private_compress_data *pcd = para_calloc(
-               sizeof(struct private_compress_data));
-       pcd->conf = fn->conf;
-       fn->private_data = pcd;
-       fn->bufsize = COMPRESS_CHUNK_SIZE;
-       fn->buf = para_malloc(fn->bufsize);
-       pcd->current_gain = 1 << pcd->conf->inertia_arg;
-       pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
-}
-
-/**
- * The init function of the compress filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void compress_init(struct filter *f)
-{
-       f->open = open_compress;
-       f->close = close_compress;
-       f->convert = compress;
-       f->print_help = compress_cmdline_parser_print_help;
-       f->parse_config = compress_parse_config;
-}
diff --git a/compress_filter.c b/compress_filter.c
new file mode 100644 (file)
index 0000000..c7af325
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file compress_filter.c Paraslash's dynamic audio range compressor. */
+
+/*
+ * Uses ideas of AudioCompress, (C) 2002-2004  M. Hari Nezumi <magenta@trikuare.cx>
+ */
+
+#include "para.h"
+#include "compress_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "string.h"
+#include "error.h"
+
+/** The size of the output data buffer. */
+#define COMPRESS_CHUNK_SIZE 40960
+
+extern char *stat_item_values[NUM_STAT_ITEMS];
+
+/** Data specific to the compress filter. */
+struct private_compress_data {
+       /** The current multiplier. */
+       unsigned current_gain;
+       /** Points to the configuration data for this instance of the compress filter. */
+       struct compress_filter_args_info *conf;
+       /** Maximal admissible gain. */
+       unsigned max_gain;
+       /** Number of samples already seen. */
+       unsigned num_samples;
+       /** Absolute value of the maximal sample in the current block. */
+       int peak;
+};
+
+static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
+{
+       size_t i, length = PARA_MIN((inbuf_len / 2) * 2,
+               (fn->bufsize - fn->loaded) / 2 * 2);
+       struct private_compress_data *pcd = fn->private_data;
+       int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded);
+       unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
+               mask = (1 << pcd->conf->blocksize_arg) - 1;
+
+       if (!length)
+               return 0;
+       for (i = 0; i < length / 2; i++) {
+               /* be careful in that heat, my dear */
+               int sample = *ip++, adjusted_sample = (PARA_ABS(sample) *
+                       pcd->current_gain) >> gain_shift;
+               if (unlikely(adjusted_sample > 32767)) { /* clip */
+                       PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
+                               sample, adjusted_sample);
+                       adjusted_sample = 32767;
+                       pcd->current_gain = (3 * pcd->current_gain +
+                               (1 << pcd->conf->inertia_arg)) / 4;
+                       pcd->peak = 0;
+               } else
+                       pcd->peak = PARA_MAX(pcd->peak, adjusted_sample);
+               *op++ = sample >= 0? adjusted_sample : -adjusted_sample;
+               if (likely(++pcd->num_samples & mask))
+                       continue;
+//             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
+//                     pcd->peak);
+               if (pcd->peak < pcd->conf->target_level_arg) {
+                       if (pcd->current_gain < pcd->max_gain)
+                               pcd->current_gain++;
+               } else
+                       pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
+                               1U << pcd->conf->inertia_arg);
+               pcd->peak = 0;
+       }
+       fn->loaded += length;
+       return length;
+}
+
+static void close_compress(struct filter_node *fn)
+{
+       free(fn->private_data);
+       free(fn->buf);
+}
+
+/** TODO: Add sanity checks */
+static int compress_parse_config(int argc, char **argv, void **config)
+{
+       int ret;
+       struct compress_filter_args_info *compress_conf
+               = para_calloc(sizeof(*compress_conf));
+
+       ret = -E_COMPRESS_SYNTAX;
+       if (compress_cmdline_parser(argc, argv, compress_conf))
+               goto err;
+       *config = compress_conf;
+       return 1;
+err:
+       free(compress_conf);
+       return  ret;
+}
+
+static void open_compress(struct filter_node *fn)
+{
+       struct private_compress_data *pcd = para_calloc(
+               sizeof(struct private_compress_data));
+       pcd->conf = fn->conf;
+       fn->private_data = pcd;
+       fn->bufsize = COMPRESS_CHUNK_SIZE;
+       fn->buf = para_malloc(fn->bufsize);
+       pcd->current_gain = 1 << pcd->conf->inertia_arg;
+       pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
+}
+
+/**
+ * The init function of the compress filter.
+ *
+ * \param f Pointer to the struct to initialize.
+ */
+void compress_filter_init(struct filter *f)
+{
+       struct compress_filter_args_info dummy;
+
+       compress_cmdline_parser_init(&dummy);
+       f->open = open_compress;
+       f->close = close_compress;
+       f->convert = compress;
+       f->parse_config = compress_parse_config;
+       f->help = (struct ggo_help) {
+               .short_help = compress_filter_args_info_help,
+               .detailed_help = compress_filter_args_info_detailed_help
+       };
+}
index 3f4f8e2ea8ccabc745bc1770809b96e43063b728..74dcbc0332753e1011962a62c9dbe961881b4d1f 100644 (file)
@@ -1,7 +1,37 @@
-section "The dynamic audio range compressor"
+option "blocksize" b
+#~~~~~~~~~~~~~~~~~~~
+"adjust block size"
+int typestr="number"
+default="15"
+optional
+details = "
+       Larger blocksize means fewer volume adjustments per time unit.
+"
 
-option "blocksize" b "larger blocksize means fewer volume adjustments per time unit" int typestr="number" default="15" optional
-option "aggressiveness" a "controls the maximum amount to amplify by" int typestr="number" default="4" optional
-option "inertia" i "how much inertia ramping has" int typestr="number" default="6" optional
-option "target_level" t "target signal level (0-32768)" int typestr="number" default="20000" optional
-option "damp" d "if non-zero, scale down after normalizing" int typestr="number" default="0" optional
+option "aggressiveness" a
+#~~~~~~~~~~~~~~~~~~~~~~~~
+ "controls the maximum amount to amplify by"
+int typestr="number"
+default="4"
+optional
+
+option "inertia" i
+#~~~~~~~~~~~~~~~~~
+ "how much inertia ramping has"
+ int typestr="number"
+default="6"
+optional
+
+option "target_level" t
+#~~~~~~~~~~~~~~~~~~~~~~
+"target signal level (0-32768)"
+int typestr="number"
+default="20000"
+optional
+
+option "damp" d
+#~~~~~~~~~~~~~~
+"if non-zero, scale down after normalizing"
+int typestr="number"
+default="0"
+optional
index 63cbdc973f7e17d809351c534cf0b9a562766092..664280ac9a4835e8e5fe1209584fb25217e2554a 100644 (file)
@@ -81,21 +81,22 @@ AC_CHECK_FUNCS([atexit dup2 memchr memmove memset \
 all_errlist_objs="server mp3_afh afh_common vss command net string signal time
 daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter
 dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer
-playlist sha1 rbtree sched audiod grab_client filter_chain wav compress
+playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter
 http_recv dccp_recv recv_common write_common file_write audiod_command
-client_common recv stdout filter stdin audioc write client fsck exec send_common"
-all_executables="server recv filter audioc write client fsck afh amp_filter"
+client_common recv stdout filter stdin audioc write client fsck exec send_common ggo"
+
+all_executables="server recv filter audioc write client fsck afh"
 
 recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline"
 recv_errlist_objs="http_recv recv_common recv time string net dccp_recv
-       fd sched stdout"
+       fd sched stdout ggo"
 recv_ldflags=""
 
 receivers=" http dccp"
 senders=" http dccp"
 
 filter_cmdline_objs="filter.cmdline compress_filter.cmdline amp_filter.cmdline"
-filter_errlist_objs="filter_chain wav compress filter string stdin stdout sched fd amp_filter"
+filter_errlist_objs="filter_common wav_filter compress_filter filter string stdin stdout sched fd amp_filter ggo"
 filter_ldflags=""
 filters=" compress wav amp"
 
@@ -107,9 +108,9 @@ audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline
        http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline
        audiod_command_list amp_filter.cmdline"
 audiod_errlist_objs="audiod signal string daemon stat net
-       time grab_client filter_chain wav compress amp_filter http_recv dccp_recv
+       time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
        recv_common fd sched write_common file_write audiod_command crypt
-       client_common"
+       client_common ggo"
 audiod_ldflags=""
 audiod_audio_formats=""
 
@@ -126,7 +127,7 @@ server_ldflags=""
 server_audio_formats=" mp3"
 
 write_cmdline_objs="write.cmdline file_write.cmdline"
-write_errlist_objs="write write_common file_write time fd string sched stdin"
+write_errlist_objs="write write_common file_write time fd string sched stdin ggo"
 write_ldflags=""
 writers=" file"
 default_writer="FILE_WRITE"
@@ -315,7 +316,7 @@ AC_CHECK_LIB([ogg], [ogg_stream_init], [], [ have_ogg="no" ])
 AC_CHECK_LIB([vorbis], [vorbis_info_init], [], [ have_ogg="no" ])
 AC_CHECK_HEADERS([ogg/ogg.h vorbis/codec.h], [], [ have_ogg="no" ])
 if test "$have_ogg" = "yes"; then
-       all_errlist_objs="$all_errlist_objs oggdec ogg_afh"
+       all_errlist_objs="$all_errlist_objs oggdec_filter ogg_afh"
        AC_DEFINE(HAVE_OGGVORBIS, 1, define to 1 to turn on ogg vorbis support)
        filters="$filters oggdec"
        if test "$OSTYPE" = "Darwin"; then
@@ -330,13 +331,12 @@ if test "$have_ogg" = "yes"; then
        audiod_cmdline_objs="$audiod_cmdline_objs oggdec_filter.cmdline"
 
        server_errlist_objs="$server_errlist_objs ogg_afh"
-       filter_errlist_objs="$filter_errlist_objs oggdec"
-       audiod_errlist_objs="$audiod_errlist_objs oggdec"
+       filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+       audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
        afh_errlist_objs="$afh_errlist_objs ogg_afh"
 
        audiod_audio_formats="ogg"
        server_audio_formats="$server_audio_formats ogg"
-       filter_filters="$filter_filters oggdec"
        AC_SUBST(oggvorbis_cppflags)
        AC_SUBST(oggvorbis_libs)
 else
@@ -366,11 +366,10 @@ AC_CHECK_HEADER(neaacdec.h, [], have_faad=no)
 AC_CHECK_LIB([faad], [NeAACDecOpen], [], have_faad=no)
 if test "$have_faad" = "yes"; then
        AC_DEFINE(HAVE_FAAD, 1, define to 1 if you want to build the aacdec filter)
-       all_errlist_objs="$all_errlist_objs aac_common aacdec aac_afh"
-       filter_errlist_objs="$filter_errlist_objs aacdec aac_common"
+       all_errlist_objs="$all_errlist_objs aac_common aacdec_filter aac_afh"
+       filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
        afh_errlist_objs="$afh_errlist_objs aac_common aac_afh"
-       filter_filters="$filter_filters aacdec"
-       audiod_errlist_objs="$audiod_errlist_objs aacdec aac_common"
+       audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
        server_errlist_objs="$server_errlist_objs aac_afh aac_common"
        server_ldflags="$server_ldflags $faad_libs -lfaad"
        filter_ldflags="$filter_ldflags $faad_libs -lfaad"
@@ -413,9 +412,11 @@ AC_CHECK_LIB([mad], [mad_stream_init], [], [
 ])
 if test "$have_mad" = "yes"; then
        AC_DEFINE(HAVE_MAD, 1, define to 1 if you want to build the mp3dec filter)
-       all_errlist_objs="$all_errlist_objs mp3dec"
-       filter_errlist_objs="$filter_errlist_objs mp3dec"
-       audiod_errlist_objs="$audiod_errlist_objs mp3dec"
+       filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter.cmdline"
+       audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter.cmdline"
+       all_errlist_objs="$all_errlist_objs mp3dec_filter"
+       filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
+       audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
        filter_ldflags="$filter_ldflags $mad_libs -lmad"
        audiod_ldflags="$audiod_ldflags $mad_libs -lmad"
        audiod_audio_formats="$audiod_audio_formats mp3"
@@ -706,6 +707,15 @@ AC_SUBST(fade_objs, add_dot_o($fade_objs))
 AC_DEFINE_UNQUOTED(INIT_FADE_ERRLISTS,
        objlist_to_errlist($fade_errlist_objs), errors used by para_fade)
 
+
+enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
+       enum of supported filters)
+inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
+AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
+array="$(for i in $filters; do printf '{.name = \"'$i'\", .init = '$i'_filter_init},'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
+
 enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
 AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
        enum of supported writers)
@@ -716,6 +726,7 @@ inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer
 AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
 array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
 AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
+
 enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
 AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMATS_ENUM, $enum NUM_AUDIO_FORMATS,
        enum of audio formats supported by audiod)
index d0bc53542137ac06cce22b75fcbe17d649041b9b..c73088f83939e153d94e3eb4dcead35d04a1ff05 100644 (file)
@@ -18,6 +18,7 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "string.h"
 #include "net.h"
@@ -143,10 +144,17 @@ static void dccp_recv_post_select(struct sched *s, struct task *t)
  */
 void dccp_recv_init(struct receiver *r)
 {
+       struct dccp_recv_args_info dummy;
+
+       dccp_recv_cmdline_parser_init(&dummy);
        r->shutdown = dccp_shutdown;
        r->open = dccp_recv_open;
        r->close = dccp_recv_close;
        r->pre_select = dccp_recv_pre_select;
        r->post_select = dccp_recv_post_select;
        r->parse_config = dccp_recv_parse_config;
+       r->help = (struct ggo_help) {
+               .short_help = dccp_recv_args_info_help,
+               .detailed_help = dccp_recv_args_info_detailed_help
+       };
 }
index 52cfef6d7bbce0491676407d42b91a9794c2efaf..9717329d29b036baf460fb747901ea41b913e632 100644 (file)
@@ -1,3 +1,13 @@
-section "options of the dccp receiver"
-option "host" i "ip or host" string default="localhost" optional
-option "port" p "port to connect to" int default="8000" optional
+option "host" i
+"ip or host"
+string default="localhost"
+optional
+details="
+       Both IPv4 and IPv6 addresses are supported.
+"
+
+option "port" p
+"port to connect to"
+int
+default="8000"
+optional
diff --git a/error.h b/error.h
index 4674ec04de88f335e24cde3732af14f154216cd8..37ff48a9463780220cd7e02b431d622080a432a4 100644 (file)
--- a/error.h
+++ b/error.h
@@ -13,8 +13,7 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 
 /* these do not need error handling (yet) */
 #define SERVER_ERRORS
-#define WAV_ERRORS
-#define COMPRESS_ERRORS
+#define WAV_FILTER_ERRORS
 #define TIME_ERRORS
 #define CLOSE_ON_FORK_ERRORS
 #define DAEMON_ERRORS
@@ -25,15 +24,27 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define SHA1_ERRORS
 #define RBTREE_ERRORS
 #define RECV_ERRORS
-#define SEND_COMMON_ERRORS
 #define STDOUT_ERRORS
 #define IPC_ERRORS
-#define AMP_FILTER_ERRORS
-
+#define DCCP_SEND_ERRORS
+#define HTTP_SEND_ERRORS
+#define GGO_ERRORS
 
 
 extern const char **para_errlist[];
 
+#define COMPRESS_FILTER_ERRORS \
+       PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \
+
+
+#define AMP_FILTER_ERRORS \
+       PARA_ERROR(AMP_SYNTAX, "syntax error in amp filter config"), \
+
+
+#define SEND_COMMON_ERRORS \
+       PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
+
+
 #define FADE_ERRORS \
        PARA_ERROR(FADE_SYNTAX, "fade syntax error"), \
 
@@ -113,6 +124,7 @@ extern const char **para_errlist[];
        PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
        PARA_ERROR(NO_MOOD, "no mood available"), \
        PARA_ERROR(NOT_ADMISSIBLE, "file is not admissible"), \
+       PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
 
 
 #define ATTRIBUTE_ERRORS \
@@ -123,7 +135,6 @@ extern const char **para_errlist[];
 
 #define BLOB_ERRORS \
        PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
-       PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
 
 
 #define PLAYLIST_ERRORS \
@@ -169,6 +180,7 @@ extern const char **para_errlist[];
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(INVALID_CHALLENGE, "did not receive valid challenge"), \
        PARA_ERROR(NO_CONFIG, "config file not found"), \
+       PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
        PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
        PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
 
@@ -229,7 +241,7 @@ extern const char **para_errlist[];
        PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
 
 
-#define FILTER_CHAIN_ERRORS \
+#define FILTER_COMMON_ERRORS \
        PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
        PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
        PARA_ERROR(FC_EOF, "filter chain: eof"), \
@@ -240,13 +252,14 @@ extern const char **para_errlist[];
        PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
 
 
-#define OGGDEC_ERRORS \
+#define OGGDEC_FILTER_ERRORS \
        PARA_ERROR(OGGDEC_READ, "read from media returned an error"), \
        PARA_ERROR(OGGDEC_NOTVORBIS, "bitstream is not vorbis data"), \
        PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
        PARA_ERROR(OGGDEC_BADHEADER, "invalid vorbis bitstream header"), \
        PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \
        PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \
+       PARA_ERROR(OGGDEC_SYNTAX, "syntax error in oggdec config"), \
 
 
 #define GRAB_CLIENT_ERRORS \
@@ -259,9 +272,10 @@ extern const char **para_errlist[];
        PARA_ERROR(GC_VERSION_GIVEN, ""), /* not really an error */ \
 
 
-#define MP3DEC_ERRORS \
+#define MP3DEC_FILTER_ERRORS \
        PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
        PARA_ERROR(MP3DEC_OVERRUN, "mp3 output buffer overrun"), \
+       PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
 
 
 #define FILTER_ERRORS \
@@ -316,7 +330,6 @@ extern const char **para_errlist[];
 
 
 #define VSS_ERRORS \
-       PARA_ERROR(CHUNK, "unable to get chunk"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
 
 
@@ -329,10 +342,6 @@ extern const char **para_errlist[];
        PARA_ERROR(CHALLENGE, "failed to read challenge"), \
 
 
-#define HTTP_SEND_ERRORS \
-       PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
-
-
 #define COMMAND_ERRORS \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
        PARA_ERROR(AUTH, "did not receive auth request"), \
@@ -349,9 +358,6 @@ extern const char **para_errlist[];
        PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
 
 
-#define DCCP_SEND_ERRORS \
-
-
 #define FD_ERRORS \
        PARA_ERROR(FGETS, "fgets error"), \
        PARA_ERROR(EMPTY, "file empty"), \
@@ -394,7 +400,7 @@ extern const char **para_errlist[];
        PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \
 
 
-#define AACDEC_ERRORS \
+#define AACDEC_FILTER_ERRORS \
        PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
        PARA_ERROR(AAC_DECODE, "aac decode error"), \
        PARA_ERROR(AAC_OVERRUN, "aac output buffer overrun"), \
diff --git a/exec.c b/exec.c
index 86362c381fd8b3fe236caeddd4facec88130c901..bafae240260ef386a1adfc77937fad458a27e40b 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -37,7 +37,7 @@ static int para_exec(pid_t *pid, const char *file, char *const *const args, int
        if (fds[2] > 0 && pipe(err) < 0)
                goto err_out;
        if (!fds[0] || !fds[1] || !fds[2]) {
-               ret = para_open("/dev/null", O_RDONLY, 42);
+               ret = para_open("/dev/null", O_RDWR, 42);
                if (ret < 0)
                        goto err_out;
                null = ret;
@@ -139,8 +139,6 @@ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds)
        char **argv;
        char *tmp = para_strdup(cmdline);
 
-       if (!tmp)
-               exit(EXIT_FAILURE);
        argc = split_args(tmp, &argv, " \t");
        ret = para_exec(pid, argv[0], argv, fds);
        free(argv);
diff --git a/fade.c b/fade.c
index 48faed83124baeea273337ae950b0e88aeb8fc03..33f1ccbeda51ae52cc1dd6a627b545aee84b0923 100644 (file)
--- a/fade.c
+++ b/fade.c
@@ -28,7 +28,7 @@
 INIT_FADE_ERRLISTS;
 struct fade_args_info conf;
 
-void para_log(__a_unused int ll, const char *fmt,...)
+__printf_2_3 void para_log(__a_unused int ll, const char *fmt, ...)
 {
        va_list argp;
        time_t t1;
index 1ab71a5319de36eb893e4c853e1f38384eb3b3d7..5965cba96f6a5363da66244b1b97890aee0d7dec 100644 (file)
--- a/fade.ggo
+++ b/fade.ggo
@@ -74,7 +74,7 @@ option "wake_hour" H
 option "wake_min" M
 #~~~~~~~~~~~~~~~~~~
 "(0-59)"
-       int typestr="seconds"
+       int typestr="minutes"
        default="0"
        optional
 
diff --git a/fd.c b/fd.c
index 8fc1abad19057bd724f26a25dca2ddb3ba5003ee..c675aaffd4b99083d3ab0e26445574c2b87ad51b 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -285,9 +285,10 @@ int para_opendir(const char *dirname, DIR **dir, int *cwd)
        if (*dir)
                return 1;
        ret = -ERRNO_TO_PARA_ERROR(errno);
-/* Ignore return value of fchdir() and close(). We're busted anyway. */
-       if (cwd)
-               fchdir(*cwd);
+       /* Ignore return value of fchdir() and close(). We're busted anyway. */
+       if (cwd) {
+               int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */
+       }
 close_cwd:
        if (cwd)
                close(*cwd);
index d7cea2f9efd8aa1d0922b4a9442d2126462dd1e8..533d33313136133f675a80ca7db98a2aba6f8346 100644 (file)
@@ -12,6 +12,7 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "write.h"
 #include "string.h"
 #include "fd.h"
@@ -95,10 +96,10 @@ static void file_write_close(struct writer_node *wn)
 
 __malloc static void *file_write_parse_config(const char *options)
 {
-       PARA_INFO_LOG("options: %s\n", options);
        struct file_write_args_info *conf
                = para_calloc(sizeof(struct file_write_args_info));
        int ret = file_cmdline_parser_string(options, conf, "file_write");
+
        PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given);
        if (!ret)
                return conf;
@@ -109,10 +110,17 @@ __malloc static void *file_write_parse_config(const char *options)
 /** the init function of the file writer */
 void file_write_init(struct writer *w)
 {
+       struct file_write_args_info dummy;
+
+       file_cmdline_parser_init(&dummy);
        w->open = file_write_open;
        w->pre_select = file_write_pre_select;
        w->post_select = file_write_post_select;
        w->parse_config = file_write_parse_config;
        w->close = file_write_close;
        w->shutdown = NULL; /* nothing to do */
+       w->help = (struct ggo_help) {
+               .short_help = file_write_args_info_help,
+               .detailed_help = file_write_args_info_detailed_help
+       };
 }
index a172f75779aa201bf6b82f9808443b9c41275b1e..56c44f7a7a6096b0e7e338f9571168bb5f1c56d4 100644 (file)
@@ -1,11 +1,9 @@
-section "file writer options"
-
 option "filename" f
 #~~~~~~~~~~~~~~~~~~
-
-"select output file name. Defaults to a
-random filename in ~/.paraslash."
-
-       string typestr="filename"
-       optional
+"specify output file name"
+string typestr="filename"
+optional
+details="
+       Defaults to a random filename in ~/.paraslash.
+"
 
index 09b644151a3cf4b8f081bf42743224b28a6829dd..d67aeae44a8d226866c6fcf1a46c192ea4a15cc7 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -11,6 +11,7 @@
 #include "filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "filter.h"
 #include "string.h"
 #include "stdin.h"
@@ -94,15 +95,31 @@ err:
        return ret;
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       int d = conf.detailed_help_given;
+       const char **p = d? filter_args_info_detailed_help
+               : filter_args_info_help;
+
+       printf_or_die("%s\n\n", FILTER_CMDLINE_PARSER_PACKAGE "-"
+               FILTER_CMDLINE_PARSER_VERSION);
+       printf_or_die("%s\n\n", filter_args_info_usage);
+       for (; *p; p++)
+               printf_or_die("%s\n", *p);
+       print_filter_helps(d);
+       exit(0);
+}
+
 static int parse_config(int argc, char *argv[])
 {
        static char *cf; /* config file */
        struct stat statbuf;
-       int i;
 
        if (filter_cmdline_parser(argc, argv, &conf))
                return -E_FILTER_SYNTAX;
        HANDLE_VERSION_FLAG("filter", conf);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        if (!cf) {
                char *home = para_homedir();
                cf = make_message("%s/.paraslash/filter.conf", home);
@@ -118,15 +135,7 @@ static int parse_config(int argc, char *argv[])
                if (filter_cmdline_parser_config_file(cf, &conf, &params))
                        return -E_FILTER_SYNTAX;
        }
-       if (!conf.list_filters_given)
-               return 1;
-       printf("available filters: ");
-       for (i = 0; filters[i].name; i++)
-               printf("%s%s%s", i? " " : "", filters[i].name,
-                       filters[i].parse_config? "*": "");
-       printf("\nFilters marked with \"*\" have further command line options. Try\n"
-               "\tpara_filter -f '<filtername> -h'\nfor more information.\n");
-       exit(EXIT_SUCCESS);
+       return 1;
 }
 
 /**
@@ -156,6 +165,7 @@ int main(int argc, char *argv[])
        ret = init_filter_chain();
        if (ret < 0)
                goto out;
+       sit->output_error = &fc->task.error;
 
        stdout_set_defaults(sot);
        sot->buf = fc->outbuf;
index 111e87c698bcfb33441f47d971cb94e02b5ef5fd..5376a6f8c73996c8e85444cfb781f1b755b03911 100644 (file)
@@ -1,17 +1,24 @@
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "filter" f "Specify filter.
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+ "set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
 
-May be given multiple times to 'pipe' the stream
-through arbitrary many filters in an efficient
-way.  The same filter may appear more than once,
-order matters.
+option "filter" f
+#~~~~~~~~~~~~~~~~
+"Specify filter."
+string typestr="filter_spec"
+optional
+multiple
+details="
+       May be given multiple times to 'pipe' the stream through
+       arbitrary many filters in an efficient way.  The same filter
+       may appear more than once, order matters.
 
-Filter options may be specified for each '-f'
-option separately. Note that you will have to
-quote these options like this:
+       Filter options may be specified for each '-f' option
+       separately. Note that you will have to quote these options
+       like this:
 
-       -f 'compress --inertia 5 --damp 2'
+               -f 'compress --inertia 5 --damp 2'
 "
-string typestr="filter_spec" optional multiple
-
-option "list_filters" L "print list of available filters and exit" flag off
index 500a85379bd42b0dabdea4383d875a4f2180b640..c2a6310b5fa12903f7e15ca5453dd312bb3e02c8 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -4,8 +4,10 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file filter.h Filter-related structures and exported symbols from filter_chain.c. */
+/** \file filter.h Filter-related structures and exported symbols from filter_common.c. */
 
+/** The list of supported filters. */
+enum filter_enum {FILTER_ENUM};
 
 /**
  * Describes one running instance of a filter.
@@ -34,6 +36,7 @@ struct filter_node {
 
 /** Describes one running instance of a chain of filters */
 struct filter_chain {
+       /** The length of the filter chain. */
        unsigned int num_filters;
        /**
         * The number of channels of the current stream.
@@ -140,7 +143,7 @@ struct filter_callback {
  * Note: As several instances of the same filter may be running at the same
  * time, all these filter functions must be reentrant; no static non-constant
  * variables may be used.
- * \sa mp3dec.c, oggdec.c, wav.c, compress.c, filter_node
+ * \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node
  */
 struct filter {
        /** The name of the filter. */
@@ -180,31 +183,28 @@ struct filter {
         * by the open() function.
         */
        void (*close)(struct filter_node *fn);
-       /**
-        * Print the help text for this filter and exit.
-        *
-        * This is optional and it is not necessary to initialize this pointer if
-        * the filter does not have a help text.
-        */
-       void (*print_help)(void);
        /**
         * A pointer to the filter's command line parser.
         *
-        * If this optional function pointer is not NULL, any filter options are passed
-        * from the main program to this command line parser once at application
-        * startup. The command line parser should check its command line options given
-        * by \a argc and \a argv and abort on errors. On success, it should return a
-        * pointer to the filter-specific configuration data determined by \a argc and
-        * \a argv.
+        * If this optional function pointer is not NULL, any filter options
+        * are passed from the main program to this command line parser once at
+        * application startup. The command line parser should check its
+        * command line options given by \a argc and \a argv and abort on
+        * errors. Success must be indicated by a non-negative return value. In
+        * this case the function should return a pointer to the
+        * filter-specific configuration data determined by \a argc and \a
+        * argv. On failure, a negative paraslash error code must be returned.
         */
-       void *(*parse_config)(int argc, char **argv);
+       int (*parse_config)(int argc, char **argv, void **config);
+
+       struct ggo_help help;
 };
 
 void close_filters(struct filter_chain *fc);
 void filter_init(struct filter *all_filters);
 int check_filter_arg(char *filter_arg, void **conf);
 void filter_pre_select(__a_unused struct sched *s, struct task *t);
-
+void print_filter_helps(int detailed);
 
 static inline void write_int16_host_endian(char *buf, int val)
 {
@@ -217,55 +217,9 @@ static inline void write_int16_host_endian(char *buf, int val)
 #endif
 }
 
+DECLARE_FILTER_INITS
 
-/** \cond */
-extern struct filter filters[];
-#define DECLARE_EXTERN_FILTER_INIT(name) \
-       extern void name ## _init(struct filter *f)
-
-#define FILTER_INIT(filter) { \
-       .name = #filter, \
-       .init = filter ## _init, \
-       .parse_config = NULL, \
-       .print_help = NULL \
-},
-
-/* filters that are always present */
-DECLARE_EXTERN_FILTER_INIT(wav);
-DECLARE_EXTERN_FILTER_INIT(compress);
-DECLARE_EXTERN_FILTER_INIT(amp);
-
-/* next the optional filters */
-#ifdef HAVE_MAD
-DECLARE_EXTERN_FILTER_INIT(mp3dec);
-#define MP3DEC_FILTER FILTER_INIT(mp3dec)
-#else
-#define MP3DEC_FILTER
-#endif
-
-#ifdef HAVE_FAAD
-DECLARE_EXTERN_FILTER_INIT(aacdec);
-#define AACDEC_FILTER FILTER_INIT(aacdec)
-#else
-#define AACDEC_FILTER
-#endif
-
-#ifdef HAVE_OGGVORBIS
-DECLARE_EXTERN_FILTER_INIT(oggdec);
-#define OGGDEC_FILTER FILTER_INIT(oggdec)
-#else
-#define OGGDEC_FILTER
-#endif
-/** \endcond */
-
-/** define an array of all available filters */
-#define DEFINE_FILTER_ARRAY(filters) struct filter filters[] = { \
-       FILTER_INIT(wav) \
-       FILTER_INIT(compress) \
-       FILTER_INIT(amp) \
-       MP3DEC_FILTER \
-       AACDEC_FILTER \
-       OGGDEC_FILTER \
-       { .name = NULL } };
-
+#define FOR_EACH_SUPPORTED_FILTER(j)  for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
 
+/** The filter array, one structure for each supported filter. */
+extern struct filter filters[NUM_SUPPORTED_FILTERS];
diff --git a/filter_chain.c b/filter_chain.c
deleted file mode 100644 (file)
index 1a29024..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file filter_chain.c Common helper functions for filter input/output. */
-
-#include <sys/types.h>
-#include <dirent.h>
-
-#include "para.h"
-#include "list.h"
-#include "sched.h"
-#include "fd.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-
-DEFINE_FILTER_ARRAY(filters);
-
-/**
- * Call the init function of each supported filter.
- *
- * \param all_filters the array of all supported filters.
- *
- * \sa filter::init
- */
-void filter_init(struct filter *all_filters)
-{
-       struct filter *f;
-
-       for (f = all_filters; f->name; f++)
-               f->init(f);
-}
-
-/**
- * Close and destroy a filter callback.
- *
- * \param fcb The filter callback to close.
- *
- * This removes \a fcb from the list of filter callbacks and calls
- * the close callback associated with \a fcb.
- */
-static void close_filter_callback(struct filter_callback *fcb)
-{
-       PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data);
-       list_del(&fcb->node);
-       fcb->close(fcb);
-}
-
-/**
- * Close all callbacks of a filter node.
- *
- * \param fn The filter node which contains the filter callbacks to be closed.
- *
- * Call close_filter_callback() for each entry in the filter callback list
- * of \a fn.
- */
-static void close_callbacks(struct filter_node *fn)
-{
-       struct filter_callback *fcb, *tmp;
-
-       list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
-               PARA_INFO_LOG("closing %s filter callback\n",
-                       filters[fn->filter_num].name);
-               close_filter_callback(fcb);
-       }
-}
-
-static void call_callbacks(struct filter_node *fn, char *inbuf, size_t inlen,
-       char *outbuf, size_t outlen)
-{
-       struct filter_callback *fcb, *tmp;
-       list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
-               int ret;
-               if (inlen && fcb->input_cb) {
-                       ret = fcb->input_cb(inbuf, inlen, fcb);
-                       if (ret < 0) {
-                               close_filter_callback(fcb);
-                               continue;
-                       }
-               }
-               if (!outlen || !fcb->output_cb)
-                       continue;
-               ret = fcb->output_cb(outbuf, outlen, fcb);
-               if (ret < 0)
-                       close_filter_callback(fcb);
-       }
-}
-
-/**
- * Call the convert function of each filter.
- *
- * \param s Unused.
- * \param t The task identifying the filter chain.
- *
- * This is the core function of the filter subsystem. It loops over the list of
- * filter nodes determined by \a t and calls the filter's convert function if
- * there is input available for the filter node in question. If the convert
- * function consumed some or all of its input data, all registered input
- * callbacks are called.  Similarly, if a convert function produced output, all
- * registerd output callbacks get called.
- *
- * On errors a (negative) error code is stored in t->error.
- *
- * \sa filter_node, filter#convert, filter_callback.
- */
-void filter_pre_select(__a_unused struct sched *s, struct task *t)
-{
-       struct filter_chain *fc = container_of(t, struct filter_chain, task);
-       struct filter_node *fn;
-       char *ib;
-       size_t *loaded;
-       int i, conv, conv_total = 0;
-
-       if (fc->output_error && *fc->output_error < 0) {
-               t->error =  *fc->output_error;
-               return;
-       }
-again:
-       ib = fc->inbuf;
-       loaded = fc->in_loaded;
-       conv = 0;
-       FOR_EACH_FILTER_NODE(fn, fc, i) {
-               struct filter *f = filters + fn->filter_num;
-               if (*loaded && fn->loaded < fn->bufsize) {
-                       size_t size, old_fn_loaded = fn->loaded;
-//                     PARA_DEBUG_LOG("fc %p loaded: %zd, calling %s convert\n",
-//                             fc, *loaded, fn->filter->name);
-                       t->error = f->convert(ib, *loaded, fn);
-                       if (t->error < 0)
-                               return;
-                       size = t->error;
-                       call_callbacks(fn, ib, size, fn->buf + old_fn_loaded,
-                               fn->loaded - old_fn_loaded);
-                       *loaded -= size;
-                       conv += size;
-                       if (*loaded && size) {
-//                             PARA_DEBUG_LOG("moving %zd bytes in input "
-//                                     "buffer for %s filter\n",
-//                                     *loaded,  fn->filter->name);
-                               memmove(ib, ib + size, *loaded);
-                       }
-               }
-               ib = fn->buf;
-               loaded = &fn->loaded;
-       }
-       conv_total += conv;
-//     PARA_DEBUG_LOG("eof (in/out/fc): %d/%d/%d out_loaded: %zd, "
-//             "conv: %d, conv_total: %d\n", *fc->input_eof,
-//             fc->output_eof? *fc->output_eof : -42,
-//             fc->eof, *fc->out_loaded, conv, conv_total);
-       if (conv)
-               goto again;
-       if (!*fc->input_error)
-               return;
-       if (*fc->out_loaded)
-               return;
-       if (*fc->in_loaded && conv_total)
-               return;
-       t->error = -E_FC_EOF;
-}
-
-/**
- * Close all filter nodes and their callbacks.
- *
- * \param fc The filter chain to close.
- *
- * For each filter node determined by \a fc, call the close function of each
- * registered filter callback as well as the close function of the
- * corresponding filter.  Free all resources and destroy all callback lists and
- * the filter node list.
- *
- * \sa filter::close, filter_callback::close
- */
-void close_filters(struct filter_chain *fc)
-{
-       struct filter_node *fn;
-       int i;
-
-       if (!fc)
-               return;
-       PARA_NOTICE_LOG("closing filter chain %p\n", fc);
-       FOR_EACH_FILTER_NODE(fn, fc, i) {
-               struct filter *f = filters + fn->filter_num;
-               close_callbacks(fn);
-               PARA_INFO_LOG("closing %s filter\n", f->name);
-               f->close(fn);
-       }
-       free(fc->filter_nodes);
-}
-
-/*
- * If the filter has a command line parser and options is not NULL, run it.
- * Returns filter_num on success, negative on errors
- */
-static int parse_filter_args(int filter_num, char *options, void **conf)
-{
-       struct filter *f = &filters[filter_num];
-       int i, argc = 2;
-       char **argv;
-
-//     PARA_DEBUG_LOG("%s, options: %s, parser: %p\n", f->name,
-//             options? options : "(none)", f->parse_config);
-       if (!f->parse_config)
-               return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
-//     PARA_DEBUG_LOG("options: %s\n", options);
-       argc = split_args(options, &argv, " \t");
-//             PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
-       for (i = argc - 1; i >= 0; i--)
-               argv[i + 1] = argv[i];
-       argv[0] = para_strdup(f->name);
-       argc += 1;
-       *conf = f->parse_config(argc, argv);
-       free(argv[0]);
-       free(argv);
-       return *conf? filter_num : -E_BAD_FILTER_OPTIONS;
-}
-
-/**
- * Check the filter command line options.
- *
- * \param fa The command line options.
- * \param conf Points to the filter configuration upon successful return.
- *
- * Check if \a fa starts with a the name of a supported filter, followed by
- * a colon. If yes, call the command line parser of that filter.
- *
- * \return On success, the number of the filter is returned and \a conf
- * is initialized to point to the filter configuration determined by \a fa.
- * On errors, a negative value is returned.
- *
- * Note: If \a fa specifies a filter that has no command line parser success is
- * returned, and \a conf is initialized to \p NULL.
- *
- * \sa filter::parse_config
- */
-int check_filter_arg(char *fa, void **conf)
-{
-       int j;
-
-       *conf = NULL;
-//     PARA_DEBUG_LOG("arg: %s\n", fa);
-       for (j = 0; filters[j].name; j++) {
-               const char *name = filters[j].name;
-               size_t len = strlen(name);
-               char c;
-               if (strlen(fa) < len)
-                       continue;
-               if (strncmp(name, fa, len))
-                       continue;
-               c = fa[len];
-               if (c && c != ' ')
-                       continue;
-               if (c && !filters[j].parse_config)
-                       return -E_BAD_FILTER_OPTIONS;
-               return parse_filter_args(j, c? fa + len + 1 :
-                       fa + strlen(fa), conf);
-       }
-       return -E_UNSUPPORTED_FILTER;
-}
-
diff --git a/filter_common.c b/filter_common.c
new file mode 100644 (file)
index 0000000..a39eefd
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file filter_common.c Common helper functions for filter input/output. */
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "para.h"
+#include "list.h"
+#include "sched.h"
+#include "fd.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+
+/** The array of supported filters. */
+struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+
+/**
+ * Call the init function of each supported filter.
+ *
+ * \param all_filters the array of all supported filters.
+ *
+ * \sa filter::init
+ */
+void filter_init(struct filter *all_filters)
+{
+       int i;
+
+       FOR_EACH_SUPPORTED_FILTER(i)
+               all_filters[i].init(all_filters + i);
+}
+
+/**
+ * Close and destroy a filter callback.
+ *
+ * \param fcb The filter callback to close.
+ *
+ * This removes \a fcb from the list of filter callbacks and calls
+ * the close callback associated with \a fcb.
+ */
+static void close_filter_callback(struct filter_callback *fcb)
+{
+       PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data);
+       list_del(&fcb->node);
+       fcb->close(fcb);
+}
+
+/**
+ * Close all callbacks of a filter node.
+ *
+ * \param fn The filter node which contains the filter callbacks to be closed.
+ *
+ * Call close_filter_callback() for each entry in the filter callback list
+ * of \a fn.
+ */
+static void close_callbacks(struct filter_node *fn)
+{
+       struct filter_callback *fcb, *tmp;
+
+       list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
+               PARA_INFO_LOG("closing %s filter callback\n",
+                       filters[fn->filter_num].name);
+               close_filter_callback(fcb);
+       }
+}
+
+static void call_callbacks(struct filter_node *fn, char *inbuf, size_t inlen,
+       char *outbuf, size_t outlen)
+{
+       struct filter_callback *fcb, *tmp;
+       list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
+               int ret;
+               if (inlen && fcb->input_cb) {
+                       ret = fcb->input_cb(inbuf, inlen, fcb);
+                       if (ret < 0) {
+                               close_filter_callback(fcb);
+                               continue;
+                       }
+               }
+               if (!outlen || !fcb->output_cb)
+                       continue;
+               ret = fcb->output_cb(outbuf, outlen, fcb);
+               if (ret < 0)
+                       close_filter_callback(fcb);
+       }
+}
+
+/**
+ * Call the convert function of each filter.
+ *
+ * \param s Unused.
+ * \param t The task identifying the filter chain.
+ *
+ * This is the core function of the filter subsystem. It loops over the list of
+ * filter nodes determined by \a t and calls the filter's convert function if
+ * there is input available for the filter node in question. If the convert
+ * function consumed some or all of its input data, all registered input
+ * callbacks are called.  Similarly, if a convert function produced output, all
+ * registerd output callbacks get called.
+ *
+ * On errors a (negative) error code is stored in t->error.
+ *
+ * \sa filter_node, filter#convert, filter_callback.
+ */
+void filter_pre_select(__a_unused struct sched *s, struct task *t)
+{
+       struct filter_chain *fc = container_of(t, struct filter_chain, task);
+       struct filter_node *fn;
+       char *ib;
+       size_t *loaded;
+       int i, conv, conv_total = 0;
+
+       if (fc->output_error && *fc->output_error < 0) {
+               t->error =  *fc->output_error;
+               return;
+       }
+again:
+       ib = fc->inbuf;
+       loaded = fc->in_loaded;
+       conv = 0;
+       FOR_EACH_FILTER_NODE(fn, fc, i) {
+               struct filter *f = filters + fn->filter_num;
+               if (fn->loaded < fn->bufsize) {
+                       size_t size, old_fn_loaded = fn->loaded;
+//                     PARA_DEBUG_LOG("fc %p loaded: %zd, calling %s convert\n",
+//                             fc, *loaded, fn->filter->name);
+                       t->error = f->convert(ib, *loaded, fn);
+                       if (t->error < 0)
+                               return;
+                       size = t->error;
+                       call_callbacks(fn, ib, size, fn->buf + old_fn_loaded,
+                               fn->loaded - old_fn_loaded);
+                       *loaded -= size;
+                       conv += size;
+                       if (*loaded && size) {
+//                             PARA_DEBUG_LOG("moving %zd bytes in input "
+//                                     "buffer for %s filter\n",
+//                                     *loaded,  fn->filter->name);
+                               memmove(ib, ib + size, *loaded);
+                       }
+               }
+               ib = fn->buf;
+               loaded = &fn->loaded;
+       }
+       conv_total += conv;
+//     PARA_DEBUG_LOG("eof (in/out/fc): %d/%d/%d out_loaded: %zd, "
+//             "conv: %d, conv_total: %d\n", *fc->input_eof,
+//             fc->output_eof? *fc->output_eof : -42,
+//             fc->eof, *fc->out_loaded, conv, conv_total);
+       if (conv)
+               goto again;
+       if (!*fc->input_error)
+               return;
+       if (*fc->out_loaded)
+               return;
+       if (*fc->in_loaded && conv_total)
+               return;
+       t->error = -E_FC_EOF;
+}
+
+/**
+ * Close all filter nodes and their callbacks.
+ *
+ * \param fc The filter chain to close.
+ *
+ * For each filter node determined by \a fc, call the close function of each
+ * registered filter callback as well as the close function of the
+ * corresponding filter.  Free all resources and destroy all callback lists and
+ * the filter node list.
+ *
+ * \sa filter::close, filter_callback::close
+ */
+void close_filters(struct filter_chain *fc)
+{
+       struct filter_node *fn;
+       int i;
+
+       if (!fc)
+               return;
+       PARA_NOTICE_LOG("closing filter chain %p\n", fc);
+       FOR_EACH_FILTER_NODE(fn, fc, i) {
+               struct filter *f = filters + fn->filter_num;
+               close_callbacks(fn);
+               PARA_INFO_LOG("closing %s filter\n", f->name);
+               f->close(fn);
+       }
+       free(fc->filter_nodes);
+}
+
+/*
+ * If the filter has a command line parser and options is not NULL, run it.
+ * Returns filter_num on success, negative on errors
+ */
+static int parse_filter_args(int filter_num, char *options, void **conf)
+{
+       struct filter *f = &filters[filter_num];
+       int ret, i, argc = 2;
+       char **argv;
+
+//     PARA_DEBUG_LOG("%s, options: %s, parser: %p\n", f->name,
+//             options? options : "(none)", f->parse_config);
+       if (!f->parse_config)
+               return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
+//     PARA_DEBUG_LOG("options: %s\n", options);
+       argc = split_args(options, &argv, " \t");
+//             PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
+       for (i = argc - 1; i >= 0; i--)
+               argv[i + 1] = argv[i];
+       argv[0] = para_strdup(f->name);
+       argc += 1;
+       ret = f->parse_config(argc, argv, conf);
+       free(argv[0]);
+       free(argv);
+       return ret < 0? ret : filter_num;
+}
+
+/**
+ * Check the filter command line options.
+ *
+ * \param fa The command line options.
+ * \param conf Points to the filter configuration upon successful return.
+ *
+ * Check if \a fa starts with a the name of a supported filter, followed by
+ * a colon. If yes, call the command line parser of that filter.
+ *
+ * \return On success, the number of the filter is returned and \a conf
+ * is initialized to point to the filter configuration determined by \a fa.
+ * On errors, a negative value is returned.
+ *
+ * Note: If \a fa specifies a filter that has no command line parser success is
+ * returned, and \a conf is initialized to \p NULL.
+ *
+ * \sa filter::parse_config
+ */
+int check_filter_arg(char *fa, void **conf)
+{
+       int j;
+
+       *conf = NULL;
+//     PARA_DEBUG_LOG("arg: %s\n", fa);
+       FOR_EACH_SUPPORTED_FILTER(j) {
+               const char *name = filters[j].name;
+               size_t len = strlen(name);
+               char c;
+               if (strlen(fa) < len)
+                       continue;
+               if (strncmp(name, fa, len))
+                       continue;
+               c = fa[len];
+               if (c && c != ' ')
+                       continue;
+               if (c && !filters[j].parse_config)
+                       return -E_BAD_FILTER_OPTIONS;
+               return parse_filter_args(j, c? fa + len + 1 :
+                       fa + strlen(fa), conf);
+       }
+       return -E_UNSUPPORTED_FILTER;
+}
+
+void print_filter_helps(int detailed)
+{
+       int i;
+
+       printf_or_die("\nAvailable filters: \n\t");
+       FOR_EACH_SUPPORTED_FILTER(i)
+               printf_or_die("%s%s", i? " " : "", filters[i].name);
+       printf_or_die("\n\n");
+
+       FOR_EACH_SUPPORTED_FILTER(i) {
+               struct filter *f = filters + i;
+
+               if (!f->help.short_help)
+                       continue;
+               printf_or_die("Options for %s:\n", f->name);
+               ggo_print_help(&f->help, detailed);
+       }
+
+}
diff --git a/ggo.c b/ggo.c
new file mode 100644 (file)
index 0000000..55f1043
--- /dev/null
+++ b/ggo.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file ggo.c Function for printing help. */
+
+
+#include "para.h"
+#include "ggo.h"
+
+__printf_1_2 void printf_or_die(const char *fmt, ...)
+{
+       va_list argp;
+       int ret;
+
+       va_start(argp, fmt);
+       ret = vprintf(fmt, argp);
+       va_end(argp);
+       if (ret >= 0)
+               return;
+       fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(errno));
+}
+
+void ggo_print_help(struct ggo_help *help, int detailed_help)
+{
+       const char **p;
+
+       if (!help)
+               return;
+       if (detailed_help)
+               p = help->detailed_help;
+       else
+               p = help->short_help;
+       if (!p)
+               return;
+       p += 3; /* skip -h and -V */
+       for (; *p; p++)
+               printf_or_die("\t%s\n", *p);
+}
diff --git a/ggo.h b/ggo.h
new file mode 100644 (file)
index 0000000..c9111b1
--- /dev/null
+++ b/ggo.h
@@ -0,0 +1,7 @@
+struct ggo_help {
+       const char **short_help;
+       const char **detailed_help;
+};
+
+void ggo_print_help(struct ggo_help *help, int detailed_help);
+void printf_or_die(const char *fmt, ...);
index 77d3616d2c2e41b3caf32f5fea13d54cf778642b..b9d70147bb63de054005be4eb17f13e5ae42ee43 100644 (file)
@@ -18,6 +18,7 @@
 #include "grab_client.cmdline.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "audiod.h"
diff --git a/gui.c b/gui.c
index 9a02bdcabc3c252fb875b893cea5610f23243b2e..616890299047629dcaad4541a239efae19f2b76c 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -489,7 +489,7 @@ static int add_output_line(char *line, __a_unused void *data)
        return 1;
 }
 
-void para_log(int ll, const char *fmt,...)
+__printf_2_3 void para_log(int ll, const char *fmt,...)
 {
        int color;
        char *msg;
@@ -523,7 +523,7 @@ static void setup_signal_handling(void)
        signal(SIGHUP, SIG_IGN);
 }
 
-static void do_exit(int ret)
+__noreturn static void do_exit(int ret)
 {
        signal(SIGTERM, SIG_IGN);
        kill(0, SIGTERM);
@@ -539,7 +539,7 @@ static void shutdown_curses(void)
        endwin();
 }
 
-static void finish(int ret)
+__noreturn static void finish(int ret)
 {
        shutdown_curses();
        do_exit(ret);
@@ -548,7 +548,7 @@ static void finish(int ret)
 /*
  * exit curses and print given message to stdout/stderr
  */
-__printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
+__noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
 {
        va_list argp;
        FILE *outfd = ret? stderr: stdout;
@@ -1280,7 +1280,7 @@ static void com_version(void)
                CODENAME "\"");
 }
 
-static void com_quit(void)
+__noreturn static void com_quit(void)
 {
        finish(0);
 }
index 78f7aa071c32aeb8cdad489850bfb4e10f651700..ce79ca63e694a3c438d68dbc764d7f8e5ce16cc6 100644 (file)
@@ -261,16 +261,25 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_NUM_CHUNKS].align = RIGHT;
        d[SI_NUM_CHUNKS].x = 73;
        d[SI_NUM_CHUNKS].y = 27;
-       d[SI_NUM_CHUNKS].len = 12;
+       d[SI_NUM_CHUNKS].len = 11;
 
-       d[SI_CHUNK_TIME].prefix = " ";
+       d[SI_CHUNK_TIME].prefix = "";
        d[SI_CHUNK_TIME].postfix = "ms";
        d[SI_CHUNK_TIME].fg = COLOR_MAGENTA;
        d[SI_CHUNK_TIME].bg = COLOR_BLACK;
        d[SI_CHUNK_TIME].align = LEFT;
-       d[SI_CHUNK_TIME].x = 85;
+       d[SI_CHUNK_TIME].x = 84;
        d[SI_CHUNK_TIME].y = 27;
-       d[SI_CHUNK_TIME].len = 15;
+       d[SI_CHUNK_TIME].len = 8;
+
+       d[SI_AMPLIFICATION].prefix = "amp:";
+       d[SI_AMPLIFICATION].postfix = "";
+       d[SI_AMPLIFICATION].fg = COLOR_MAGENTA;
+       d[SI_AMPLIFICATION].bg = COLOR_BLACK;
+       d[SI_AMPLIFICATION].align = RIGHT;
+       d[SI_AMPLIFICATION].x = 92;
+       d[SI_AMPLIFICATION].y = 27;
+       d[SI_AMPLIFICATION].len = 8;
 
        d[SI_AUDIO_FILE_INFO].prefix = "";
        d[SI_AUDIO_FILE_INFO].postfix = "";
diff --git a/hash.h b/hash.h
index 03b45e04db71965dd70dfce56eafce6ad83f163e..2d6de8ab25baedad30a039da8f3fa0dba4d3b165 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -6,8 +6,6 @@
 
 /** \file hash.h Inline functions for hash values. */
 
-#include "portable_io.h"
-
 /** hash arrays are always unsigned char. */
 #define HASH_TYPE unsigned char
 
index 2e518b2063a8c81946b27d971ad80f5fca3c070e..7716bb0f9f9c5ba5439bb7cd70c2055887e7e3c1 100644 (file)
@@ -14,6 +14,7 @@
 #include "http.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "http_recv.cmdline.h"
 #include "net.h"
@@ -119,7 +120,7 @@ static void http_recv_post_select(struct sched *s, struct task *t)
        if (!FD_ISSET(phd->fd, &s->rfds))
                return;
        if (phd->status == HTTP_SENT_GET_REQUEST) {
-               t->error = recv_pattern(phd->fd, HTTP_OK_MSG, MAXLINE);
+               t->error = recv_pattern(phd->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
                if (t->error >= 0) {
                        PARA_INFO_LOG("received ok msg, streaming\n");
                        phd->status = HTTP_STREAMING;
@@ -189,10 +190,17 @@ static int http_recv_open(struct receiver_node *rn)
  */
 void http_recv_init(struct receiver *r)
 {
+       struct http_recv_args_info dummy;
+
+       http_recv_cmdline_parser_init(&dummy);
        r->open = http_recv_open;
        r->close = http_recv_close;
        r->pre_select = http_recv_pre_select;
        r->post_select = http_recv_post_select;
        r->shutdown = http_shutdown;
        r->parse_config = http_recv_parse_config;
+       r->help = (struct ggo_help) {
+               .short_help = http_recv_args_info_help,
+               .detailed_help = http_recv_args_info_detailed_help
+       };
 }
index c368efd7ad301339b86d02574f2b4e4f721152cc..2f1b4d61d54b3be411e2fb78a509685d8f4b07c2 100644 (file)
@@ -1,3 +1,15 @@
-section "options of the http receiver"
-option "host" i "ip or host" string default="localhost" optional
-option "port" p "tcp port to connect to" int default="8000" optional
+option "host" i
+#~~~~~~~~~~~~~~
+"ip or host"
+string
+default="localhost"
+optional
+details="
+       Both IPv4 and IPv6 addresses are supported.
+"
+
+option "port" p
+#~~~~~~~~~~~~~~
+"tcp port to connect to"
+int default="8000"
+optional
diff --git a/mood.c b/mood.c
index aa220f1e5d53b230b7fc03ef08060828b854fe72..cdc0745a255f994eb1a37c661cbfa1dfe1952221 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -84,7 +84,7 @@ typedef void mood_cleanup_function(void *);
  * Used for scoring and to determine whether a file is admissible.
  */
 struct mood_method {
-       /* The name of the method. */
+       /** The name of the method. */
        const char *name;
        /** Pointer to the mood parser. */
        mood_parser *parser;
index ca6441606c6efaeffa1531975387f90070dfb4ad..b809e8b3611d278d41b6ad7f965aa547389e52d3 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -280,7 +280,7 @@ static int frame_length(struct mp3header *header)
 
 static int compare_headers(struct mp3header *h1,struct mp3header *h2)
 {
-       if ((*(uint*)h1) == (*(uint*)h2))
+       if ((*(unsigned int*)h1) == (*(unsigned int*)h2))
                return 1;
        if ((h1->version == h2->version) &&
                        (h1->layer == h2->layer) &&
diff --git a/mp3dec.c b/mp3dec.c
deleted file mode 100644 (file)
index 763ca37..0000000
--- a/mp3dec.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file mp3dec.c Paraslash's mp3 decoder. */
-
-#include "para.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include <mad.h>
-#include "string.h"
-
-/** The output buffer size. */
-#define MP3_OUTBUF_SIZE (128 * 1024)
-
-/** Convert a sample value from libmad to a signed short. */
-#define MAD_TO_SHORT(f) (f) >= MAD_F_ONE? SHRT_MAX :\
-       (f) <= -MAD_F_ONE? -SHRT_MAX : (signed short) ((f) >> (MAD_F_FRACBITS - 15))
-
-/** Data specific to the mp3dec filter. */
-struct private_mp3dec_data {
-       /** Information on the current mp3 stream. */
-       struct mad_stream stream;
-       /** Information about the frame which is currently decoded. */
-       struct mad_frame frame;
-       /** Contains the PCM output. */
-       struct mad_synth synth;
-};
-
-static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
-{
-       int i, ret;
-       struct private_mp3dec_data *pmd = fn->private_data;
-       size_t copy = PARA_MIN(len, 4096);
-
-       if (fn->loaded > fn->bufsize * 4 / 5)
-               return 0;
-       mad_stream_buffer(&pmd->stream, (unsigned char *) inbuffer, copy);
-       pmd->stream.error = 0;
-next_frame:
-       ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
-       if (ret < 0)
-               goto out;
-       fn->fc->samplerate = pmd->frame.header.samplerate;
-       fn->fc->channels = MAD_NCHANNELS(&pmd->frame.header);
-       ret = mad_frame_decode(&pmd->frame, &pmd->stream);
-       if (ret) {
-               if (MAD_RECOVERABLE(pmd->stream.error) || pmd->stream.error == MAD_ERROR_BUFLEN)
-                       goto out;
-               PARA_ERROR_LOG("fatal: ret = %d, loaded = %zd\n", ret, fn->loaded);
-               return -E_MAD_FRAME_DECODE;
-       }
-       mad_synth_frame(&pmd->synth, &pmd->frame);
-
-       for (i = 0; i < pmd->synth.pcm.length; i++) {
-               int s = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
-               write_int16_host_endian(fn->buf + fn->loaded, s);
-               fn->loaded += 2;
-               if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
-                       s = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
-                       write_int16_host_endian(fn->buf + fn->loaded, s);
-                       fn->loaded += 2;
-               }
-               if (fn->loaded != fn->bufsize) /* output buffer not full */
-                       continue;
-               PARA_ERROR_LOG("output buffer full: %zd\n", fn->loaded);
-                       return -E_MP3DEC_OVERRUN;
-       }
-       if (fn->loaded <= fn->bufsize * 4 / 5)
-               goto next_frame;
-out:
-       if (pmd->stream.next_frame) { /* we still have some data */
-               size_t off = pmd->stream.bufend - pmd->stream.next_frame;
-//             PARA_INFO_LOG("off: %zd, rate: %u, returning %zd\n", off,
-//                     fn->fc->samplerate, copy - off);
-               return copy - off;
-       }
-       return copy;
-}
-
-static void mp3dec_close(struct filter_node *fn)
-{
-       struct private_mp3dec_data *pmd = fn->private_data;
-
-       mad_synth_finish(&pmd->synth);
-       mad_frame_finish(&pmd->frame);
-       mad_stream_finish(&pmd->stream);
-
-       free(fn->buf);
-       fn->buf = NULL;
-       free(pmd);
-       fn->private_data = NULL;
-}
-
-static void mp3dec_open(struct filter_node *fn)
-{
-       fn->private_data = para_calloc(sizeof(struct private_mp3dec_data));
-       struct private_mp3dec_data *pmd = fn->private_data;
-
-       mad_stream_init(&pmd->stream);
-       mad_frame_init(&pmd->frame);
-       mad_synth_init(&pmd->synth);
-       fn->loaded = 0;
-       fn->bufsize = MP3_OUTBUF_SIZE;
-       fn->buf = para_calloc(fn->bufsize);
-}
-
-/**
- * The init function of the mp3dec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void mp3dec_init(struct filter *f)
-{
-       f->open = mp3dec_open;
-       f->convert = mp3dec;
-       f->close = mp3dec_close;
-}
diff --git a/mp3dec_filter.c b/mp3dec_filter.c
new file mode 100644 (file)
index 0000000..ce6cfbc
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mp3dec_filter.c Paraslash's mp3 decoder. */
+
+#include "para.h"
+#include "mp3dec_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include <mad.h>
+#include "string.h"
+
+/** Convert a sample value from libmad to a signed short. */
+#define MAD_TO_SHORT(f) (f) >= MAD_F_ONE? SHRT_MAX :\
+       (f) <= -MAD_F_ONE? -SHRT_MAX : (signed short) ((f) >> (MAD_F_FRACBITS - 15))
+
+/** Data specific to the mp3dec filter. */
+struct private_mp3dec_data {
+       /** Information on the current mp3 stream. */
+       struct mad_stream stream;
+       /** Information about the frame which is currently decoded. */
+       struct mad_frame frame;
+       /** Contains the PCM output. */
+       struct mad_synth synth;
+};
+
+static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
+{
+       int i, ret;
+       struct private_mp3dec_data *pmd = fn->private_data;
+       size_t copy = PARA_MIN(len, (size_t)4096);
+
+       if (fn->loaded + 16384 > fn->bufsize)
+               return 0;
+       mad_stream_buffer(&pmd->stream, (unsigned char *) inbuffer, copy);
+       pmd->stream.error = 0;
+next_frame:
+       ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
+       if (ret < 0) {
+               if (pmd->stream.error != MAD_ERROR_BUFLEN &&
+                       pmd->stream.error != MAD_ERROR_LOSTSYNC)
+                       PARA_DEBUG_LOG("header decode: %s\n",
+                               mad_stream_errorstr(&pmd->stream));
+               goto out;
+       }
+       fn->fc->samplerate = pmd->frame.header.samplerate;
+       fn->fc->channels = MAD_NCHANNELS(&pmd->frame.header);
+       ret = mad_frame_decode(&pmd->frame, &pmd->stream);
+       if (ret) {
+               if (MAD_RECOVERABLE(pmd->stream.error) ||
+                       pmd->stream.error == MAD_ERROR_BUFLEN) {
+                       PARA_DEBUG_LOG("frame decode: %s\n",
+                               mad_stream_errorstr(&pmd->stream));
+                       goto out;
+               }
+               PARA_ERROR_LOG("frame decode: %s\n",
+                       mad_stream_errorstr(&pmd->stream));
+               return -E_MAD_FRAME_DECODE;
+       }
+       mad_synth_frame(&pmd->synth, &pmd->frame);
+
+       for (i = 0; i < pmd->synth.pcm.length; i++) {
+               int s = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
+               write_int16_host_endian(fn->buf + fn->loaded, s);
+               fn->loaded += 2;
+               if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
+                       s = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
+                       write_int16_host_endian(fn->buf + fn->loaded, s);
+                       fn->loaded += 2;
+               }
+               if (fn->loaded != fn->bufsize) /* output buffer not full */
+                       continue;
+               PARA_ERROR_LOG("output buffer full: %zd\n", fn->loaded);
+                       return -E_MP3DEC_OVERRUN;
+       }
+       if (fn->loaded + 16384 <= fn->bufsize)
+               goto next_frame;
+out:
+       if (pmd->stream.next_frame) { /* we still have some data */
+               size_t off = pmd->stream.bufend - pmd->stream.next_frame;
+//             PARA_INFO_LOG("off: %zd, rate: %u, returning %zd\n", off,
+//                     fn->fc->samplerate, copy - off);
+               return copy - off;
+       }
+       return copy;
+}
+
+static void mp3dec_close(struct filter_node *fn)
+{
+       struct private_mp3dec_data *pmd = fn->private_data;
+
+       mad_synth_finish(&pmd->synth);
+       mad_frame_finish(&pmd->frame);
+       mad_stream_finish(&pmd->stream);
+
+       free(fn->buf);
+       fn->buf = NULL;
+       free(pmd);
+       fn->private_data = NULL;
+}
+
+static void mp3dec_open(struct filter_node *fn)
+{
+       struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
+       struct mp3dec_filter_args_info *mp3_conf = fn->conf;
+
+       fn->private_data = pmd;
+       mad_stream_init(&pmd->stream);
+       mad_frame_init(&pmd->frame);
+       mad_synth_init(&pmd->synth);
+       fn->loaded = 0;
+       fn->bufsize = mp3_conf->bufsize_arg * 1024;
+       fn->buf = para_calloc(fn->bufsize);
+       if (mp3_conf->ignore_crc_given)
+               mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC);
+}
+
+static int mp3dec_parse_config(int argc, char **argv, void **config)
+{
+       int ret;
+       struct mp3dec_filter_args_info *mp3_conf;
+
+       mp3_conf = para_calloc(sizeof(*mp3_conf));
+       ret = -E_MP3DEC_SYNTAX;
+       if (mp3dec_cmdline_parser(argc, argv, mp3_conf))
+               goto err;
+       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (mp3_conf->bufsize_arg < 32)
+               goto err;
+       if (mp3_conf->bufsize_arg >= INT_MAX / 1024)
+               goto err;
+       *config = mp3_conf;
+       return 1;
+err:
+       free(mp3_conf);
+       return ret;
+}
+
+/**
+ * The init function of the mp3dec filter.
+ *
+ * \param f Pointer to the filter struct to initialize.
+ *
+ * \sa filter::init.
+ */
+void mp3dec_filter_init(struct filter *f)
+{
+       struct mp3dec_filter_args_info dummy;
+
+       mp3dec_cmdline_parser_init(&dummy);
+       f->open = mp3dec_open;
+       f->convert = mp3dec;
+       f->close = mp3dec_close;
+       f->parse_config = mp3dec_parse_config;
+       f->help = (struct ggo_help) {
+               .short_help = mp3dec_filter_args_info_help,
+               .detailed_help = mp3dec_filter_args_info_detailed_help
+       };
+}
diff --git a/mp3dec_filter.ggo b/mp3dec_filter.ggo
new file mode 100644 (file)
index 0000000..a34c30a
--- /dev/null
@@ -0,0 +1,22 @@
+option "bufsize" b
+#~~~~~~~~~~~~~~~~~
+"size of output buffer"
+int typestr="kilobyte"
+default="128"
+optional
+details="
+       Increase this if you encounter output buffer overrun
+       errors. Smaller values make the mp3dec filter use less
+       memory. The minimal size is 32K.
+"
+
+option "ignore-crc" i
+#~~~~~~~~~~~~~~~~~~~~
+"ignore CRC information in the audio stream."
+flag off
+details="
+       This causes frames with CRC errors to be decoded and played
+       anyway. This option is not recommended, but since some encoders
+       have been known to generate bad CRC information, this option
+       is a work-around to play streams from such encoders.
+"
diff --git a/net.c b/net.c
index 60f8e9a1d07cceafa4dcc51bb64ec5ae6b50b82e..9309ac1f02dc63edcc7af2939686b10b6d9b0599 100644 (file)
--- a/net.c
+++ b/net.c
@@ -728,10 +728,9 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
  *
  * \return Positive if \a pattern was received, negative otherwise.
  *
- * This function creates a buffer of size \a bufsize and tries
- * to receive at most \a bufsize bytes from file descriptor \a fd.
- * If at least \p strlen(\a pattern) bytes were received, the beginning of
- * the received buffer is compared with \a pattern, ignoring case.
+ * This function tries to receive at most \a bufsize bytes from file descriptor
+ * \a fd. If at least \p strlen(\a pattern) bytes were received, the beginning
+ * of the received buffer is compared with \a pattern, ignoring case.
  *
  * \sa recv_buffer(), \sa strncasecmp(3).
  */
@@ -739,7 +738,7 @@ int recv_pattern(int fd, const char *pattern, size_t bufsize)
 {
        size_t len = strlen(pattern);
        char *buf = para_malloc(bufsize + 1);
-       int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize);
+       int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize + 1);
 
        if (n < len)
                goto out;
index 11f7f5ad53a9a1f999fa7c7076d88df3aeb4407b..2030eada238a85e939a325c0ef9ad0b771f0e8d2 100644 (file)
--- a/ogg_afh.c
+++ b/ogg_afh.c
@@ -34,8 +34,15 @@ struct ogg_datasource {
 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
 {
        struct ogg_datasource *ods = datasource;
-       size_t copy = PARA_MIN(ods->numbytes - ods->fpos, size * nmemb),
-               ret = copy / size;
+       size_t copy, ret;
+
+       if (!size)
+               return 0;
+
+       assert(ods->numbytes >= ods->fpos);
+       ret = ods->numbytes - ods->fpos;
+       copy = PARA_MIN(ret, size * nmemb);
+       ret = copy / size;
        if (!ret)
                return 0;
        memcpy(buf, ods->map + ods->fpos, copy);
@@ -115,7 +122,7 @@ static int ogg_compute_header_len(char *map, size_t numbytes,
                struct afh_info *afhi)
 {
        int ret;
-       size_t len = PARA_MIN(numbytes, CHUNK_SIZE);
+       size_t len = PARA_MIN(numbytes, (size_t)CHUNK_SIZE);
        int serial;
        char *buf;
 
@@ -200,37 +207,30 @@ static long unsigned ogg_compute_chunk_table(OggVorbis_File *of,
        struct afh_info *afhi, long unsigned time_total)
 {
        int i, ret, num;
-       ssize_t max_chunk_len, pos = 0, min = 0, old_pos;
        long unsigned num_chunks;
+       ogg_int64_t max = 0, min = 0, old_pos = 0;
 
-       old_pos = 0;
        ret = 0;
        num = time_total / chunk_time + 3;
        PARA_DEBUG_LOG("chunk time: %g allocating %d chunk pointers\n",
                chunk_time, num);
        afhi->chunk_table = para_malloc((num + 1) * sizeof(size_t));
        afhi->chunk_table[0] = 0;
-       max_chunk_len = 0;
-       for (i = 1; ret <= num; i++) {
-               ogg_int64_t diff;
+       for (i = 1; i <= num; i++) {
+               ogg_int64_t diff, pos;
                ret = ov_time_seek(of, i * chunk_time);
                if (ret)
                        break;
                pos = ov_raw_tell(of);
                diff = pos - old_pos;
-               max_chunk_len = PARA_MAX(max_chunk_len, diff);
+               max = PARA_MAX(max, diff);
                min = (i == 1)? diff : PARA_MIN(min, diff);
                afhi->chunk_table[i] = pos;
-//             if (i < 11 || !((i - 1) % 1000)|| i > num - 11)
-//                     PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %zd, "
-//                             "size: %zd\n", i - 1,
-//                             i * chunk_time, pos, pos - old_pos);
                old_pos = pos;
        }
        num_chunks = i - 1;
-//fi->chunk_table[i] = pos;
-       PARA_DEBUG_LOG("%lu chunks (%fs), max chunk: %zd, min chunk: %zd\n",
-               num_chunks, chunk_time, max_chunk_len, min);
+       PARA_DEBUG_LOG("%lu chunks (%fs), max chunk: %lld, min chunk: %lld\n",
+               num_chunks, chunk_time, (long long)max, (long long)min);
        return num_chunks;
 }
 
diff --git a/oggdec.c b/oggdec.c
deleted file mode 100644 (file)
index b82f12e..0000000
--- a/oggdec.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file oggdec.c Paraslash's ogg vorbis decoder. */
-
-#include "para.h"
-
-#include "oggdec_filter.cmdline.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-
-#include <vorbis/vorbisfile.h>
-
-/** Determine byte sex. */
-#ifdef WORDS_BIGENDIAN
-#define ENDIAN 1
-#else
-#define ENDIAN 0
-#endif
-
-/** Data specific to the oggdec filter. */
-struct private_oggdec_data {
-       /** Describes an ogg vorbis file. */
-       OggVorbis_File *vf;
-       /** The input buffer. */
-       char *inbuf;
-       /** The length of \a inbuf. */
-       size_t inbuf_len;
-       /** The number of bytes consumed from the input buffer. */
-       size_t converted;
-};
-
-static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
-{
-       struct filter_node *fn = datasource;
-       struct private_oggdec_data *pod = fn->private_data;
-       size_t ret, have = pod->inbuf_len - pod->converted;
-       char *p = pod->inbuf + pod->converted;
-
-//     PARA_DEBUG_LOG("pod = %p\n", pod);
-//     PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have);
-       if (pod->inbuf_len < size) {
-               if (*fn->fc->input_error)
-                       return 0;
-               errno = EAGAIN;
-               return (size_t)-1;
-       }
-       ret = PARA_MIN(nmemb, have / size) * size;
-       memcpy(buf, p, ret);
-       pod->converted += ret;
-       return ret;
-}
-
-/*
- * Custom data seeking function.
- *
- * Since we want the data source to be treated as unseekable at all
- * times, the provided seek callback always returns -1 (failure).
- */
-static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
-               __a_unused int whence)
-{
-       return -1;
-}
-
-static int cb_close(__a_unused void *datasource)
-{
-       return 0;
-}
-
-static const ov_callbacks ovc = {
-       .read_func = cb_read,
-       .seek_func = cb_seek,
-       .close_func = cb_close,
-       /*
-        * The tell function need not be provided if the data IO abstraction is
-        * not seekable
-        */
-       .tell_func = NULL
-};
-
-static void ogg_open(struct filter_node *fn)
-{
-       struct private_oggdec_data *pod = para_calloc(
-               sizeof(struct private_oggdec_data));
-       struct oggdec_filter_args_info *conf = fn->conf;
-
-       fn->private_data = pod;
-       fn->bufsize = conf->bufsize_arg * 1024;
-       fn->buf = para_malloc(fn->bufsize);
-}
-
-static void ogg_close(struct filter_node *fn)
-{
-       struct private_oggdec_data *pod = fn->private_data;
-       if (pod->vf) {
-               PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
-               ov_clear(pod->vf);
-               free(pod->vf);
-               pod->vf = NULL;
-       } else
-               PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod);
-       free(fn->buf);
-       fn->buf = NULL;
-       free(fn->private_data);
-       fn->private_data = NULL;
-}
-
-static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
-{
-       ssize_t ret;
-       struct private_oggdec_data *pod = fn->private_data;
-       struct oggdec_filter_args_info *conf = fn->conf;
-       /* make the buffer known to the read callback cb_read() */
-       pod->inbuf = inbuffer;
-       pod->inbuf_len = len;
-       pod->converted = 0;
-
-       if (!pod->vf) {
-               int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
-               if (len <ib && !*fn->fc->input_error) {
-                       PARA_DEBUG_LOG("initial input buffer %zd/%d, "
-                               "waiting for more data\n", len, ib);
-                       return 0;
-               }
-               pod->vf = para_malloc(sizeof(struct OggVorbis_File));
-               PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
-               ret = ov_open_callbacks(fn, pod->vf,
-                       NULL, /* no initial buffer */
-                       0, /* no initial bytes */
-                       ovc); /* the ov_open_callbacks */
-               if (ret == OV_EREAD)
-                       return -E_OGGDEC_READ;
-               if (ret == OV_ENOTVORBIS)
-                       return -E_OGGDEC_NOTVORBIS;
-               if (ret == OV_EVERSION)
-                       return -E_OGGDEC_VERSION;
-               if (ret == OV_EBADHEADER)
-                       return -E_OGGDEC_BADHEADER;
-               if (ret < 0)
-                       return -E_OGGDEC_FAULT;
-               fn->fc->channels = ov_info(pod->vf, 0)->channels;
-               fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
-               PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
-                       fn->fc->samplerate);
-       }
-       while (!*fn->fc->input_error && fn->loaded < fn->bufsize) {
-               int length = fn->bufsize - fn->loaded;
-               long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
-                       ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
-               if (read_ret == OV_HOLE || !read_ret)
-                       return pod->converted;
-               if (read_ret < 0)
-                       return -E_OGGDEC_BADLINK;
-               fn->loaded += read_ret;
-       }
-       return pod->converted;
-}
-
-static void *oggdec_parse_config(int argc, char **argv)
-{
-       struct oggdec_filter_args_info *ret = para_calloc(sizeof(struct oggdec_filter_args_info));
-       if (!oggdec_cmdline_parser(argc, argv, ret))
-               return ret;
-       free(ret);
-       return NULL;
-}
-
-/**
- * The init function of the ogg vorbis decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void oggdec_init(struct filter *f)
-{
-       f->open = ogg_open;
-       f->close = ogg_close;
-       f->convert = ogg_convert;
-       f->print_help = oggdec_cmdline_parser_print_help;
-       f->parse_config = oggdec_parse_config;
-}
diff --git a/oggdec_filter.c b/oggdec_filter.c
new file mode 100644 (file)
index 0000000..0653f7d
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file oggdec_filter.c Paraslash's ogg vorbis decoder. */
+
+#include "para.h"
+
+#include "oggdec_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+
+#include <vorbis/vorbisfile.h>
+
+/** Determine byte sex. */
+#ifdef WORDS_BIGENDIAN
+#define ENDIAN 1
+#else
+#define ENDIAN 0
+#endif
+
+/** Data specific to the oggdec filter. */
+struct private_oggdec_data {
+       /** Describes an ogg vorbis file. */
+       OggVorbis_File *vf;
+       /** The input buffer. */
+       char *inbuf;
+       /** The length of \a inbuf. */
+       size_t inbuf_len;
+       /** The number of bytes consumed from the input buffer. */
+       size_t converted;
+};
+
+static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
+{
+       struct filter_node *fn = datasource;
+       struct private_oggdec_data *pod = fn->private_data;
+       size_t ret, have = pod->inbuf_len - pod->converted;
+       char *p = pod->inbuf + pod->converted;
+
+//     PARA_DEBUG_LOG("pod = %p\n", pod);
+//     PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have);
+       if (pod->inbuf_len < size) {
+               if (*fn->fc->input_error)
+                       return 0;
+               errno = EAGAIN;
+               return (size_t)-1;
+       }
+       ret = PARA_MIN(nmemb, have / size) * size;
+       memcpy(buf, p, ret);
+       pod->converted += ret;
+       return ret;
+}
+
+/*
+ * Custom data seeking function.
+ *
+ * Since we want the data source to be treated as unseekable at all
+ * times, the provided seek callback always returns -1 (failure).
+ */
+static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
+               __a_unused int whence)
+{
+       return -1;
+}
+
+static int cb_close(__a_unused void *datasource)
+{
+       return 0;
+}
+
+static const ov_callbacks ovc = {
+       .read_func = cb_read,
+       .seek_func = cb_seek,
+       .close_func = cb_close,
+       /*
+        * The tell function need not be provided if the data IO abstraction is
+        * not seekable
+        */
+       .tell_func = NULL
+};
+
+static void ogg_open(struct filter_node *fn)
+{
+       struct private_oggdec_data *pod = para_calloc(
+               sizeof(struct private_oggdec_data));
+       struct oggdec_filter_args_info *conf = fn->conf;
+
+       fn->private_data = pod;
+       fn->bufsize = conf->bufsize_arg * 1024;
+       fn->buf = para_malloc(fn->bufsize);
+}
+
+static void ogg_close(struct filter_node *fn)
+{
+       struct private_oggdec_data *pod = fn->private_data;
+       if (pod->vf) {
+               PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
+               ov_clear(pod->vf);
+               free(pod->vf);
+               pod->vf = NULL;
+       } else
+               PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod);
+       free(fn->buf);
+       fn->buf = NULL;
+       free(fn->private_data);
+       fn->private_data = NULL;
+}
+
+static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
+{
+       ssize_t ret;
+       struct private_oggdec_data *pod = fn->private_data;
+       struct oggdec_filter_args_info *conf = fn->conf;
+       /* make the buffer known to the read callback cb_read() */
+       pod->inbuf = inbuffer;
+       pod->inbuf_len = len;
+       pod->converted = 0;
+
+       if (!pod->vf) {
+               int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
+               if (len <ib && !*fn->fc->input_error) {
+                       PARA_DEBUG_LOG("initial input buffer %zd/%d, "
+                               "waiting for more data\n", len, ib);
+                       return 0;
+               }
+               pod->vf = para_malloc(sizeof(struct OggVorbis_File));
+               PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
+               ret = ov_open_callbacks(fn, pod->vf,
+                       NULL, /* no initial buffer */
+                       0, /* no initial bytes */
+                       ovc); /* the ov_open_callbacks */
+               if (ret == OV_EREAD)
+                       return -E_OGGDEC_READ;
+               if (ret == OV_ENOTVORBIS)
+                       return -E_OGGDEC_NOTVORBIS;
+               if (ret == OV_EVERSION)
+                       return -E_OGGDEC_VERSION;
+               if (ret == OV_EBADHEADER)
+                       return -E_OGGDEC_BADHEADER;
+               if (ret < 0)
+                       return -E_OGGDEC_FAULT;
+               fn->fc->channels = ov_info(pod->vf, 0)->channels;
+               fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
+               PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
+                       fn->fc->samplerate);
+       }
+       while (fn->loaded < fn->bufsize) {
+               int length = fn->bufsize - fn->loaded;
+               long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
+                       ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
+               if (read_ret == OV_HOLE || !read_ret)
+                       return pod->converted;
+               if (read_ret < 0)
+                       return -E_OGGDEC_BADLINK;
+               fn->loaded += read_ret;
+       }
+       return pod->converted;
+}
+
+static int oggdec_parse_config(int argc, char **argv, void **config)
+{
+       int ret;
+       struct oggdec_filter_args_info *ogg_conf;
+
+       ogg_conf = para_calloc(sizeof(*ogg_conf));
+       ret = -E_OGGDEC_SYNTAX;
+       if (oggdec_cmdline_parser(argc, argv, ogg_conf))
+               goto err;
+       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (ogg_conf->bufsize_arg < 0)
+               goto err;
+       if (ogg_conf->bufsize_arg >= INT_MAX / 1024)
+               goto err;
+       if (ogg_conf->initial_buffer_arg < 0)
+               goto err;
+       if (ogg_conf->initial_buffer_arg >= INT_MAX / 1024)
+               goto err;
+       *config = ogg_conf;
+       return 1;
+err:
+       free(ogg_conf);
+       return ret;
+}
+
+/**
+ * The init function of the ogg vorbis decoder.
+ *
+ * \param f Its fields are filled in by the function.
+ */
+void oggdec_filter_init(struct filter *f)
+{
+       struct oggdec_filter_args_info dummy;
+
+       oggdec_cmdline_parser_init(&dummy);
+       f->open = ogg_open;
+       f->close = ogg_close;
+       f->convert = ogg_convert;
+       f->parse_config = oggdec_parse_config;
+       f->help = (struct ggo_help) {
+               .short_help = oggdec_filter_args_info_help,
+               .detailed_help = oggdec_filter_args_info_detailed_help
+       };
+}
index 70f73452e8600edb929feffa929549276144abdb..e2d653d7dafc8674ea95eff31197597284ae29e3 100644 (file)
@@ -1,2 +1,21 @@
-option "bufsize" b "size of output buffer" int typestr="kilobyte" default="128" optional
-option "initial_buffer" i "size of initial input buffer" int typestr="kilobyte" default="16" optional
+option "bufsize" b
+#~~~~~~~~~~~~~~~~~
+"size of output buffer"
+int typestr="kilobyte"
+default="128"
+optional
+details="
+       Increase this if you encounter output buffer overrun errors. Smaller
+       values make the oggdec filter use less memory.
+"
+
+option "initial_buffer" i
+#~~~~~~~~~~~~~~~~~~~~~~~~
+"size of initial input buffer"
+int typestr="kilobyte"
+default="16"
+optional
+details="
+       On startup, defer decoding until that many kilobytes are
+       available in the input buffer.
+"
index 164c1cb0be3a2e344ef3e74cc3e11521d64fb4c6..102fe9acb90c773d3d40c9d4939b02331c6ebe26 100644 (file)
@@ -11,6 +11,7 @@
 #include "ortp.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "ortp_recv.cmdline.h"
 
@@ -280,12 +281,19 @@ static int ortp_recv_open(struct receiver_node *rn)
  */
 void ortp_recv_init(struct receiver *r)
 {
+       struct ortp_recv_args_info dummy;
+
+       ortp_recv_cmdline_parser_init(&dummy);
        r->shutdown = ortp_shutdown;
        r->open = ortp_recv_open;
        r->close = ortp_recv_close;
        r->pre_select = ortp_recv_pre_select;
        r->post_select = ortp_recv_post_select;
        r->parse_config = ortp_recv_parse_config;
+       r->help = (struct ggo_help) {
+               .short_help = ortp_recv_args_info_help,
+               .detailed_help = ortp_recv_args_info_detailed_help
+       };
 
        ortp_init();
 }
index 192f21c273044e23764e88e137abd51a7a856094..a706d543609059792baa32504cc4d986048433eb 100644 (file)
@@ -1,4 +1,19 @@
-section "ortp options"
-option "host" i "ip or host to receive rtp packets from" string default="224.0.1.38" optional
-option "port" p "udp port." int typestr="portnumber" default="1500" optional
-option "jitter_compensation" j "set ortp's adaptive jitter compensation" int typestr="milliseconds" default="0" optional
+option "host" i
+"ip or host to receive rtp packets from"
+string default="224.0.1.38"
+optional
+details="
+       The default address resoves to DANTZ.MCAST.NET and activates
+       multicast.
+"
+
+option "port" p "udp port"
+int typestr="portnumber"
+default="1500"
+optional
+
+option "jitter_compensation" j
+"set ortp's adaptive jitter compensation"
+int typestr="milliseconds"
+default="0"
+optional
diff --git a/osl.c b/osl.c
index f06d4ba3fafa061a4e49e5d34c32648949282ff3..2aee5b6aa6e1b9e9f9361bb920933c305e0e9b5a 100644 (file)
--- a/osl.c
+++ b/osl.c
@@ -31,8 +31,9 @@
  */
 int para_lseek(int fd, off_t *offset, int whence)
 {
-       *offset = lseek(fd, *offset, whence);
        int ret = -E_LSEEK;
+
+       *offset = lseek(fd, *offset, whence);
        if (*offset == -1)
                return ret;
        return 1;
@@ -1447,8 +1448,10 @@ int osl_add_and_get_row(struct osl_table *t, struct osl_object *objects,
        goto out;
 rollback: /* rollback all changes made, ignore further errors */
        for (i--; i >= 0; i--) {
+               enum osl_storage_type st;
+
                cd = get_column_description(t->desc, i);
-               enum osl_storage_type st = cd->storage_type;
+               st = cd->storage_type;
                if (st == OSL_NO_STORAGE)
                        continue;
 
index e042665ab9eda67e95f2c5773f11f1311f468805..5e2acaa0dc6fcc2074b9686724020be56d6f822f 100644 (file)
@@ -9,6 +9,7 @@
 #include "rbtree.h"
 #include "osl.h"
 #include "string.h"
+#include "portable_io.h"
 #include "hash.h"
 
 /** Internal representation of a column of an osl table. */
index 6001b8597d8823f1c7d528439580836088b269cf..057526e30b6900b5dbc747919c43401d0aa42c88 100644 (file)
@@ -148,6 +148,12 @@ static OSStatus osx_callback(void * inClientData,
        return 0;
 }
 
+#ifdef WORDS_BIGENDIAN /* ppc */
+#define ENDIAN_FLAGS kLinearPCMFormatFlagIsBigEndian
+#else
+#define ENDIAN_FLAGS 0
+#endif
+
 static int osx_write_open(struct writer_node *wn)
 {
        struct private_osx_write_data *powd = para_calloc(
@@ -195,7 +201,7 @@ static int osx_write_open(struct writer_node *wn)
        /* flags specific to each format */
        format.mFormatFlags = kLinearPCMFormatFlagIsFloat
                | kLinearPCMFormatFlagIsPacked
-               | kLinearPCMFormatFlagIsBigEndian;
+               | ENDIAN_FLAGS;
        if (!conf->channels_given && wng->channels)
                powd->channels = *wng->channels;
        else
diff --git a/para.h b/para.h
index a39ed97df45502a0f93dcd8217cf5d9d6e792c78..218b8147add53bf6805b0a8135927a8e36faa95a 100644 (file)
--- a/para.h
+++ b/para.h
 /** used in various contexts */
 #define MAXLINE 255
 
-/** compute the minimum of \a a and \a b */
-#define PARA_MIN(a,b) ((a) < (b) ? (a) : (b))
-/** compute the maximum of \a a and \a b */
-#define PARA_MAX(a,b) ((a) > (b) ? (a) : (b))
-/** compute the absolute value of \a a */
-#define PARA_ABS(a) ((a) > 0 ? (a) : -(a))
+/** Compute the minimum of \a x and \a y. */
+#define PARA_MIN(x, y) ({ \
+       typeof(x) _min1 = (x); \
+       typeof(y) _min2 = (y); \
+       (void) (&_min1 == &_min2); \
+       _min1 < _min2 ? _min1 : _min2; })
+
+/** Compute the maximum of \a x and \a y. */
+#define PARA_MAX(x, y) ({ \
+       typeof(x) _max1 = (x); \
+       typeof(y) _max2 = (y); \
+       (void) (&_max1 == &_max2); \
+       _max1 < _max2 ? _max2 : _max1; })
+
+/** Compute the absolute value of \a x. */
+#define PARA_ABS(x) ({ \
+       typeof(x) _x = (x); \
+       _x > 0? _x : -_x; })
 
 /** debug loglevel, gets really noisy */
 #define DEBUG 1
index 5d24aeb5063a788215eca93a73698f50a68b0bc2..95bcee398d7d04b869479529f8b1c3b5eb72c516 100644 (file)
--- a/rbtree.c
+++ b/rbtree.c
@@ -438,9 +438,9 @@ struct rb_node *rb_nth(struct rb_node *node, unsigned n)
  */
 int rb_rank(struct rb_node *node, unsigned *rank)
 {
-       *rank = 1;
        struct rb_node *parent;
 
+       *rank = 1;
        if (!node)
                return -1;
        if (node->rb_left)
diff --git a/recv.c b/recv.c
index 5bce847e2dfde634595c6bb62b39bafe39bae953..52935a8840462af33589ed1378b481c593c405a1 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -12,6 +12,7 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "recv.cmdline.h"
 #include "fd.h"
@@ -27,21 +28,28 @@ INIT_STDERR_LOGGING(conf.loglevel_arg);
 /** init array of error codes used by para_recv */
 INIT_RECV_ERRLISTS;
 
-static void *parse_config(int argc, char *argv[], int *receiver_num)
+__noreturn static void print_help_and_die(void)
 {
-       int i;
+       int d = conf.detailed_help_given;
+       const char **p = d? recv_args_info_detailed_help
+               : recv_args_info_help;
+
+       printf_or_die("%s\n\n", RECV_CMDLINE_PARSER_PACKAGE "-"
+               RECV_CMDLINE_PARSER_VERSION);
+       printf_or_die("%s\n\n", recv_args_info_usage);
+       for (; *p; p++)
+               printf_or_die("%s\n", *p);
+       print_receiver_helps(d);
+       exit(0);
+}
 
+static void *parse_config(int argc, char *argv[], int *receiver_num)
+{
        if (recv_cmdline_parser(argc, argv, &conf))
                return NULL;
        HANDLE_VERSION_FLAG("recv", conf);
-       if (conf.list_receivers_given) {
-               printf("available receivers: ");
-               for (i = 0; receivers[i].name; i++)
-                       printf("%s%s", i? " " : "", receivers[i].name);
-               printf("\nTry\n\tpara_recv -r '<receivername> -h'\n"
-                       "for help on <receivername>.\n");
-               exit(EXIT_SUCCESS);
-       }
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        return check_receiver_arg(conf.receiver_arg, receiver_num);
 }
 
@@ -67,8 +75,9 @@ int main(int argc, char *argv[])
        s.default_timeout.tv_sec = 1;
        s.default_timeout.tv_usec = 0;
 
+       memset(&sot, 0, sizeof(struct stdout_task));
        memset(&rn, 0, sizeof(struct receiver_node));
-       for (ret = 0; receivers[ret].name; ret++)
+       FOR_EACH_RECEIVER(ret)
                receivers[ret].init(&receivers[ret]);
        ret = -E_RECV_SYNTAX;
        rn.conf = parse_config(argc, argv, &receiver_num);
index febe1a9cea5de7b6dd5e2a9d45a7accb253660b7..b7003b0e5b2bacde7b68c1028b39ca015e9105bd 100644 (file)
--- a/recv.ggo
+++ b/recv.ggo
@@ -1,11 +1,19 @@
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "list_receivers" L "print list of available receivers" flag off
-option "receiver" r "Select receiver.
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+"set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
 
-Any options for the selected receiver must
-be quoted. Example:
+option "receiver" r
+"Select receiver"
+string typestr="receiver_spec"
+default="http"
+optional
+details="
+       Any options for the selected receiver must
+       be quoted. Example:
 
-       -r 'http -i www.paraslash.org  -p 8009'
+               -r 'http -i www.paraslash.org -p 8009'
 "
 
-string typestr="receiver_spec" default="http" optional
diff --git a/recv.h b/recv.h
index d6c012b649a545da6c572afbb48a9ec572351b01..3aed018042d8217f5483197b819bb2ac43d67205 100644 (file)
--- a/recv.h
+++ b/recv.h
@@ -104,6 +104,8 @@ struct receiver {
         * \sa select(2), struct receiver.
         */
        void (*post_select)(struct sched *s, struct task *t);
+
+       struct ggo_help help;
 };
 
 
@@ -130,4 +132,7 @@ extern struct receiver receivers[];
        ORTP_RECEIVER \
        {.name = NULL}};
 
+#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++)
+
 void *check_receiver_arg(char *ra, int *receiver_num);
+void print_receiver_helps(int detailed);
index c8f97c9e9d61ef914da8559bf7b10ce6fb3e4542..5f2412b5a0b02c0a40b479ed4ed635c43adb617e 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "recv.h"
 #include "string.h"
 
@@ -83,3 +84,21 @@ void *check_receiver_arg(char *ra, int *receiver_num)
        PARA_ERROR_LOG("receiver not found\n");
        return NULL;
 }
+
+void print_receiver_helps(int detailed)
+{
+       int i;
+
+       printf_or_die("\nAvailable receivers: \n\t");
+       FOR_EACH_RECEIVER(i)
+               printf_or_die("%s%s", i? " " : "", receivers[i].name);
+       printf_or_die("\n\n");
+       FOR_EACH_RECEIVER(i) {
+               struct receiver *r = receivers + i;
+
+               if (!r->help.short_help)
+                       continue;
+               printf_or_die("Options for %s:\n", r->name);
+               ggo_print_help(&r->help, detailed);
+       }
+}
diff --git a/send.h b/send.h
index 270affd5279d5bb3c1a00a05833c0859028a0c1f..2fe1cc56ad0c35f773dfc608b8cfd628baf33417 100644 (file)
--- a/send.h
+++ b/send.h
@@ -107,7 +107,7 @@ struct sender_client {
 
 /** Describes the current status of one paraslash sender. */
 struct sender_status {
-       /* The file descriptor of the socket this sender is listening on. */
+       /** The file descriptor of the socket this sender is listening on. */
        int listen_fd;
        /** The TCP/DCCP port used by this sender. */
        int port;
index c0098b98a833f0cae309aba11a43bec1c2c7d1a6..3d87008850a8ab3c39b3f1711d5aca1b2e2f3a45 100644 (file)
@@ -62,6 +62,8 @@ static int open_sender(unsigned l4type, int port)
  * Close the file descriptor given by \a sc, remove it from the close-on-fork
  * list, destroy the chunk queue of this client, delete the client from the
  * list of connected clients and free the sender_client struct.
+ *
+ * \sa shutdown_clients().
  */
 void shutdown_client(struct sender_client *sc, struct sender_status *ss)
 {
@@ -76,6 +78,14 @@ void shutdown_client(struct sender_client *sc, struct sender_status *ss)
        ss->num_clients--;
 }
 
+/**
+ * Shut down all clients connected to a paraslash sender.
+ *
+ * \param ss The sender whose clients are to be shut down.
+ *
+ * This just loops over all connected clients and calls shutdown_client()
+ * for each client.
+ */
 void shutdown_clients(struct sender_status *ss)
 {
        struct sender_client *sc, *tmp;
index 915687649f7f84c8a572b2257a7439498d31c873..0924ace4b07eac452a193df49c4b48bf6c8562aa 100644 (file)
--- a/server.c
+++ b/server.c
@@ -23,8 +23,8 @@
  * The gory details, listed by topic:
  *
  *     - Audio format handlers: \ref send_common.c \ref mp3_afh.c, \ref ogg_afh.c, \ref aac_afh.c,
- *     - Decoders: \ref mp3dec.c, \ref oggdec.c, \ref aacdec.c,
- *     - Volume normalizer: \ref compress.c,
+ *     - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c, \ref aacdec_filter.c,
+ *     - Volume normalizer: \ref compress_filter.c,
  *     - Output: \ref alsa_write.c, \ref osx_write.c,
  *     - http: \ref http_recv.c, \ref http_send.c,
  *     - ortp: \ref ortp_recv.c, \ref ortp_send.c,
@@ -136,7 +136,7 @@ struct server_command_task {
  * \param ll The log level.
  * \param fmt The format string describing the log message.
  */
-void para_log(int ll, const char* fmt,...)
+__printf_2_3 void para_log(int ll, const char* fmt,...)
 {
        va_list argp;
        FILE *outfd;
index 012e1041104d9c98e7394a691e340029264a33a3..9f3912032eeb8ce1ca420a8a3e0193fbe45eb58f 100644 (file)
@@ -162,9 +162,8 @@ option "afs_database_dir" D
 string typestr="path"
 optional
 details="
-       Where para_server should look for the  osl
-       database of the audio file selector. The default is
-       '~/.paraslash/afs_database'.
+       Where para_server should look for the osl database of the audio
+       file selector. The default is '~/.paraslash/afs_database'.
 "
 
 option "afs_socket" s
index f8be2f4612002214f669fa74ce92390866b0dc90..9168e918205a5f6733761db31ea7fc6573b2bbae 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -57,8 +57,15 @@ err_out:
  */
 static void generic_signal_handler(int s)
 {
-       write(signal_pipe[1], &s, sizeof(int));
-       //fprintf(stderr, "got sig %i, write returned %d\n", s, ret);
+       ssize_t ret = write(signal_pipe[1], &s, sizeof(int));
+
+       if (ret == sizeof(int))
+               return;
+       if (ret < 0)
+               PARA_EMERG_LOG("%s\n", strerror(errno));
+       else
+               PARA_EMERG_LOG("short write to signal pipe\n");
+       exit(EXIT_FAILURE);
 }
 
 /**
diff --git a/stdin.c b/stdin.c
index 5b6bf520cb6b09f54e6dc30cb3f21c8d7118ba63..0b84cdc72011e369bf290f671ae96d6e8cab8569 100644 (file)
--- a/stdin.c
+++ b/stdin.c
 static void stdin_pre_select(struct sched *s, struct task *t)
 {
        struct stdin_task *sit = container_of(t, struct stdin_task, task);
+
+       if (sit->output_error && *sit->output_error < 0) {
+               t->error = *sit->output_error;
+               return;
+       }
        t->error = 0;
        sit->check_fd = 0;
        if (sit->loaded >= sit->bufsize)
@@ -55,6 +60,10 @@ static void stdin_post_select(struct sched *s, struct task *t)
        struct stdin_task *sit = container_of(t, struct stdin_task, task);
        ssize_t ret;
 
+       if (sit->output_error && *sit->output_error < 0) {
+               t->error = *sit->output_error;
+               return;
+       }
        t->error = 0;
        if (!sit->check_fd)
                return;
@@ -89,6 +98,7 @@ void stdin_set_defaults(struct stdin_task *sit)
        ret = mark_fd_nonblocking(STDIN_FILENO);
        if (ret >= 0)
                return;
+       sit->output_error = NULL;
        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
 }
diff --git a/stdin.h b/stdin.h
index 63d00d5dfc167a5c29a52ba4946bba49926d1301..99233f9ffbbd79b62035371cfed88510e36cb914 100644 (file)
--- a/stdin.h
+++ b/stdin.h
@@ -14,6 +14,8 @@ struct stdin_task {
        size_t bufsize;
        /** Number of bytes currently loaded in \a buf. */
        size_t loaded;
+       /** Pointer to the error member of the consumer. */
+       int *output_error;
        /** Whether \p STDIN_FILENO was included in the read fd set. */
        int check_fd;
        /** The task structure. */
index c73ec1e6d70377b24bafcb9462035067099fc945..bdc113d3586c63059311968275f6134f5a9037b9 100644 (file)
--- a/string.c
+++ b/string.c
@@ -59,9 +59,10 @@ __must_check __malloc void *para_realloc(void *p, size_t size)
  */
 __must_check __malloc void *para_malloc(size_t size)
 {
-       assert(size);
-       void *p = malloc(size);
+       void *p;
 
+       assert(size);
+       p = malloc(size);
        if (!p) {
                PARA_EMERG_LOG("malloc failed (size = %zu),  aborting\n",
                        size);
diff --git a/time.c b/time.c
index 1fd7b0981584fc4557079ee62a87fbd713554f3a..0ebc6ec0c8f3f10e66f77ffe18d0f8fe05d7210a 100644 (file)
--- a/time.c
+++ b/time.c
@@ -170,6 +170,16 @@ int tv_convex_combination(const long a, const struct timeval *tv1,
        return ret;
 }
 
+/**
+ * Compute when to send a chunk of an audio file.
+ *
+ * \param chunk_num The number of the chunk.
+ * \param chunk_tv The duration of one chunk.
+ * \param stream_start When the first chunk was sent.
+ * \param result The time when to send chunk number \a chunk_num.
+ *
+ * This function computes stream_start + chunk_num * chunk_time.
+ */
 void compute_chunk_time(long unsigned chunk_num,
                struct timeval *chunk_tv, struct timeval *stream_start,
                struct timeval *result)
diff --git a/versions/paraslash-0.3.3.tar.bz2 b/versions/paraslash-0.3.3.tar.bz2
new file mode 100644 (file)
index 0000000..40834de
Binary files /dev/null and b/versions/paraslash-0.3.3.tar.bz2 differ
diff --git a/versions/paraslash-0.3.3.tar.bz2.asc b/versions/paraslash-0.3.3.tar.bz2.asc
new file mode 100644 (file)
index 0000000..2392f88
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQBJM93LWto1QDEAkw8RAkqoAJ4otG8p97XWBP7x2o1zy1R3M80BVQCfZy7K
+WxcDbEGOvK1Vk0zhGPWr7gU=
+=Ni2V
+-----END PGP SIGNATURE-----
diff --git a/vss.c b/vss.c
index f92e15e0d653ada1f541975876d77384ba3c5b57..4ed75792185f685e26db278ae88b49ff4d2cd59f 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -418,7 +418,6 @@ static void vss_send_chunk(struct vss_task *vsst)
        if (chk_barrier("data send", &vsst->data_send_barrier,
                        &due, 1) < 0)
                return;
-       mmd->new_vss_status_flags &= ~VSS_REPOS;
        if (mmd->current_chunk >= mmd->afd.afhi.chunks_total) { /* eof */
                mmd->new_vss_status_flags |= VSS_NEXT;
                return;
@@ -502,6 +501,7 @@ void init_vss_task(int afs_socket)
        free(hn);
        free(home);
        mmd->sender_cmd_data.cmd_num = -1;
+       make_empty_status_items(mmd->afd.verbose_ls_output);
        if (conf.autoplay_given) {
                struct timeval tmp;
                mmd->vss_status_flags |= VSS_PLAYING;
diff --git a/wav.c b/wav.c
deleted file mode 100644 (file)
index 52a97af..0000000
--- a/wav.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file wav.c a filter that inserts a wave header */
-
-#include "para.h"
-
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "string.h"
-
-/** size of the output buffer */
-#define WAV_OUTBUF_SIZE 81920
-/** a wav header is always 44 bytes */
-#define WAV_HEADER_LEN 44
-/** always write 16 bit header */
-#define BITS 16
-
-/**
- * write a 32 bit unsigned value to a buffer
- *
- * \param buf the buffer to write to
- * \param x the value to write
- */
-#define WRITE_U32(buf, x) (buf)[0] = (unsigned char)((x) & 0xff);\
-       (buf)[1] = (unsigned char)(((x) >> 8) & 0xff);\
-       (buf)[2] = (unsigned char)(((x) >> 16) & 0xff);\
-       (buf)[3] = (unsigned char)(((x) >> 24) & 0xff);
-
-/**
- * write a 16 bit unsigned value to a buffer
- *
- * \param buf the buffer to write to
- * \param x the value to write
- */
-#define WRITE_U16(buf, x) (buf)[0] = (unsigned char)((x) & 0xff);
-
-static void make_wav_header(unsigned int channels, unsigned int samplerate,
-               struct filter_node *fn)
-{
-
-       char *headbuf = fn->buf;
-       unsigned int size = 0x7fffffff;
-       int bytespersec = channels * samplerate * BITS / 8;
-       int align = channels * BITS / 8;
-
-       PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, samplerate);
-       memset(headbuf, 0, WAV_HEADER_LEN);
-       memcpy(headbuf, "RIFF", 4);
-       WRITE_U32(headbuf + 4, size - 8);
-       memcpy(headbuf + 8, "WAVE", 4);
-       memcpy(headbuf + 12, "fmt ", 4);
-       WRITE_U32(headbuf + 16, 16);
-       WRITE_U16(headbuf + 20, 1);     /* format */
-       WRITE_U16(headbuf + 22, channels);
-       WRITE_U32(headbuf + 24, samplerate);
-       WRITE_U32(headbuf + 28, bytespersec);
-       WRITE_U16(headbuf + 32, align);
-       WRITE_U16(headbuf + 34, BITS);
-       memcpy(headbuf + 36, "data", 4);
-       WRITE_U32(headbuf + 40, size - 44);
-}
-
-static ssize_t wav_convert(char *inbuf, size_t len, struct filter_node *fn)
-{
-       size_t copy;
-       int *bof = fn->private_data;
-
-       if (*bof) {
-               make_wav_header(fn->fc->channels, fn->fc->samplerate, fn);
-               fn->loaded = WAV_HEADER_LEN;
-               *bof = 0;
-//             return 0;
-       }
-       copy = PARA_MIN(len, fn->bufsize - fn->loaded);
-       memmove(fn->buf + fn->loaded, inbuf, copy);
-       fn->loaded += copy;
-//     PARA_DEBUG_LOG("len = %d, copy = %d\n", len, copy);
-       return copy;
-}
-
-static void wav_close(struct filter_node *fn)
-{
-       free(fn->buf);
-       fn->buf = NULL;
-       free(fn->private_data);
-       fn->private_data = NULL;
-}
-
-static void wav_open(struct filter_node *fn)
-{
-       int *bof;
-
-       fn->bufsize = WAV_OUTBUF_SIZE;
-       fn->buf = para_malloc(fn->bufsize);
-       fn->private_data = para_malloc(sizeof(int));
-       bof = fn->private_data;
-       *bof = 1;
-       PARA_INFO_LOG("wav filter node: %p, output buffer: %p, loaded: %zd\n",
-               fn, fn->buf, fn->loaded);
-}
-
-/**
- * the init function of the wav filter
- *
- * \param f struct to initialize
- */
-void wav_init(struct filter *f)
-{
-       f->convert = wav_convert;
-       f->close = wav_close;
-       f->open = wav_open;
-}
diff --git a/wav_filter.c b/wav_filter.c
new file mode 100644 (file)
index 0000000..d30b018
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file wav_filter.c A filter that inserts a wave header. */
+
+#include "para.h"
+
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "string.h"
+#include "portable_io.h"
+
+/** size of the output buffer */
+#define WAV_OUTBUF_SIZE 81920
+/** a wav header is always 44 bytes */
+#define WAV_HEADER_LEN 44
+/** always write 16 bit header */
+#define BITS 16
+
+static void make_wav_header(unsigned int channels, unsigned int samplerate,
+               struct filter_node *fn)
+{
+
+       char *headbuf = fn->buf;
+       unsigned int size = 0x7fffffff;
+       int bytespersec = channels * samplerate * BITS / 8;
+       int align = channels * BITS / 8;
+
+       assert(channels);
+       PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, samplerate);
+       memset(headbuf, 0, WAV_HEADER_LEN);
+       memcpy(headbuf, "RIFF", 4);
+       write_u32(headbuf + 4, size - 8);
+       memcpy(headbuf + 8, "WAVE", 4);
+       memcpy(headbuf + 12, "fmt ", 4);
+       write_u32(headbuf + 16, 16);
+       write_u16(headbuf + 20, 1);     /* format */
+       write_u16(headbuf + 22, channels);
+       write_u32(headbuf + 24, samplerate);
+       write_u32(headbuf + 28, bytespersec);
+       write_u16(headbuf + 32, align);
+       write_u16(headbuf + 34, BITS);
+       memcpy(headbuf + 36, "data", 4);
+       write_u32(headbuf + 40, size - 44);
+}
+
+static ssize_t wav_convert(char *inbuf, size_t len, struct filter_node *fn)
+{
+       size_t copy;
+       int *bof = fn->private_data;
+
+       if (*bof) {
+               if (!len)
+                       return 0;
+               make_wav_header(fn->fc->channels, fn->fc->samplerate, fn);
+               fn->loaded = WAV_HEADER_LEN;
+               *bof = 0;
+//             return 0;
+       }
+       copy = PARA_MIN(len, fn->bufsize - fn->loaded);
+       memmove(fn->buf + fn->loaded, inbuf, copy);
+       fn->loaded += copy;
+//     PARA_DEBUG_LOG("len = %d, copy = %d\n", len, copy);
+       return copy;
+}
+
+static void wav_close(struct filter_node *fn)
+{
+       free(fn->buf);
+       fn->buf = NULL;
+       free(fn->private_data);
+       fn->private_data = NULL;
+}
+
+static void wav_open(struct filter_node *fn)
+{
+       int *bof;
+
+       fn->bufsize = WAV_OUTBUF_SIZE;
+       fn->buf = para_malloc(fn->bufsize);
+       fn->private_data = para_malloc(sizeof(int));
+       bof = fn->private_data;
+       fn->loaded = 0;
+       *bof = 1;
+       PARA_INFO_LOG("wav filter node: %p, output buffer: %p, loaded: %zd\n",
+               fn, fn->buf, fn->loaded);
+}
+
+/**
+ * the init function of the wav filter
+ *
+ * \param f struct to initialize
+ */
+void wav_filter_init(struct filter *f)
+{
+       f->convert = wav_convert;
+       f->close = wav_close;
+       f->open = wav_open;
+}
index 86a53872ffd0067d42138e34356ca500f445da3d..ac3d3cd0566a9e7fc7bfe4a7331ba333de57c189 100644 (file)
@@ -1,6 +1,10 @@
 <h1>Events</h1>
 <hr>
 <ul>
+       <li>2008-12-01: <a href="versions/paraslash-0.3.3.tar.bz2">paraslash-0.3.3</a>
+               <a href="versions/paraslash-0.3.3.tar.bz2.asc">(sig)</a>
+               "axiomatic perspectivity"
+       </li>
        <li>2008-04-11: <a href="versions/paraslash-0.3.2.tar.bz2">paraslash-0.3.2</a>
                <a href="versions/paraslash-0.3.2.tar.bz2.asc">(sig)</a>
                "probabilistic parity"
diff --git a/write.c b/write.c
index 140e80e54ca6a94372460393d0c6153b332b45bb..9b7a92113e69ff236e08ce235ca306c9c410fd07 100644 (file)
--- a/write.c
+++ b/write.c
@@ -14,6 +14,7 @@
 #include "write.cmdline.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "stdin.h"
 #include "write.h"
 #include "write_common.h"
@@ -34,6 +35,7 @@ struct check_wav_task {
        unsigned channels;
        /** Sample rate specified in wav header given by \a buf. */
        unsigned samplerate;
+       /** The task structure used by the scheduler. */
        struct task task;
 };
 
@@ -105,14 +107,19 @@ static void initial_delay_pre_select(struct sched *s, struct task *t)
 
        if (!idt->start_time.tv_sec && !idt->start_time.tv_usec) {
                t->error = -E_NO_DELAY;
-               return;
+               goto register_check_wav;
        }
        if (tv_diff(now, &idt->start_time, &diff) > 0) {
                t->error = -E_DELAY_TIMEOUT;
-               return;
+               goto register_check_wav;
        }
        if (tv_diff(&s->timeout , &diff, NULL) > 0)
                s->timeout = diff;
+       return;
+register_check_wav:
+       register_task(&the_check_wav_task.task);
+       s->timeout.tv_sec = 0;
+       s->timeout.tv_usec = 1;
 }
 
 INIT_STDERR_LOGGING(conf.loglevel_arg)
@@ -123,20 +130,6 @@ static struct writer_node_group *check_args(void)
        struct writer_node_group *g = NULL;
        struct initial_delay_task *idt = &the_initial_delay_task;
 
-       if (conf.list_writers_given) {
-               char *msg = NULL;
-               FOR_EACH_WRITER(i) {
-                       char *tmp = make_message("%s%s%s",
-                               i? msg : "",
-                               i? " " : "",
-                               writer_names[i]);
-                       free(msg);
-                       msg = tmp;
-               }
-               fprintf(stderr, "%s\n", msg);
-               free(msg);
-               exit(EXIT_SUCCESS);
-       }
        if (conf.start_time_given) {
                long unsigned sec, usec;
                if (sscanf(conf.start_time_arg, "%lu:%lu",
@@ -168,6 +161,21 @@ out:
        return NULL;
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       int d = conf.detailed_help_given;
+       const char **p = d? write_args_info_detailed_help
+               : write_args_info_help;
+
+       printf_or_die("%s\n\n", WRITE_CMDLINE_PARSER_PACKAGE "-"
+               WRITE_CMDLINE_PARSER_VERSION);
+       printf_or_die("%s\n\n", write_args_info_usage);
+       for (; *p; p++)
+               printf_or_die("%s\n", *p);
+       print_writer_helps(d);
+       exit(0);
+}
+
 /**
  * Para_write's main function.
  *
@@ -186,16 +194,22 @@ int main(int argc, char *argv[])
        struct check_wav_task *cwt = &the_check_wav_task;
        struct initial_delay_task *idt = &the_initial_delay_task;
 
+       init_supported_writers();
        write_cmdline_parser(argc, argv, &conf);
        HANDLE_VERSION_FLAG("write", conf);
-       init_supported_writers();
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
 
        wng = check_args();
        if (!wng)
                goto out;
        stdin_set_defaults(&sit);
-       if (conf.bufsize_given)
-               sit.bufsize = conf.bufsize_arg;
+       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (conf.bufsize_arg < 0)
+               goto out;
+       if (conf.bufsize_arg >= INT_MAX / 1024)
+               goto out;
+       sit.bufsize = conf.bufsize_arg * 1024;
        sit.buf = para_malloc(sit.bufsize);
 
        wng->buf = sit.buf;
@@ -209,7 +223,6 @@ int main(int argc, char *argv[])
        cwt->input_error = &sit.task.error;
        sprintf(cwt->task.status, "check wav");
        cwt->task.pre_select = check_wav_pre_select;
-       register_task(&cwt->task);
 
        idt->task.pre_select = initial_delay_pre_select;
        sprintf(idt->task.status, "initial_delay");
index 1ea8abf29265d9bc6c569f7fbeda5734cd70e972..8f557bb9de6ae1284a2e7e284e618cf75d4995bf 100644 (file)
--- a/write.ggo
+++ b/write.ggo
@@ -1,45 +1,35 @@
-section "general options"
-#########################
-
-option "list_writers" L
-#~~~~~~~~~~~~~~~~~~~~~~
-"print available writers and exit"
-
-       flag off
-
 option "loglevel" l
 #~~~~~~~~~~~~~~~~~~
 "set loglevel (0-6)"
-
-       int typestr="level"
-       default="4"
-       optional
+int typestr="level"
+default="4"
+optional
 
 option "bufsize" b
 #~~~~~~~~~~~~~~~~~
 "input buffer size"
-
-       int typestr="kilobytes"
-       default="64"
-       optional
+int typestr="kilobytes"
+default="64"
+optional
 
 option "writer" w
 #~~~~~~~~~~~~~~~~
-
-"select stream writer
-may be give multiple times. The same writer
-may be specified more than once"
-
-       string typestr="name"
-       default="alsa (file if alsa is unsupported)"
-       optional
-       multiple
+"select stream writer"
+string typestr="name"
+default="alsa (file if alsa is unsupported)"
+optional
+multiple
+details="
+       May be give multiple times. The same writer may be specified
+       more than once.
+"
 
 option "start_time" t
 #~~~~~~~~~~~~~~~~~~~~
-"start playback at given time which must be
-in a:b format where a denotes seconds and b
-denotes microseconds since the epoch"
-
-       string typestr="timeval"
-       optional
+"defer playback"
+string typestr="timeval"
+optional
+details="
+       Start playback at given time which must be in a:b format where
+       a denotes seconds and b denotes microseconds since the epoch.
+"
diff --git a/write.h b/write.h
index 091f8c77181cbf3f1a014e31db2667b534aa5b0d..7df62f9f08e68090afe45cb09ec43dde024b7fab 100644 (file)
--- a/write.h
+++ b/write.h
@@ -4,54 +4,49 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file write.h writer-related structures */
+/** \file write.h Writer-related structures. */
 
-/** the list of supported writers */
+/** The list of supported writers. */
 enum writer_enum {WRITER_ENUM};
 
 /**
  * Describes one running instance of a writer.
  */
 struct writer_node {
-       /** points to the writer structure associated with this node */
-       struct writer *writer; /* FIXME: Should better be only the number */
-       /** writer-specific data */
+       /** Points to the writer structure associated with this node. */
+       struct writer *writer; /* FIXME: Should better be only the number. */
+       /** Writer-specific data. */
        void *private_data;
-       /** send that many bytes in one go */
+       /** Send that many bytes in one go. */
        int chunk_bytes;
-       /** pointer to the group this node belongs to */
+       /** Pointer to the group this node belongs to. */
        struct writer_node_group *wng;
-       /** the writer-specific configuration of this node */
+       /** The writer-specific configuration of this node. */
        void *conf;
-       /** how much of the wng's buffer is already written */
+       /** How much of the wng's buffer is already written. */
        size_t written;
 };
 
-/** describes one supported writer */
+/** Describes one supported writer. */
 struct writer {
        /**
-        * the init function of the writer
+        * The init function of the writer.
         *
         * It must fill in all other function pointers of the given
         * writer structure.
-        *
         */
        void (*init)(struct writer *w);
        /**
-        *
-        *
-        * the command line parser of the writer
+        * The command line parser of the writer.
         *
         * It should check whether the command line options given by \a options are
         * valid.  On success, it should return a pointer to the writer-specific
         * configuration data determined by \a options.  Note that this might be called
         * more than once with different values of \a options.
-        *
         */
-               void * (*parse_config)(const char *options);
+       void *(*parse_config)(const char *options);
        /**
-        *
-        * open one instance of this writer
+        * Open one instance of this writer.
         *
         * This function should perform any work necessary to write the incoming
         * stream. If To this aim, it may allocate its private data structure and store
@@ -59,8 +54,7 @@ struct writer {
         */
        int (*open)(struct writer_node *);
        /**
-        *
-        * write a chunk of audio data
+        * Prepare the fd sets for select.
         *
         * This is called from the writer node group task's pre_select(). It
         * may use the sched pointer to add any file descriptors or to decrease
@@ -69,64 +63,66 @@ struct writer {
         */
        int (*pre_select)(struct sched *s, struct writer_node *wn);
        /**
+        * Write audio data.
+        *
         * Called from the post_select function of the wng task. It must keep
         * track of the number of bytes consumed from the wng's buffer via
-        * the wn->written variable (which may be modified by the wng handling
+        * the \p wn->written variable (which may be modified by the wng handling
         * functions). This function must return positive on success and
         * negative on errors.
         */
        int (*post_select)(struct sched *s, struct writer_node *wn);
        /**
-        * close one instance of the writer
+        * Close one instance of the writer.
         *
         * This function is assumed to succeed.
         */
        void (*close)(struct writer_node *);
        /**
-        * shutdown the writer
+        * Shutdown the writer
         *
-        * This is a optional function pointer used for cleaning
-        * up.
+        * This is a optional function pointer used for cleaning up.
         */
        void (*shutdown)(struct writer_node *);
+       struct ggo_help help;
 };
 
 /**
- * describes a set of writer nodes that all write the same stream.
+ * Describes a set of writer nodes that all write the same stream.
  */
 struct writer_node_group {
-       /** number of nodes belonging to this group */
+       /** Number of nodes belonging to this group. */
        unsigned num_writers;
-       /** array of pointers to the corresponding writer nodes */
+       /** Array of pointers to the corresponding writer nodes. */
        struct writer_node *writer_nodes;
-       /** the maximum of the chunk_bytes values of the writer nodes in this group */
-       size_t max_chunk_bytes;
+       /** The maximum of the chunk_bytes values of the writer nodes in this group. */
+       int max_chunk_bytes;
        /** Non-zero if an error or end of file was encountered by the feeding task. */
        int *input_error;
-       /** current output buffer */
+       /** Current output buffer. */
        char *buf;
-       /** number of bytes loaded in the output buffer */
+       /** Number of bytes loaded in the output buffer. */
        size_t *loaded;
-       /** number of audio channels of the current stream */
+       /** Number of audio channels of the current stream. */
        unsigned int *channels;
-       /** sample rate of the current stream */
+       /** Sample rate of the current stream. */
        unsigned int *samplerate;
-       /** the task associated to this group */
+       /** The task associated to this group. */
        struct task task;
        /** Whether the group is open, i.e. wng_open() was called. */
        int open;
 };
 
-/** loop over each writer node in a writer group */
+/** Loop over each writer node in a writer group. */
 #define FOR_EACH_WRITER_NODE(i, wng) for (i = 0; i < (wng)->num_writers; i++)
-/** loop over each supported writer */
+/** Loop over each supported writer. */
 #define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
 
-/** declare the init functions of all supported writers */
+/** Declare the init functions of all supported writers. */
 DECLARE_WRITER_INITS;
 
-/** array containing the name of each writer */
+/** Array containing the name of each writer. */
 extern const char *writer_names[];
 
-/** the writer structure for each supported writer */
+/** The writer structure for each supported writer. */
 extern struct writer writers[NUM_SUPPORTED_WRITERS];
index 3bf5d1199f560e9ce93b7d97a06e278ce495591f..5896d91c4d415ee18989574b8e10c68cdf990f3b 100644 (file)
@@ -10,6 +10,7 @@
 #include "string.h"
 #include "list.h"
 #include "sched.h"
+#include "ggo.h"
 #include "write.h"
 #include "error.h"
 
@@ -210,3 +211,21 @@ struct writer_node_group *setup_default_wng(void)
        wng->writer_nodes[0].conf = writers[DEFAULT_WRITER].parse_config("");
        return wng;
 }
+
+void print_writer_helps(int detailed)
+{
+       int i;
+
+       printf_or_die("\nAvailable writers: \n\t");
+       FOR_EACH_WRITER(i)
+               printf_or_die("%s%s", i? " " : "", writer_names[i]);
+       printf_or_die("\n\n");
+       FOR_EACH_WRITER(i) {
+               struct writer *w = writers + i;
+
+               if (!w->help.short_help)
+                       continue;
+               printf_or_die("Options for %s:\n", writer_names[i]);
+               ggo_print_help(&w->help, detailed);
+       }
+}
index 49938fe664ceb1d4a7f5df4388c27aab83925be3..7f0bbc57501dc7416243e8b92e580daf742b5cc6 100644 (file)
@@ -9,7 +9,7 @@
 int wng_open(struct writer_node_group *g);
 void wng_close(struct writer_node_group *g);
 struct writer_node_group *wng_new(unsigned num_writers);
-void wng_unregister(struct writer_node_group *g);
 void init_supported_writers(void);
 void *check_writer_arg(const char *wa, int *writer_num);
 struct writer_node_group *setup_default_wng(void);
+void print_writer_helps(int detailed);