]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/opus'
authorAndre Noll <maan@systemlinux.org>
Sat, 6 Jul 2013 00:24:48 +0000 (02:24 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 6 Jul 2013 00:27:58 +0000 (02:27 +0200)
Was cooking for two weeks with no problems.

273756 The opus decoder.
7007ae The opus audio format handler.
8bcf75 ogg/opus: Infrastructure.
53133e speex: Don't export spx_ctl().

80 files changed:
Makefile.in
NEWS
afh.c
afh_recv.c
afs.cmd
alsa_write.c
amp_filter.c
ao_write.c
audioc.c
audiod.c
audiod_command.c
client.c
client_common.c
command.c
compress_filter.c
configure.ac
dccp_recv.c
error.h
fade.c
file_write.c
filter.c
filter.h
filter_common.c
ggo.c
ggo.h
gui.c
http_recv.c
interactive.c
m4/gengetopt/afh.m4
m4/gengetopt/afh_recv.m4
m4/gengetopt/alsa_write.m4
m4/gengetopt/amp_filter.m4
m4/gengetopt/ao_write.m4
m4/gengetopt/audioc.m4
m4/gengetopt/audiod.m4
m4/gengetopt/client.m4
m4/gengetopt/compress_filter.m4
m4/gengetopt/dccp_recv.m4
m4/gengetopt/fade.m4
m4/gengetopt/file_write.m4
m4/gengetopt/filter.m4
m4/gengetopt/gui.m4
m4/gengetopt/http_recv.m4
m4/gengetopt/makefile
m4/gengetopt/mp3dec_filter.m4
m4/gengetopt/oss_write.m4
m4/gengetopt/osx_write.m4
m4/gengetopt/play.m4
m4/gengetopt/prebuffer_filter.m4
m4/gengetopt/recv.m4
m4/gengetopt/resample_filter.m4
m4/gengetopt/server.m4
m4/gengetopt/udp_recv.m4
m4/gengetopt/write.m4
mood.c
mp3dec_filter.c
oss_mix.c
oss_write.c
osx_write.c
play.c
playlist.c
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
server.c
stdin.c
stdin.h
stdout.c
stdout.h
string.c
string.h
udp_recv.c
version.c [new file with mode: 0644]
version.h
web/download.in.html
write.c
write_common.c
write_common.h

index f3dfea0065c31a564156000d0b1842eedc24e6c7..f82afd7b88f65a9ad2d75032ca401d0445025272 100644 (file)
@@ -9,6 +9,7 @@ PACKAGE_VERSION := @PACKAGE_VERSION@
 PACKAGE_STRING := @PACKAGE_STRING@
 install_sh := @install_sh@
 executables := @executables@
+ggo_descriptions_declared := @ggo_descriptions_declared@
 
 GENGETOPT := @gengetopt@
 HELP2MAN := @help2man@
@@ -18,7 +19,6 @@ 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 := spectral gravity
 
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
 
@@ -67,7 +67,6 @@ CPPFLAGS += -Wchar-subscripts
 CPPFLAGS += -DBINDIR='"$(BINDIR)"'
 CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
 CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
-CPPFLAGS += -DCODENAME='"$(codename)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -Werror-implicit-function-declaration
 CPPFLAGS += -Wmissing-noreturn
@@ -182,6 +181,10 @@ $(object_dir)/mp3dec_filter.o: mp3dec_filter.c | $(object_dir)
        @[ -z "$(Q)" ] || echo 'CC $<'
        $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $<
 
+$(object_dir)/compress_filter.o: compress_filter.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) -O3 $<
+
 $(object_dir)/aacdec_filter.o: aacdec_filter.c | $(object_dir)
        @[ -z "$(Q)" ] || echo 'CC $<'
        $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
diff --git a/NEWS b/NEWS
index 56f84753314b71f7f41b193eb4e7a6df6ce8d82f..19c98a4be597bf6d156ce29006f11944b7b7903c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,23 @@
 ------------------------------------------
 0.?.? (to be announced) "spectral gravity"
 ------------------------------------------
+A new audio format, UTF-8 support, and tons of other improvements
+and fixes all over the place.
 
+       - New audio format: ogg/opus.
        - UTF8 support for para_gui and the mp3 audio format handler.
        - Scheduler improvements and fixes.
        - The obsolete gettimeofday() function has been replaced
          by clock_gettime() on systems which support it.
+       - Speed and usability improvements for para_gui.
+       - para_client now restores the fd flags of stdin and stdout
+         on shutdown.
+       - Improved manual pages.
+       - Consistent version strings for all executables.
+       - Reduced depencies on generated files result in fewer
+         recompilations on changes.
+       - Performance improvements for the compress filter.
+       - Improved downloads web page.
 
 -----------------------------------------
 0.4.12 (2012-12-20) "volatile relativity"
diff --git a/afh.c b/afh.c
index 4c65d7c1f76bdc4b054461127d14b9547881d6f7..3881955d3ff14c757eef89a6330ccb95133cb09f 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -15,6 +15,7 @@
 #include "afh.h"
 #include "error.h"
 #include "version.h"
+#include "ggo.h"
 
 static struct afh_args_info conf;
 INIT_AFH_ERRLISTS;
@@ -56,6 +57,17 @@ static void print_chunk_table(struct afh_info *afhi)
        }
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(afh);
+       int d = conf.detailed_help_given;
+       unsigned flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+
+       ggo_print_help(&h, flags);
+       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+       exit(EXIT_SUCCESS);
+}
+
 /**
  * The main function of para_afh.
  *
@@ -72,8 +84,11 @@ int main(int argc, char **argv)
        struct afh_info afhi;
 
        afh_cmdline_parser(argc, argv, &conf);
-       HANDLE_VERSION_FLAG("afh", conf);
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("afh", conf.version_given);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
+       afh_init();
        ret = -E_AFH_SYNTAX;
        if (conf.inputs_num == 0)
                goto out;
index 774ae88c14fd048a9c3a6f1eff18d322b8090352..5c17dab7de21132cf48d50bfaf37eabba506ce6f 100644 (file)
@@ -70,10 +70,8 @@ static void *afh_recv_parse_config(int argc, char **argv)
 {
        struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp));
 
-       if (!afh_recv_cmdline_parser(argc, argv, tmp))
-               return tmp;
-       free(tmp);
-       return NULL;
+       afh_recv_cmdline_parser(argc, argv, tmp);
+       return tmp;
 }
 
 static void afh_recv_free_config(void *conf)
@@ -256,9 +254,6 @@ void afh_recv_init(struct receiver *r)
        r->parse_config = afh_recv_parse_config;
        r->free_config = afh_recv_free_config;
        r->execute = afh_execute;
-       r->help = (struct ggo_help) {
-               .short_help = afh_recv_args_info_help,
-               .detailed_help = afh_recv_args_info_detailed_help
-       };
+       r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv);
        afh_recv_cmdline_parser_free(&dummy);
 }
diff --git a/afs.cmd b/afs.cmd
index f0bd26f1a1bef059126b362478526081e68e3743..82b6dc485cd00683b58a4ec79bd1e69c5602690f 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -210,7 +210,7 @@ H:  normalize the volume of the audio file.  A value of zero means
 H:     no amplification, 64 means the amplitude should be multiplied
 H:     by a factor of two, 128 by three and so on.
 H:
-H:     This value is used by the compress filter.
+H:     This value is used by the amp filter.
 H:
 H: -v  Verbose mode. Explain what is being done.
 H:
index 88e48b4524717bc832201d7e5c5cac75976abffc..a3912c1165e6054b0be0bd622b17175fdad4fd8a 100644 (file)
@@ -361,9 +361,6 @@ void alsa_write_init(struct writer *w)
        w->post_select = alsa_write_post_select;
        w->parse_config_or_die = alsa_parse_config_or_die;
        w->free_config = alsa_free_config;
-       w->help = (struct ggo_help) {
-               .short_help = alsa_write_args_info_help,
-               .detailed_help = alsa_write_args_info_detailed_help
-       };
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write);
        alsa_write_cmdline_parser_free(&dummy);
 }
index a6bfca25acd3126562d14a198e64ac1459a1b988..f62ea8a2f50962182e158ce8da7f18fb4ec1df4f 100644 (file)
@@ -137,8 +137,5 @@ void amp_filter_init(struct filter *f)
        f->post_select = amp_post_select;
        f->parse_config = amp_parse_config;
        f->free_config = amp_free_config;
-       f->help = (struct ggo_help) {
-               .short_help = amp_filter_args_info_help,
-               .detailed_help = amp_filter_args_info_detailed_help
-       };
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter);
 }
index 9d204ff3b5220afebbf29efd14d3721411c60b17..fdae8eea542f97720f963f281bddd121cf5d0a38 100644 (file)
@@ -371,9 +371,7 @@ void ao_write_init(struct writer *w)
        w->post_select = aow_post_select;
        w->parse_config_or_die = aow_parse_config_or_die;
        w->free_config = aow_free_config;
-       w->help = (struct ggo_help) {
-               .short_help = ao_write_args_info_help,
-       };
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write);
        /* create detailed help containing all supported drivers/options */
        for (i = 0; ao_write_args_info_detailed_help[i]; i++)
                ; /* nothing */
index 74fb11cb0622cbab54f5c46ce28cbd5856efffe8..0edab366efe6ef6fa50f99c22b9ffa3492494640 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -17,6 +17,7 @@
 #include "net.h"
 #include "string.h"
 #include "fd.h"
+#include "ggo.h"
 #include "version.h"
 
 INIT_AUDIOC_ERRLISTS;
@@ -42,6 +43,25 @@ static char *concat_args(unsigned argc, char * const *argv)
        return buf;
 }
 
+static int connect_audiod(const char *sname, char *args)
+{
+       int fd = -1, ret;
+
+       ret = connect_local_socket(sname);
+       if (ret < 0)
+               goto fail;
+       fd = ret;
+       ret = send_cred_buffer(fd, args);
+       if (ret < 0)
+               goto fail;
+       return fd;
+fail:
+       PARA_ERROR_LOG("could not connect %s\n", sname);
+       if (fd >= 0)
+               close(fd);
+       return ret;
+}
+
 #ifdef HAVE_READLINE
 #include "list.h"
 #include "sched.h"
@@ -146,8 +166,6 @@ static int audioc_i9e_line_handler(char *line)
 {
        char *args = NULL;
        int ret;
-       if (!line || !*line)
-               return 0;
 
        PARA_DEBUG_LOG("line: %s\n", line);
        ret = create_argv(line, " ", &conf.inputs);
@@ -156,15 +174,14 @@ static int audioc_i9e_line_handler(char *line)
        conf.inputs_num = ret;
        args = concat_args(conf.inputs_num, conf.inputs);
        free_argv(conf.inputs);
+       if (!args)
+               return 0;
        conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */
-       ret = connect_local_socket(socket_name);
+       ret = connect_audiod(socket_name, args);
        if (ret < 0)
                goto out;
        at->fd = ret;
        ret = mark_fd_nonblocking(at->fd);
-       if (ret < 0)
-               goto close;
-       ret = send_cred_buffer(at->fd, args);
        if (ret < 0)
                goto close;
        free(args);
@@ -194,7 +211,7 @@ __noreturn static void interactive_session(void)
                .loglevel = loglevel,
                .completers = audiod_completers,
        };
-       PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("audioc"));
+       PARA_NOTICE_LOG("\n%s\n", version_text("audioc"));
        if (conf.history_file_given)
                history_file = para_strdup(conf.history_file_arg);
        else {
@@ -263,6 +280,15 @@ static char *configfile_exists(void)
        return NULL;
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(audioc);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       exit(0);
+}
+
 /**
  * The client program to connect to para_audiod.
  *
@@ -281,29 +307,29 @@ static char *configfile_exists(void)
  */
 int main(int argc, char *argv[])
 {
-       int ret = -E_AUDIOC_SYNTAX, fd;
+       int ret, fd;
        char *cf, *buf = NULL, *args = NULL;
        size_t bufsize;
 
-       if (audioc_cmdline_parser(argc, argv, &conf))
-               goto out;
-       HANDLE_VERSION_FLAG("audioc", conf);
+       audioc_cmdline_parser(argc, argv, &conf);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("audioc", conf.version_given);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        cf = configfile_exists();
        if (cf) {
                struct audioc_cmdline_parser_params params = {
                        .override = 0,
                        .initialize = 0,
                        .check_required = 0,
-                       .check_ambiguity = 0
+                       .check_ambiguity = 0,
+                       .print_errors = 1,
+
                };
-               ret = audioc_cmdline_parser_config_file(cf, &conf, &params);
+               audioc_cmdline_parser_config_file(cf, &conf, &params);
                free(cf);
-               if (ret) {
-                       fprintf(stderr, "parse error in config file\n");
-                       exit(EXIT_FAILURE);
-               }
+               loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        if (conf.socket_given)
                socket_name = para_strdup(conf.socket_arg);
        else {
@@ -320,21 +346,16 @@ int main(int argc, char *argv[])
                interactive_session();
        args = concat_args(conf.inputs_num, conf.inputs);
 
-       ret = connect_local_socket(socket_name);
+       ret = connect_audiod(socket_name, args);
        free(socket_name);
-       if (ret < 0) {
-               PARA_EMERG_LOG("failed to connect to local socket\n");
+       if (ret < 0)
                goto out;
-       }
        fd = ret;
-       ret = send_cred_buffer(fd, args);
+       ret = mark_fd_blocking(STDOUT_FILENO);
        if (ret < 0)
                goto out;
        bufsize = conf.bufsize_arg;
        buf = para_malloc(bufsize);
-       ret = mark_fd_blocking(STDOUT_FILENO);
-       if (ret < 0)
-               goto out;
        do {
                size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
                if (ret <= 0)
index d2c66f2bfe5f5bd0d8ba1da88b048ad21d9ceb00..8552af6b07d2d4d977fe4f0587c552c42f1bf038 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -343,10 +343,11 @@ static void parse_config_or_die(void)
                PARA_EMERG_LOG("can not read config file %s\n", config_file);
                goto err;
        }
-       if (ret)
+       if (ret) {
                audiod_cmdline_parser_config_file(config_file, &conf, &params);
+               daemon_set_loglevel(conf.loglevel_arg);
+       }
        free(config_file);
-       daemon_set_loglevel(conf.loglevel_arg);
        return;
 err:
        free(config_file);
@@ -1300,18 +1301,17 @@ static void set_initial_status(void)
 
 __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);
+       struct ggo_help h = DEFINE_GGO_HELP(audiod);
+       bool d = conf.detailed_help_given;
+       unsigned flags;
+
+       flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+       ggo_print_help(&h, flags);
+
+       flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
+       print_receiver_helps(flags);
+       print_filter_helps(flags);
+       print_writer_helps(flags);
        exit(0);
 }
 
@@ -1350,9 +1350,9 @@ int main(int argc, char *argv[])
        };
 
        valid_fd_012();
-       if (audiod_cmdline_parser_ext(argc, argv, &conf, &params))
-               exit(EXIT_FAILURE);
-       HANDLE_VERSION_FLAG("audiod", conf);
+       audiod_cmdline_parser_ext(argc, argv, &conf, &params);
+       daemon_set_loglevel(conf.loglevel_arg);
+       version_handle_flag("audiod", conf.version_given);
        /* init receivers/filters/writers early to make help work */
        recv_init();
        filter_init();
index 2f3726faed46a8d004d4111a9e503a70851af511..b49d659e0ac0bd784b2002b941b3471540ee5ac9 100644 (file)
@@ -442,7 +442,7 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret < 0)
                goto out;
        ret = create_argv(buf, "\n", &argv);
-       if (ret < 0)
+       if (ret <= 0)
                goto out;
        argc = ret;
        //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
index 90dc432b8829426cd9d4ce081e4f8e20a5fb194e..2d6ef31f590bca1736d458cc61c4fbe67459641b 100644 (file)
--- a/client.c
+++ b/client.c
@@ -445,11 +445,9 @@ static int client_i9e_line_handler(char *line)
        int ret;
 
        client_disconnect(ct);
-       if (!line || !*line)
-               return 0;
-       PARA_DEBUG_LOG("line handler: %s\n", line);
+       PARA_DEBUG_LOG("line: %s\n", line);
        ret = make_client_argv(line);
-       if (ret < 0)
+       if (ret <= 0)
                return ret;
        ret = client_connect(ct, &sched, NULL, NULL);
        if (ret < 0)
@@ -477,7 +475,7 @@ __noreturn static void interactive_session(void)
                .completers = completers,
        };
 
-       PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("client"));
+       PARA_NOTICE_LOG("\n%s\n", version_text("client"));
        if (ct->conf.history_file_given)
                history_file = para_strdup(ct->conf.history_file_arg);
        else {
index 8ea41922c6976996d039912ec9f754fc29fd197f..1ecba73068f04c4dbf43480f5ccf32a932e01279 100644 (file)
@@ -13,7 +13,6 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "client.cmdline.h"
 #include "crypt.h"
 #include "net.h"
 #include "fd.h"
 #include "client.h"
 #include "buffer_tree.h"
 #include "version.h"
+#include "ggo.h"
 
 /** The size of the receiving buffer. */
 #define CLIENT_BUFSIZE 4000
 
 /**
- * Close the connection to para_server and deallocate per-command ressources.
+ * Close the connection to para_server and deallocate per-command resources.
  *
  * \param ct The client task.
  *
- * This frees all ressources of the current command but keeps the configuration
+ * This frees all resources of the current command but keeps the configuration
  * in \p ct->conf.
  *
  * \sa \ref client_close().
@@ -612,6 +612,15 @@ err_out:
        return ret;
 }
 
+__noreturn static void print_help_and_die(struct client_task *ct)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(client);
+       bool d = ct->conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       exit(0);
+}
+
 /**
  * Parse a client configuration.
  *
@@ -642,7 +651,9 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        ret = -E_CLIENT_SYNTAX;
        if (client_cmdline_parser(argc, argv, &ct->conf))
                goto out;
-       HANDLE_VERSION_FLAG("client", ct->conf);
+       version_handle_flag("client", ct->conf.version_given);
+       if (ct->conf.help_given || ct->conf.detailed_help_given)
+               print_help_and_die(ct);
 
        ct->config_file = ct->conf.config_file_given?
                para_strdup(ct->conf.config_file_arg) :
index ec822c82978d78649b6641d2206f41db44ab3bd7..79ca3652040188010dbbf2209fbf8ffe5757db50 100644 (file)
--- a/command.c
+++ b/command.c
@@ -395,7 +395,8 @@ static int com_si(struct command_context *cc)
                free(info);
        }
        ut = get_server_uptime_str(now);
-       ret = xasprintf(&msg, "version: " GIT_VERSION "\n"
+       ret = xasprintf(&msg,
+               "version: %s\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
@@ -403,6 +404,7 @@ static int com_si(struct command_context *cc)
                "current loglevel: %s\n"
                "supported audio formats: %s\n"
                "%s",
+               version_git(),
                ut, mmd->num_played,
                (int)getppid(),
                (int)mmd->afs_pid,
@@ -431,11 +433,9 @@ static int com_version(struct command_context *cc)
 
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
-       msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS
-               ", " CC_VERSION "\n";
-       len = strlen(msg);
+       len = xasprintf(&msg, "%s", version_text("server"));
        if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true);
+               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
        return sc_send_bin_buffer(&cc->scc, msg, len);
 }
 
index d7162791b86d372562f690b464779b250519cbeb..0c0200e7e3b522af5aad7bb0a9f86f7e32024218 100644 (file)
@@ -74,18 +74,23 @@ next_buffer:
                op = para_malloc(length);
        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 (adjusted_sample > 32767) { /* clip */
-                       PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
-                               sample, adjusted_sample);
-                       adjusted_sample = 32767;
+               int sample = *ip++;
+               bool neg = false;
+
+               if (sample < 0) {
+                       sample = -sample;
+                       neg = true;
+               }
+               sample *= pcd->current_gain;
+               sample >>= gain_shift;
+               if (sample > 32767) { /* clip */
+                       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[i] = sample >= 0? adjusted_sample : -adjusted_sample;
+               } else if (sample > pcd->peak)
+                       pcd->peak = sample;
+               op[i] = neg? -sample : sample;
                if (++pcd->num_samples & mask)
                        continue;
 //             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
@@ -153,8 +158,5 @@ void compress_filter_init(struct filter *f)
        f->post_select = compress_post_select;
        f->parse_config = compress_parse_config;
        f->free_config = compress_free_config;
-       f->help = (struct ggo_help) {
-               .short_help = compress_filter_args_info_help,
-               .detailed_help = compress_filter_args_info_detailed_help
-       };
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter);
 }
index b3f3afaf66c34dafe95e0f9fe7f82da3b80a3de5..b3332c670f6eb610afa05f84adca763490e1e4c7 100644 (file)
@@ -102,7 +102,7 @@ all_errlist_objs="mp3_afh afh_common net string signal time daemon
        exec send_common ggo udp_recv color fec fecdec_filter
        prebuffer_filter bitstream imdct check_wav
        wma_afh wma_common wmadec_filter buffer_tree crypt_common
-       gui gui_theme sideband afh_recv play"
+       gui gui_theme sideband afh_recv play version"
 
 executables="recv filter audioc write client afh audiod play"
 
@@ -111,49 +111,83 @@ recv_cmdline_objs="add_cmdline(recv http_recv dccp_recv udp_recv afh_recv)"
 recv_errlist_objs="
        http_recv recv_common recv time string net dccp_recv fd
        sched stdout ggo udp_recv buffer_tree afh_recv afh_common
-       wma_afh wma_common mp3_afh
+       wma_afh wma_common mp3_afh version
 "
 
 recv_ldflags=""
 
 filter_cmdline_objs="add_cmdline(filter compress_filter amp_filter prebuffer_filter)"
 filter_errlist_objs="filter_common wav_filter compress_filter filter string
-       stdin stdout sched fd amp_filter ggo fecdec_filter fec
+       stdin stdout sched fd amp_filter ggo fecdec_filter fec version
        prebuffer_filter time bitstream imdct wma_common wmadec_filter buffer_tree"
 filter_ldflags="-lm"
 filters=" compress wav amp fecdec wmadec prebuffer"
 
 audioc_cmdline_objs="add_cmdline(audioc)"
-audioc_errlist_objs="audioc string net fd"
+audioc_errlist_objs="
+       audioc
+       string
+       net
+       fd
+       version
+       ggo
+"
 audioc_ldflags=""
 
 audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
 audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband
        time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
        recv_common fd sched write_common file_write audiod_command fecdec_filter
-       client_common ggo udp_recv color fec prebuffer_filter
+       client_common ggo udp_recv color fec prebuffer_filter version
        bitstream imdct wma_common wmadec_filter buffer_tree"
 audiod_ldflags="-lm"
 audiod_audio_formats="wma"
 
 afh_cmdline_objs="add_cmdline(afh)"
-afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common"
+afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common
+       version ggo"
 afh_ldflags=""
 
 write_cmdline_objs="add_cmdline(write file_write)"
 write_errlist_objs="write write_common file_write time fd string sched stdin
-       buffer_tree ggo check_wav"
+       buffer_tree ggo check_wav version"
 write_ldflags=""
 writers=" file"
 default_writer="FILE_WRITE"
 
 client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="client net string fd sched stdin stdout time sideband
-       client_common buffer_tree crypt_common"
+client_errlist_objs="
+       client
+       net
+       string
+       fd
+       sched
+       stdin
+       stdout
+       time
+       sideband
+       client_common
+       buffer_tree
+       crypt_common
+       version
+       ggo
+"
 client_ldflags=""
 
 gui_cmdline_objs="add_cmdline(gui)"
-gui_errlist_objs="exec signal string stat ringbuffer fd gui gui_theme"
+gui_errlist_objs="
+       exec
+       signal
+       string
+       stat
+       ringbuffer
+       fd
+       gui
+       gui_theme
+       time
+       version
+       ggo
+"
 gui_objs="$gui_cmdline_objs $gui_errlist_objs"
 play_errlist_objs="play fd sched ggo buffer_tree time string net
        afh_recv afh_common
@@ -163,6 +197,7 @@ play_errlist_objs="play fd sched ggo buffer_tree time string net
        wav_filter compress_filter amp_filter prebuffer_filter fecdec_filter
                wmadec_filter
        write_common file_write
+       version
 "
 play_cmdline_objs="add_cmdline(http_recv dccp_recv udp_recv afh_recv compress_filter amp_filter prebuffer_filter file_write play)"
 play_ldflags="-lm"
@@ -290,11 +325,43 @@ else
        extras="$extras server"
        executables="$executables server"
        server_cmdline_objs="add_cmdline(server)"
-       server_errlist_objs="server afh_common mp3_afh vss command net
-               string signal time daemon http_send close_on_fork mm
-               crypt_common ipc dccp_send fd user_list chunk_queue
-               afs aft mood score attribute blob playlist sched acl
-               send_common udp_send color fec wma_afh wma_common sideband"
+       server_errlist_objs="
+               server
+               afh_common
+               mp3_afh
+               vss command
+               net
+               string
+               signal
+               time
+               daemon
+               http_send
+               close_on_fork
+               mm
+               crypt_common
+               ipc dccp_send
+               fd
+               user_list
+               chunk_queue
+               afs
+               aft
+               mood
+               score
+               attribute
+               blob
+               playlist
+               sched
+               acl
+               send_common
+               udp_send
+               color
+               fec
+               wma_afh
+               wma_common
+               sideband
+               version
+               ggo
+       "
        all_errlist_objs="$all_errlist_objs server vss command
                http_send close_on_fork mm ipc dccp_send user_list
                chunk_queue afs aft mood score attribute blob playlist
@@ -462,7 +529,15 @@ AC_MSG_RESULT($have_ucred)
 if test ${have_ucred} = yes; then
        AC_DEFINE(HAVE_UCRED, 1, define to 1 you have struct ucred)
 fi
-
+########################################################################### gengetopt
+echo 'option "z" z "" flag off' | $gengetopt --file-name conftest-ggo &&
+AC_CHECK_DECL(
+       [gengetopt_args_info_description],
+       [ggo_descriptions_declared=yes],
+       [ggo_descriptions_declared=no],
+       [#include "conftest-ggo.h"]
+)
+AC_SUBST(ggo_descriptions_declared)
 ########################################################################### curses
 have_curses="yes"
 OLD_CPPFLAGS="$CPPFLAGS"
@@ -1001,7 +1076,7 @@ if test -n "$mixers"; then
        extras="$extras fade"
        executables="$executables fade"
        all_errlist_objs="$all_errlist_objs fade"
-       fade_errlist_objs="$fade_errlist_objs fade exec string fd"
+       fade_errlist_objs="$fade_errlist_objs fade exec string fd version ggo"
        fade_cmdline_objs="add_cmdline(fade)"
        fade_objs="$fade_cmdline_objs $fade_errlist_objs"
        AC_SUBST(fade_objs, add_dot_o($fade_objs))
index 1f9df18955306c2c2449444f3c5a0317d1358a4b..c751f2f7ce5d85464d506c8c6582556bb424aa7d 100644 (file)
@@ -107,11 +107,10 @@ static void *dccp_recv_parse_config(int argc, char **argv)
 {
        struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
 
-       if (!dccp_recv_cmdline_parser(argc, argv, tmp) &&
-           dccp_recv_ccid_support_check(tmp))
-               return tmp;
-       free(tmp);
-       return NULL;
+       dccp_recv_cmdline_parser(argc, argv, tmp);
+       if (!dccp_recv_ccid_support_check(tmp))
+               exit(EXIT_FAILURE);
+       return tmp;
 }
 
 static void dccp_recv_pre_select(struct sched *s, struct task *t)
@@ -181,9 +180,6 @@ void dccp_recv_init(struct receiver *r)
        r->post_select = dccp_recv_post_select;
        r->parse_config = dccp_recv_parse_config;
        r->free_config = dccp_recv_free_config;
-       r->help = (struct ggo_help) {
-               .short_help = dccp_recv_args_info_help,
-               .detailed_help = dccp_recv_args_info_detailed_help
-       };
+       r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv);
        dccp_recv_cmdline_parser_free(&dummy);
 }
diff --git a/error.h b/error.h
index 873ce28036442b31f2d72f9c8c811cbad928b1e3..6734040bec1a99f7dda774a9d2655cabef4cac18 100644 (file)
--- a/error.h
+++ b/error.h
@@ -34,6 +34,7 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define STDIN_ERRORS
 #define WRITE_ERRORS
 #define CHECK_WAV_ERRORS
+#define VERSION_ERRORS
 
 extern const char **para_errlist[];
 
diff --git a/fade.c b/fade.c
index fee8f8ca2af34ca8aef421ede07fc8f00763ab7b..878b83a17e26f40da9c33c6035dba345353257d4 100644 (file)
--- a/fade.c
+++ b/fade.c
@@ -14,6 +14,7 @@
 #include "string.h"
 #include "mix.h"
 #include "error.h"
+#include "ggo.h"
 #include "version.h"
 
 INIT_FADE_ERRLISTS;
@@ -286,15 +287,26 @@ static struct mixer *get_mixer_or_die(void)
        exit(EXIT_FAILURE);
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(fade);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       exit(0);
+}
+
 int main(int argc, char *argv[])
 {
        int ret;
        struct mixer *m;
        struct mixer_handle *h = NULL;
 
-       if (fade_cmdline_parser(argc, argv, &conf))
-               exit(EXIT_FAILURE);
-       HANDLE_VERSION_FLAG("fade", conf);
+       fade_cmdline_parser(argc, argv, &conf);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("fade", conf.version_given);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        ret = configfile_exists();
        if (!ret && conf.config_file_given) {
                PARA_EMERG_LOG("can not read config file %s\n",
@@ -311,8 +323,8 @@ int main(int argc, char *argv[])
                };
                fade_cmdline_parser_config_file(conf.config_file_arg,
                        &conf, &params);
+               loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        init_mixers();
        m = get_mixer_or_die();
        ret = m->open(conf.mixer_device_arg, &h);
index cea21cb3f5e182798f922b8234aa49977c85ba6b..f7b2b30792881e6146a4e2af1d12373f14b8a53f 100644 (file)
@@ -157,9 +157,6 @@ void file_write_init(struct writer *w)
        w->parse_config_or_die = file_write_parse_config_or_die;
        w->free_config = file_write_free_config;
        w->close = file_write_close;
-       w->help = (struct ggo_help) {
-               .short_help = file_write_args_info_help,
-               .detailed_help = file_write_args_info_detailed_help
-       };
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(file_write);
        file_write_cmdline_parser_free(&dummy);
 }
index 40e3779eab4d22620125294e5024a443b18e6296..3b68857328b230877e2f8cd9061fb07ee789902c 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -47,30 +47,22 @@ INIT_STDERR_LOGGING(loglevel);
 
 __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);
+       struct ggo_help h = DEFINE_GGO_HELP(filter);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
        exit(0);
 }
 
-static int parse_config(int argc, char *argv[])
+static int parse_config(void)
 {
        static char *cf; /* config file */
        struct stat statbuf;
 
-       if (filter_cmdline_parser(argc, argv, &conf))
-               return -E_FILTER_SYNTAX;
-       HANDLE_VERSION_FLAG("filter", conf);
+       version_handle_flag("filter", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        if (!cf) {
                char *home = para_homedir();
                cf = make_message("%s/.paraslash/filter.conf", home);
@@ -84,8 +76,8 @@ static int parse_config(int argc, char *argv[])
                        .check_ambiguity = 0,
                        .print_errors = 1
                };
-               if (filter_cmdline_parser_config_file(cf, &conf, &params))
-                       return -E_FILTER_SYNTAX;
+               filter_cmdline_parser_config_file(cf, &conf, &params);
+               loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
        if (!conf.filter_given)
                return -E_NO_FILTERS;
@@ -112,8 +104,10 @@ int main(int argc, char *argv[])
        struct btr_node *parent;
        struct filter_node **fns;
 
+       filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        filter_init();
-       ret = parse_config(argc, argv);
+       ret = parse_config();
        if (ret < 0)
                goto out;
        sit->btrn = btr_new_node(&(struct btr_node_description)
index 9c57904e59eb73b59fb470bca1d2179384aed7f0..1cda119f0f3496183f0785c181f583fdd4a061b9 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -121,7 +121,7 @@ struct filter {
 
 void filter_init(void);
 int check_filter_arg(char *filter_arg, void **conf);
-void print_filter_helps(int detailed);
+void print_filter_helps(unsigned flags);
 void generic_filter_pre_select(struct sched *s, struct task *t);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
index 907912fd06d9f0e4a78a53c7a5026c4a0a9d4846..2616c9bdfc940a3415ef6e5abd3a446d4e859b7e 100644 (file)
@@ -101,24 +101,30 @@ int check_filter_arg(char *fa, void **conf)
 /**
  * Print help text of each filter to stdout.
  *
- * \param detailed If non-zero, print detailed help.
+ * \param flags Passed to \ref ggo_print_help().
  */
-void print_filter_helps(int detailed)
+void print_filter_helps(unsigned flags)
 {
-       int i;
+       int i, num = 0;
 
-       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");
+       printf_or_die("\nAvailable filters: ");
+       FOR_EACH_SUPPORTED_FILTER(i) {
+               if (num > 50) {
+                       printf_or_die("\n                  ");
+                       num = 0;
+               }
+               num += printf_or_die("%s%s", i? " " : "", filters[i].name);
+       }
+       printf_or_die("\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);
+               printf_or_die("\nOptions for %s (%s):", f->name,
+                       f->help.purpose);
+               ggo_print_help(&f->help, flags);
        }
 }
 
diff --git a/ggo.c b/ggo.c
index b2e079ab426a5e0a4547d5db465570e00a803695..58d86d0796adec72bf3ea134532b9bc3c80bea07 100644 (file)
--- a/ggo.c
+++ b/ggo.c
@@ -9,13 +9,14 @@
 
 #include "para.h"
 #include "ggo.h"
+#include "version.h"
 
 /**
  * Wrapper for printf() that exits on errors.
  *
  * \param fmt Usual format string.
  */
-__printf_1_2 void printf_or_die(const char *fmt, ...)
+__printf_1_2 int printf_or_die(const char *fmt, ...)
 {
        va_list argp;
        int ret;
@@ -24,7 +25,7 @@ __printf_1_2 void printf_or_die(const char *fmt, ...)
        ret = vprintf(fmt, argp);
        va_end(argp);
        if (ret >= 0)
-               return;
+               return ret;
        exit(EXIT_FAILURE);
 }
 
@@ -32,21 +33,25 @@ __printf_1_2 void printf_or_die(const char *fmt, ...)
  * Print one of the two given help texts.
  *
  * \param help contains the help texts.
- * \param detailed_help Whether to print the detailed help text.
+ * \param flags What to print, see \ref ggo_print_help_flags.
  */
-void ggo_print_help(struct ggo_help *help, int detailed_help)
+void ggo_print_help(struct ggo_help *help, unsigned flags)
 {
        const char **p;
 
-       if (!help)
-               return;
-       if (detailed_help)
+       if (help->purpose && (flags & GPH_PRINT_NAME_PURPOSE))
+               printf_or_die("para_%s - %s\n", help->prefix, help->purpose);
+       if (help->usage && (flags & GPH_PRINT_USAGE))
+               printf_or_die("\n%s\n", help->usage);
+       if (help->description && (flags & GPH_PRINT_DESCRIPTION))
+               printf_or_die("\n%s\n", help->description);
+       printf_or_die("\n");
+       if (flags & GPH_DETAILED)
                p = help->detailed_help;
        else
                p = help->short_help;
        if (!p)
                return;
-       p += 3; /* skip -h and -V */
        for (; *p; p++)
-               printf_or_die("\t%s\n", *p);
+               printf_or_die("%s\n", *p);
 }
diff --git a/ggo.h b/ggo.h
index e81b10c6a3846f2b3c73bd14de56264216a2ba89..81565e7c24b50d130afd36560c4f5973973f1090 100644 (file)
--- a/ggo.h
+++ b/ggo.h
@@ -7,14 +7,64 @@
 /** \file ggo.h Functions and structures for help text handling. */
 
 /**
- * Used by executables that can not use gengetopt's generated help function.
+ * Information extracted from the .cmdline.h header files.
  */
 struct ggo_help {
-       /** The lines of the short help text. */
+       /** Program or module (receiver, filter, writer) name. */
+       const char *prefix;
+       /** Generated by gengetopt from the options in the .ggo file. */
        const char **short_help;
-       /** The lines of the detailed help text. */
+       /** Like \a short_help, plus the \a details section. */
        const char **detailed_help;
+       /** The purpose text as specified in the ggo file. */
+       const char *purpose;
+       /** Generated by gengetopt and exported via the *.cmdline.h file. */
+       const char *usage;
+       /** The description text given in the .ggo file. */
+       const char *description;
 };
 
-void ggo_print_help(struct ggo_help *help, int detailed_help);
-__printf_1_2 void printf_or_die(const char *fmt, ...);
+/**
+ * Control the output of \ref ggo_print_help().
+ *
+ * Any combination of these flags may be passed to ggo_print_help().
+ * Note that the list of supported options is always printed.
+ */
+enum ggo_print_help_flags {
+       /** Whether to print the short help or the detailed help. */
+       GPH_DETAILED = 1 << 0,
+       /** Print the program or module name and the purpose text. */
+       GPH_PRINT_NAME_PURPOSE = 1 << 1,
+       /** Print the synopsis. */
+       GPH_PRINT_USAGE = 1 << 2,
+       /** Print the description text. */
+       GPH_PRINT_DESCRIPTION = 1 << 3,
+};
+
+/** Used to print the normal help of programs (--help) */
+#define GPH_STANDARD_FLAGS \
+       (GPH_PRINT_NAME_PURPOSE | GPH_PRINT_USAGE)
+
+/** Additional information for --detailed-help. */
+#define GPH_STANDARD_FLAGS_DETAILED \
+       (GPH_STANDARD_FLAGS | GPH_DETAILED | GPH_PRINT_DESCRIPTION)
+
+/** For module help embedded in a program help. */
+#define GPH_MODULE_FLAGS 0
+
+/** Modules help with detailed descriptions. */
+#define GPH_MODULE_FLAGS_DETAILED GPH_DETAILED | GPH_PRINT_DESCRIPTION
+
+/** Make a ggo_help structure using information from the .cmdline.h file. */
+#define DEFINE_GGO_HELP(_prefix) \
+       { \
+               .prefix = #_prefix, \
+               .short_help = _prefix ## _args_info_help, \
+               .detailed_help = _prefix ## _args_info_detailed_help, \
+               .purpose = _prefix ## _args_info_purpose, \
+               .usage = _prefix ## _args_info_usage, \
+               .description = _prefix ## _args_info_description, \
+       }
+
+void ggo_print_help(struct ggo_help *help, unsigned flags);
+__printf_1_2 int printf_or_die(const char *fmt, ...);
diff --git a/gui.c b/gui.c
index 631e7cc54113a6e03229f144c4a6a04feba7950d..baab0bd905e56fee0a7a821e0c536e15b532db5f 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -11,6 +11,7 @@
 #include <sys/types.h>
 #include <curses.h>
 #include <locale.h>
+#include <sys/time.h>
 
 #include "gui.cmdline.h"
 #include "para.h"
@@ -22,6 +23,7 @@
 #include "list.h"
 #include "sched.h"
 #include "signal.h"
+#include "ggo.h"
 #include "version.h"
 
 /** define the array of error lists needed by para_gui */
@@ -51,7 +53,7 @@ static struct ringbuffer *bot_win_rb;
 
 static unsigned scroll_position;
 
-static int cmd_died, curses_active;
+static int curses_active;
 static pid_t cmd_pid;
 
 static int command_fds[2];
@@ -288,14 +290,20 @@ static char *configfile_exists(void)
        return file_exists(tmp)? tmp: NULL;
 }
 
-/*
- * print num spaces to curses window
- */
+/* Print given number of spaces to curses window. */
 static void add_spaces(WINDOW* win, unsigned int num)
 {
-       while (num > 0) {
-               num--;
-               waddstr(win, " ");
+       char space[] = "                                ";
+       unsigned sz = sizeof(space) - 1; /* number of spaces */
+
+       while (num >= sz)  {
+               waddstr(win, space);
+               num -= sz;
+       }
+       if (num > 0) {
+               assert(num < sz);
+               space[num] = '\0';
+               waddstr(win, space);
        }
 }
 
@@ -555,7 +563,8 @@ static void setup_signal_handling(void)
        para_sigaction(SIGHUP, SIG_IGN);
 }
 
-__noreturn static void do_exit(int ret)
+/* kill every process in the process group and exit */
+__noreturn static void kill_pg_and_die(int ret)
 {
        para_sigaction(SIGTERM, SIG_IGN);
        kill(0, SIGTERM);
@@ -574,7 +583,7 @@ static void shutdown_curses(void)
 __noreturn static void finish(int ret)
 {
        shutdown_curses();
-       do_exit(ret);
+       kill_pg_and_die(ret);
 }
 
 /*
@@ -589,15 +598,15 @@ __noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
        va_start(argp, fmt);
        vfprintf(outfd, fmt, argp);
        va_end(argp);
-       do_exit(ret);
+       kill_pg_and_die(ret);
 }
 
 static void print_welcome(void)
 {
        if (loglevel > LL_NOTICE)
                return;
-       outputf(COLOR_WELCOME, "Welcome to para_gui " PACKAGE_VERSION
-               " \"" CODENAME "\". Theme: %s", theme.name);
+       outputf(COLOR_WELCOME, "Welcome to %s. Theme: %s",
+               version_single_line("gui"), theme.name);
        wclrtoeol(bot.win);
 }
 
@@ -856,10 +865,8 @@ reap_next_child:
        ret = para_reap_child(&pid);
        if (ret <= 0)
                return;
-       if (pid == cmd_pid) {
+       if (pid == cmd_pid)
                cmd_pid = 0;
-               cmd_died = 1;
-       }
        goto reap_next_child;
 }
 
@@ -947,33 +954,36 @@ static void handle_signal(int sig)
        }
 }
 
-static int open_stat_pipe(void)
+static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv)
 {
-       static int init = 1;
+       static struct timeval next_exec, atm, diff;
        int ret, fds[3] = {0, 1, 0};
        pid_t pid;
 
-       if (init)
-               init = 0;
-       else
-               /*
-                * Sleep a bit to avoid a busy loop. As the call to sleep() may
-                * be interrupted by SIGCHLD, we simply wait until the call
-                * succeeds.
-                */
-               while (sleep(2))
-                       ; /* nothing */
+       if (stat_pipe >= 0)
+               goto success;
+       /* Avoid busy loop */
+       gettimeofday(&atm, NULL);
+       if (tv_diff(&next_exec, &atm, &diff) > 0) {
+               if (tv_diff(&diff, tv, NULL) < 0)
+                       *tv = diff;
+               return;
+       }
+       next_exec.tv_sec = atm.tv_sec + 2;
        ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
        if (ret < 0)
-               return ret;
+               return;
        ret = mark_fd_nonblocking(fds[1]);
-       if (ret >= 0)
-               return fds[1];
-       close(fds[1]);
-       return ret;
+       if (ret < 0) {
+               close(fds[1]);
+               return;
+       }
+       stat_pipe = fds[1];
+success:
+       para_fd_set(stat_pipe, rfds, max_fileno);
 }
 
-#define COMMAND_BUF_SIZE 4096
+#define COMMAND_BUF_SIZE 32768
 
 /*
  * This is the core select loop. Besides the (internal) signal
@@ -995,6 +1005,7 @@ static int do_select(int mode)
        char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
        int cbo[2] = {0, 0}; /* command buf offsets */
        struct timeval tv;
+       unsigned flags[2] = {0, 0}; /* for for_each_line() */
 
 repeat:
        tv.tv_sec = conf.timeout_arg  / 1000;
@@ -1002,10 +1013,7 @@ repeat:
 //     ret = refresh_status();
        FD_ZERO(&rfds);
        max_fileno = 0;
-       if (stat_pipe < 0)
-               stat_pipe = open_stat_pipe();
-       if (stat_pipe >= 0)
-               para_fd_set(stat_pipe, &rfds, &max_fileno);
+       status_pre_select(&rfds, &max_fileno, &tv);
        /* signal pipe */
        para_fd_set(signal_pipe, &rfds, &max_fileno);
        /* command pipe only for COMMAND_MODE */
@@ -1015,6 +1023,8 @@ repeat:
                if (command_fds[1] >= 0)
                        para_fd_set(command_fds[1], &rfds, &max_fileno);
        }
+       if (mode == GETCH_MODE || mode == COMMAND_MODE)
+               para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
        ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
        if (ret <= 0)
                goto check_return; /* skip fd checks */
@@ -1033,18 +1043,26 @@ repeat:
                                COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
                        cbo[i] += sz;
                        sz = cbo[i];
-                       cbo[i] = for_each_line(command_buf[i], cbo[i],
+                       cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
                                add_output_line, &i);
-                       if (sz != cbo[i])
+                       if (sz != cbo[i]) { /* at least one line found */
                                wrefresh(bot.win);
+                               flags[i] = 0;
+                       }
                        if (ret < 0) {
                                PARA_NOTICE_LOG("closing command fd %d: %s",
                                        i, para_strerror(-ret));
                                close(command_fds[i]);
                                command_fds[i] = -1;
+                               flags[i] = 0;
                                if (command_fds[!i] < 0) /* both fds closed */
                                        return 0;
                        }
+                       if (cbo[i] == COMMAND_BUF_SIZE - 1) {
+                               PARA_NOTICE_LOG("discarding overlong line");
+                               cbo[i] = 0;
+                               flags[i] = FELF_DISCARD_FIRST;
+                       }
                }
        }
        ret = read_stat_pipe(&rfds);
@@ -1082,10 +1100,8 @@ check_return:
                        return ret;
                break;
        case EXTERNAL_MODE:
-               if (cmd_died) {
-                       cmd_died = 0;
+               if (cmd_pid == 0)
                        return 0;
-               }
        }
        goto repeat;
 }
@@ -1159,7 +1175,6 @@ static void external_cmd(char *cmd)
        shutdown_curses();
        if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
                return;
-       cmd_died = 0;
        do_select(EXTERNAL_MODE);
        init_curses();
 }
@@ -1348,10 +1363,7 @@ static void com_reread_conf(void)
        }
        PARA_INFO_LOG("rereading command line options and config file");
        gui_cmdline_parser_ext(_argc, _argv, &conf, &params);
-       if (gui_cmdline_parser_config_file(cf, &conf, &params) != 0) {
-               PARA_EMERG_LOG("errors in config file");
-               finish(EXIT_FAILURE);
-       }
+       gui_cmdline_parser_config_file(cf, &conf, &params);
        PARA_NOTICE_LOG("config file reloaded");
        if (check_key_map_args() < 0)
                finish(EXIT_FAILURE);
@@ -1420,8 +1432,7 @@ static void com_enlarge_top_win(void)
 
 static void com_version(void)
 {
-       print_in_bar(COLOR_MSG, "para_gui " PACKAGE_VERSION " \""
-               CODENAME "\"");
+       print_in_bar(COLOR_MSG, "%s", version_single_line("gui"));
 }
 
 __noreturn static void com_quit(void)
@@ -1500,6 +1511,15 @@ static void handle_command(int c)
                km_keyname(c));
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(gui);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       exit(0);
+}
+
 int main(int argc, char *argv[])
 {
        int ret;
@@ -1509,7 +1529,10 @@ int main(int argc, char *argv[])
        _argv = argv;
 
        gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
-       HANDLE_VERSION_FLAG("gui", conf);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("gui", conf.version_given);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        cf = configfile_exists();
        if (!cf && conf.config_file_given) {
                fprintf(stderr, "can not read config file %s\n",
@@ -1524,10 +1547,9 @@ int main(int argc, char *argv[])
                        .check_ambiguity = 0,
                        .print_errors = 1,
                };
-               if (gui_cmdline_parser_config_file(cf, &conf, &params) != 0)
-                       exit(EXIT_FAILURE);
+               gui_cmdline_parser_config_file(cf, &conf, &params);
+               loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        if (check_key_map_args() < 0) {
                fprintf(stderr, "invalid key map\n");
                exit(EXIT_FAILURE);
index f1d8593068ad92bd7d86730bbf5d9dbc65d38376..7db8ba193c1eaa2fd6046efd91011c25d37da032 100644 (file)
@@ -143,10 +143,8 @@ static void *http_recv_parse_config(int argc, char **argv)
 {
        struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp));
 
-       if (!http_recv_cmdline_parser(argc, argv, tmp))
-               return tmp;
-       free(tmp);
-       return NULL;
+       http_recv_cmdline_parser(argc, argv, tmp);
+       return tmp;
 }
 
 static int http_recv_open(struct receiver_node *rn)
@@ -195,9 +193,6 @@ void http_recv_init(struct receiver *r)
        r->post_select = http_recv_post_select;
        r->parse_config = http_recv_parse_config;
        r->free_config = http_recv_free_config;
-       r->help = (struct ggo_help) {
-               .short_help = http_recv_args_info_help,
-               .detailed_help = http_recv_args_info_detailed_help
-       };
+       r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv);
        http_recv_cmdline_parser_free(&dummy);
 }
index f281901248e3aebe878553220dda938d1c3fbece..43cb99f2781979e74b66eef640fadb7878f30aeb 100644 (file)
@@ -272,6 +272,7 @@ static void clear_bottom_line(void)
        rl_redisplay();
        wipe_bottom_line(); /* wipe out the prompt */
        rl_insert_text(text);
+       free(text);
        rl_point = point;
 }
 
@@ -290,21 +291,25 @@ static bool input_available(void)
 static void i9e_line_handler(char *line)
 {
        int ret;
+       struct btr_node *dummy;
 
+       if (!line) {
+               i9ep->input_eof = true;
+               return;
+       }
+       if (!*line)
+               goto free_line;
+       rl_set_prompt("");
+       dummy = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "dummy line handler"));
+       i9e_attach_to_stdout(dummy);
        ret = i9ep->ici->line_handler(line);
        if (ret < 0)
                PARA_WARNING_LOG("%s\n", para_strerror(-ret));
-       rl_set_prompt("");
-       if (line) {
-               if (!*line)
-                       rl_set_prompt(i9ep->ici->prompt);
-               else
-                       add_history(line);
-               free(line);
-       } else {
-               rl_set_prompt("");
-               i9ep->input_eof = true;
-       }
+       add_history(line);
+       btr_remove_node(&dummy);
+free_line:
+       free(line);
 }
 
 static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t)
index beb846961b79dde7b7bdbdf9663c671b5857e080..f8e29fea8693af08506fb6b91210010d722994ac 100644 (file)
@@ -1,14 +1,8 @@
-args "--unamed-opts=audio_file --no-handle-version"
+args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
 
-include(header.m4)
-<qu>
-text "
-para_afh, the audio format handler tool, is a simple program for analyzing
-audio files. It prints technical information about the given audio file to
-stdout.
-"
-</qu>
+purpose "Print information about audio file(s)."
 
+include(header.m4)
 include(loglevel.m4)
 
 <qu>
index 4995e77f187f2886f59be5287a511335f250c8a3..f4da9d992c8ffc078710a95e182a764433e9ddf8 100644 (file)
@@ -1,6 +1,8 @@
-include(header.m4)
-<qu>
-text "
+args "--no-version --no-help"
+
+purpose "Make an audio stream from a local file."
+
+description "
        The afh (audio format handler) receiver can be used to write
        selected parts of the given audio file without decoding
        the data.
@@ -8,9 +10,10 @@ text "
        The selected parts of the content of the audio file are passed
        to the child nodes of the buffer tree. Only complete chunks
        with respect of the underlying audio format are passed.
-
 "
 
+include(header.m4)
+<qu>
 option "filename" f
 #~~~~~~~~~~~~~~~~~~
 "file to open"
index d0cd4071cdcf44346c982976f324547cabcafe19..04b4963d3a9fbe6bc342ffa808ae6f33a8a168fa 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Native ALSA output plugin."
+
 include(header.m4)
 
 <qu>
index 865df3ac4048b330daf9055ffb47c2b1a2999eb3..2d4d4ce6ca5166355fa99bdbf28884a2a213daff 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Amplify the decoded audio stream."
+
 option "amp" a
 #~~~~~~~~~~~~~
 "amplification value"
index baccc57ffbfebd41ea9848a5d3b8667a7103af41..ccee4ee62f5fc23903857f9ba29e0b470792548a 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Output plugin for libao."
+
 include(header.m4)
 <qu>
 
index 736d70727d6d643e543a8911da4c73ce209d65e6..36520bfa0f16888020d431d7001c40c17c00146a 100644 (file)
@@ -1,4 +1,6 @@
-args "--unamed-opts=command --conf-parser --no-handle-version"
+args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
+
+purpose "Communicate with para_audiod through a local socket."
 
 include(header.m4)
 <qu>
index 5522a56dc0af4b10bcf0dfcd3172e426a0ee174c..7bae3435d19f20ed0a4f496b947cd1195e17bad5 100644 (file)
@@ -1,5 +1,7 @@
 args "--no-handle-help --no-handle-version --conf-parser"
 
+purpose "Connect to para_server, receive, decode and play audio streams."
+
 include(header.m4)
 define(CURRENT_PROGRAM,para_audiod)
 define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf)
index 23f7c1e65bd7e5aeb23b14a478278596b417bed9..f0bbc0bd8eadf2000e69f2f95e41b22c146f4080 100644 (file)
@@ -1,4 +1,6 @@
-args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version"
+args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version --no-handle-help"
+
+purpose "Communicate with para_server through the paraslash control port."
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_client)
index 74dcbc0332753e1011962a62c9dbe961881b4d1f..8c701a03987fe6c8e21c25a40475483b6d0bd7f7 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Dynamically adjust the volume of an audio stream."
+
 option "blocksize" b
 #~~~~~~~~~~~~~~~~~~~
 "adjust block size"
index 80d312338b8811a48b3a9ac1da7c1fa888e0be40..f8191fe003bec99957155d71c8ef5a5be694376b 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Receive a DCCP audio stream."
+
 option "host" i
 "ip or host"
 string default="localhost"
index da7a27e2c632032ccce20a528e685bf3dd7a58a1..7c731c2b6ec1cdf15322131827fe99add0917e09 100644 (file)
@@ -1,4 +1,6 @@
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "An alarm clock and volume-fader for OSS and ALSA."
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_fade)
index 56c44f7a7a6096b0e7e338f9571168bb5f1c56d4..045b8657236b57ed58b8ececdd8cd183481acb53 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Output plugin that writes to a local file."
+
 option "filename" f
 #~~~~~~~~~~~~~~~~~~
 "specify output file name"
index c7337beb3a61989460067e4916bab1f6e3eabd27..baf6ecdb974a72ec7ceb8058d13e2264e1b448ad 100644 (file)
@@ -1,5 +1,7 @@
 args "--no-handle-help --no-handle-version --conf-parser"
 
+purpose "Decode or process audio data from STDIN to STDOUT."
+
 include(header.m4)
 include(loglevel.m4)
 <qu>
index bafd325bdea98d831db842ff08931815754aec53..1f5cf6e425bc506e8d7eae143a2b3a7319dd4a5e 100644 (file)
@@ -1,4 +1,6 @@
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "Show para_audiod status in a curses window."
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_gui)
index 14ae99ab4f7c3bedf672e81a743808ed96183cc6..b8ece39d9809a85dfa4e3e0d7b09afefd2475954 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Receive an HTTP audio stream."
+
 include(header.m4)
 
 <qu>
index 05be1379c4f665314e402849b1ecefba680dfa74..548a3d5d83bcb406a66dc26a77c0a840745d868f 100644 (file)
@@ -1,6 +1,6 @@
 define ggo_opts
        --output-dir=$(cmdline_dir) \
-       --set-version="$(PACKAGE_VERSION)" \
+       --set-version="$(GIT_VERSION) ($(codename))" \
        --arg-struct-name=$(*F)_args_info \
        --file-name=$(*F).cmdline \
        --func-name=$(*F)_cmdline_parser \
@@ -12,6 +12,9 @@ endef
 $(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdline_dir)
        @[ -z "$(Q)" ] || echo 'GGO $<'
        $(Q) $(GENGETOPT) $(ggo_opts) < $<
+ifeq ($(ggo_descriptions_declared),no)
+       echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h
+endif
 
 $(ggo_dir)/server.ggo $(ggo_dir)/audiod.ggo: \
        $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/color.m4 \
index c02f1583ba1ade9716c19ad3b9408b5c01932b20..a6425b3e7f296485101c52e8dc5fa72a9acac355 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Decode an mp3 stream."
+
 include(header.m4)
 
 <qu>
index 351561cd00c5daf9f8a00722a42a793d5bf49b6e..352bea5bc3cfe2e0d2d431c2e4b7c0fb1f31f60f 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Output plugin for the Open Sound System."
+
 option "device" d
 #~~~~~~~~~~~~~~~~
 "set PCM device"
index bee16d9e629c16acb8414407fefa8af2c1b7a992..5add6f2e6e340f5eacd4260d33e4166522751261 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Output plugin for Mac OS coreaudio."
+
 section "osx options"
 #####################
 
index 57f954c4c0ee094f3f8323d2d25956672f91a0f3..ea41f562fe504e00d0be096b83f174d4dc41129d 100644 (file)
@@ -1,4 +1,14 @@
 args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help"
+
+purpose "Command line audio player."
+
+description "para_play operates either in command mode or in insert
+mode. In insert mode it presents a prompt and allows to enter commands
+like stop, play, pause etc. In command mode the current audio file
+is shown and the program reads single key strokes from stdin. Keys
+may be mapped to commands. Whenever a mapped key is pressed, the
+associated command is executed."
+
 include(header.m4)
 define(CURRENT_PROGRAM,para_play)
 define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
index 7553e9578d5e9bdccecdda34c6391ad840ea210d..9e84dcfcea04934a7f5a2f3f776d5afabe0b6bc1 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Delay processing of an audio stream."
+
 option "duration" d
 #~~~~~~~~~~~~~~~~~~
 "prebuffer time"
index c7b89fe4981bb0dad0ea997b057e3611dd762f7e..086c9c0a5c73049630bda211a8433ddfebba3963 100644 (file)
@@ -1,5 +1,7 @@
 args "--no-handle-help --no-handle-version"
 
+purpose "A command line HTTP/DCCP/UDP stream grabber."
+
 include(header.m4)
 include(loglevel.m4)
 
index a4d081ff51852a5f6e39a41ad320da5b0b92e6c3..4f4af4b0c41182d21faecf888926488bb372095e 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Transform raw audio to a different sample rate."
+
 include(header.m4)
 
 option "converter" C
index 24d344e9e2fb41a3287c32bc1e2db1c52f918040..8707554f7719ace121dae2701e179c22ab687184 100644 (file)
@@ -1,4 +1,6 @@
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "Manage and stream audio files."
 
 include(header.m4)
 define(CURRENT_PROGRAM,para_server)
@@ -340,18 +342,19 @@ optional
 option "udp_header_interval" H
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 "duration for sending header"
-int typestr="ms"
-default="2000"
+int typestr = "ms"
+default = "2000"
 optional
-details="
+details = "
        As the udp sender has no idea about connected clients it
        sends the audio file header periodically if necessary. This
-       option is used to specify the duration of the interval between
-       sending the header. Shorter values decrease the average time
-       clients have to wait before being able to start playback,
-       but this also increases the amount network traffic. Note
-       that this affects only ogg vorbis streams as this is the only
-       audio format that needs an audio file header.
+       option specifies the duration between subsequent headers are
+       sent. Smaller values decrease the average time clients have
+       to wait before starting playback, larger values decrease
+       network traffic.
+
+       Note that this affects only ogg/* and wma streams. Other
+       audio formats, including mp3, don't need an audio file header.
 "
 
 option "udp_ttl" t
index 4c37eace5affa02a9cdb90e1aecbe65a12e9e184..48770d974dc495f205a1538367511600fee97281 100644 (file)
@@ -1,3 +1,7 @@
+args "--no-version --no-help"
+
+purpose "Receive an UDP audio stream."
+
 option "host" i
 "ip or host to receive udp packets from"
 string default="224.0.1.38"
index c022bc8f24c83d6ed490cde57159d64b7e571538..8b13f457a1480a3f430b31c478d5237ae9a0ba61 100644 (file)
@@ -1,5 +1,7 @@
 args "--no-handle-help --no-handle-version"
 
+purpose "Play wav or raw audio."
+
 include(header.m4)
 include(loglevel.m4)
 
diff --git a/mood.c b/mood.c
index 5d2d38b46637322ad93e802fce150b368cdae2e5..e06382e85cf2831ce638b27d0e5b057f759faac2 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -386,7 +386,7 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m)
        if (!*mood_name)
                return -E_DUMMY_ROW;
        mlpd.m = alloc_new_mood(mood_name);
-       ret = for_each_line_ro(mood_def.data, mood_def.size,
+       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        osl_close_disk_object(&mood_def);
        if (ret < 0) {
@@ -418,7 +418,7 @@ static int check_mood(struct osl_row *mood_row, void *data)
        ret = para_printf(pb, "checking mood %s...\n", mood_name);
        if (ret < 0)
                goto out;
-       ret = for_each_line_ro(mood_def.data, mood_def.size,
+       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        if (ret < 0)
                para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
index 98627504e22aef64664dade778463404ef641c8b..33c0dfc9f7d5019484a17f97a47064fb75798358 100644 (file)
@@ -221,8 +221,5 @@ void mp3dec_filter_init(struct filter *f)
        f->pre_select = generic_filter_pre_select;
        f->post_select = mp3dec_post_select;
        f->execute = mp3dec_execute;
-       f->help = (struct ggo_help) {
-               .short_help = mp3dec_filter_args_info_help,
-               .detailed_help = mp3dec_filter_args_info_detailed_help
-       };
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter);
 }
index 9fe865045a8e96006813d48b4dc0ff59164dd460..5182ad238b81f706ebac2f6abdcaec3859b809dc 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -56,8 +56,10 @@ static int oss_mix_open(const char *dev, struct mixer_handle **handle)
                dev = "/dev/mixer";
        PARA_INFO_LOG("opening %s\n", dev);
        ret = para_open(dev, O_RDWR, 42);
-       if (ret < 0)
+       if (ret < 0) {
+               PARA_ERROR_LOG("could not open %s\n", dev);
                return ret;
+       }
        h = para_malloc(sizeof(*h));
        h->fd = ret;
        *handle = h;
index cd327243bb008f912013c0de725b024885d24bf4..3c61a445eec2456ec5fdcf585a9d95753d4cd71c 100644 (file)
@@ -235,9 +235,6 @@ void oss_write_init(struct writer *w)
        w->post_select = oss_post_select;
        w->parse_config_or_die = oss_parse_config_or_die;
        w->free_config = oss_free_config;
-       w->help = (struct ggo_help) {
-               .short_help = oss_write_args_info_help,
-               .detailed_help = oss_write_args_info_detailed_help
-       };
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(oss_write);
        oss_write_cmdline_parser_free(&dummy);
 }
index 41b190304471458884ac26bf74013e2c4a864b2a..f1e308d8beb498586f60acbe1fa93b24077bc320 100644 (file)
@@ -366,9 +366,6 @@ void osx_write_init(struct writer *w)
        w->post_select = osx_write_post_select;
        w->parse_config_or_die = osx_write_parse_config_or_die;
        w->free_config = osx_free_config;
-       w->help = (struct ggo_help) {
-               .short_help = osx_write_args_info_help,
-               .detailed_help = osx_write_args_info_detailed_help
-       };
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write);
        osx_write_cmdline_parser_free(&dummy);
 }
diff --git a/play.c b/play.c
index 2cb0536dfc410eae63c0484ec9bd5535a50fe5ae..6eed58ab53779dd22afb9442d002b60c71165525 100644 (file)
--- a/play.c
+++ b/play.c
@@ -137,30 +137,14 @@ static void check_afh_receiver_or_die(void)
        exit(EXIT_FAILURE);
 }
 
-/** Description to be included in the --detailed-help output. */
-#define PP_DESC \
-"para_play is a command line audio player.\n" \
-"\n" \
-"It operates either in command mode or in insert mode. In insert mode it\n" \
-"presents a prompt and allows to enter para_play commands like stop, play, pause\n" \
-"etc. In command mode, the current audio file is shown and the program reads\n" \
-"single key strokes from stdin. Keys may be mapped to para_play commands.\n" \
-"Whenever a mapped key is pressed, the associated command is executed.\n" \
-
 __noreturn static void print_help_and_die(void)
 {
-       int d = conf.detailed_help_given;
-       const char **p = d? play_args_info_detailed_help
-               : play_args_info_help;
-
-//     printf_or_die("%s\n\n", PLAY_CMDLINE_PARSER_PACKAGE "-"
-//             PLAY_CMDLINE_PARSER_VERSION);
-
-       printf_or_die("%s\n\n", play_args_info_usage);
-       if (d)
-               printf_or_die("%s\n", PP_DESC);
-       for (; *p; p++)
-               printf_or_die("%s\n", *p);
+       struct ggo_help help = DEFINE_GGO_HELP(play);
+       unsigned flags = conf.detailed_help_given?
+               GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+
+       ggo_print_help(&help, flags);
+       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
        exit(0);
 }
 
@@ -176,12 +160,11 @@ static void parse_config_or_die(int argc, char *argv[])
                .print_errors = 1
        };
 
-       if (play_cmdline_parser_ext(argc, argv, &conf, &params))
-               exit(EXIT_FAILURE);
-       HANDLE_VERSION_FLAG("play", conf);
+       play_cmdline_parser_ext(argc, argv, &conf, &params);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("play", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        if (conf.config_file_given)
                config_file = para_strdup(conf.config_file_arg);
        else {
@@ -198,6 +181,7 @@ static void parse_config_or_die(int argc, char *argv[])
                params.initialize = 0;
                params.check_required = 1;
                play_cmdline_parser_config_file(config_file, &conf, &params);
+               loglevel = get_loglevel_by_name(conf.loglevel_arg);
        }
        for (i = 0; i < conf.key_map_given; i++) {
                char *s = strchr(conf.key_map_arg[i] + 1, ':');
@@ -990,15 +974,7 @@ out:
 
 static int play_i9e_line_handler(char *line)
 {
-       struct play_task *pt = &play_task;
-       int ret;
-
-       if (line == NULL || !*line)
-               return 0;
-       ret = run_command(line, pt);
-       if (ret < 0)
-               return ret;
-       return 0;
+       return run_command(line, &play_task);
 }
 
 static int play_i9e_key_handler(int key)
@@ -1044,7 +1020,7 @@ static void session_open(__a_unused struct play_task *pt)
        char *history_file;
        struct sigaction act;
 
-       PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("play"));
+       PARA_NOTICE_LOG("\n%s\n", version_text("play"));
        if (conf.history_file_given)
                history_file = para_strdup(conf.history_file_arg);
        else {
index 60e27e6e6fc104034b13e054c330c4ce484749a6..c822e0802c53fdfbb72b5d728e0ca84aabbedc45 100644 (file)
@@ -70,8 +70,8 @@ static int load_playlist(struct osl_row *row, void *data)
        if (ret < 0)
                goto err;
        playlist->length = 0;
-       ret = for_each_line_ro(playlist_def.data, playlist_def.size,
-               add_playlist_entry, playlist);
+       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               playlist_def.size, add_playlist_entry, playlist);
        osl_close_disk_object(&playlist_def);
        if (ret < 0)
                goto err;
@@ -114,8 +114,8 @@ static int check_playlist(struct osl_row *row, void *data)
                ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
                if (ret < 0)
                        return ret;
-               ret = for_each_line_ro(playlist_def.data, playlist_def.size,
-                       check_playlist_path, pb);
+               ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+                       playlist_def.size, check_playlist_path, pb);
        }
        osl_close_disk_object(&playlist_def);
        return ret;
@@ -219,8 +219,8 @@ static int handle_audio_file_event(enum afs_events event, void *data)
        ret = pl_get_def_by_name(current_playlist.name, &playlist_def);
        if (ret < 0)
                return ret;
-       ret = for_each_line_ro(playlist_def.data, playlist_def.size,
-               search_path, new_path);
+       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               playlist_def.size, search_path, new_path);
        osl_close_disk_object(&playlist_def);
        is_admissible = (ret < 0);
        if (was_admissible && is_admissible)
index 0759d678bc51ca90284108e3a5d5bc25a4c3fc4a..655c981b921e0a0b484b5567ad9d448077d39631 100644 (file)
@@ -123,8 +123,5 @@ void prebuffer_filter_init(struct filter *f)
        f->free_config = prebuffer_free_config;
        f->pre_select = prebuffer_pre_select;
        f->post_select = prebuffer_post_select;
-       f->help = (struct ggo_help) {
-               .short_help = prebuffer_filter_args_info_help,
-               .detailed_help = prebuffer_filter_args_info_detailed_help
-       };
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter);
 }
diff --git a/recv.c b/recv.c
index 1fb5e25e700ea6bb6b2975fef7d6a34523b8cd9f..7a5549aaa27fe49f267d5cdcf122e8b244649f34 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -39,28 +39,12 @@ INIT_RECV_ERRLISTS;
 
 __noreturn static void print_help_and_die(void)
 {
-       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);
-}
+       struct ggo_help h = DEFINE_GGO_HELP(recv);
+       bool d = conf.detailed_help_given;
 
-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.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       return check_receiver_arg(conf.receiver_arg, receiver_num);
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
+       exit(0);
 }
 
 /**
@@ -82,16 +66,18 @@ int main(int argc, char *argv[])
        struct stdout_task sot;
        static struct sched s;
 
-       s.default_timeout.tv_sec = 1;
-       s.default_timeout.tv_usec = 0;
+       recv_cmdline_parser(argc, argv, &conf);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       version_handle_flag("recv", conf.version_given);
+       recv_init();
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
 
-       memset(&sot, 0, sizeof(struct stdout_task));
        memset(&rn, 0, sizeof(struct receiver_node));
-       recv_init();
-       ret = -E_RECV_SYNTAX;
-       rn.conf = parse_config(argc, argv, &receiver_num);
+       rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num);
        if (!rn.conf) {
-               PARA_EMERG_LOG("parse failed\n");
+               PARA_EMERG_LOG("invalid receiver specifier\n");
+               ret = -E_RECV_SYNTAX;
                goto out;
        }
        r = &receivers[receiver_num];
@@ -103,6 +89,7 @@ int main(int argc, char *argv[])
                goto out;
        r_opened = 1;
 
+       memset(&sot, 0, sizeof(struct stdout_task));
        sot.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.parent = rn.btrn, .name = "stdout"));
        stdout_set_defaults(&sot);
@@ -113,6 +100,8 @@ int main(int argc, char *argv[])
        sprintf(rn.task.status, "%s", r->name);
        register_task(&s, &rn.task);
 
+       s.default_timeout.tv_sec = 1;
+       s.default_timeout.tv_usec = 0;
        ret = schedule(&s);
 out:
        if (r_opened)
diff --git a/recv.h b/recv.h
index cc44a602a9f3ca2f63c68c8c984fb83c87f82703..008af39a8f7960d8bc0731b917642b67c44f7108 100644 (file)
--- a/recv.h
+++ b/recv.h
@@ -141,7 +141,7 @@ struct receiver {
 
 void recv_init(void);
 void *check_receiver_arg(char *ra, int *receiver_num);
-void print_receiver_helps(int detailed);
+void print_receiver_helps(unsigned flags);
 int generic_recv_pre_select(struct sched *s, struct task *t);
 
 /** \cond receiver */
index f899c301e4205070403e885325a1e98f8d0b69ca..2ea8a5992034419cf0375bae43851172c136b765 100644 (file)
@@ -92,22 +92,23 @@ void *check_receiver_arg(char *ra, int *receiver_num)
 /**
  * Print out the help texts to all receivers.
  *
- * \param detailed Whether the detailed help should be printed.
+ * \param flags Passed to \ref ggo_print_help().
  */
-void print_receiver_helps(int detailed)
+void print_receiver_helps(unsigned flags)
 {
        int i;
 
-       printf_or_die("\nAvailable receivers: \n\t");
+       printf_or_die("\nAvailable receivers: ");
        FOR_EACH_RECEIVER(i)
                printf_or_die("%s%s", i? " " : "", receivers[i].name);
-       printf_or_die("\n\n");
+       printf_or_die("\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);
+               printf_or_die("\n%s: %s", r->name,
+                       r->help.purpose);
+               ggo_print_help(&r->help, flags);
        }
 }
 
index bd8ece91ef13f27b389fa5385ac922784bb7124c..5ad584ded50eaad435f5afdfeedccac9f40bcd75 100644 (file)
@@ -310,8 +310,5 @@ void resample_filter_init(struct filter *f)
        f->parse_config = resample_parse_config;
        f->free_config = resample_free_config;
        f->execute = resample_execute;
-       f->help = (struct ggo_help) {
-               .short_help = resample_filter_args_info_help,
-               .detailed_help = resample_filter_args_info_detailed_help
-       };
+       f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter);
 }
index f3d5237f19cfbc69e01ca17851f698fda1b41779..f26abef5753204c4628c4183519f50f02b8f674e 100644 (file)
--- a/server.c
+++ b/server.c
@@ -91,6 +91,7 @@
 #include "signal.h"
 #include "user_list.h"
 #include "color.h"
+#include "ggo.h"
 #include "version.h"
 
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
@@ -236,13 +237,13 @@ void parse_config_or_die(int override)
                        .print_errors = !conf.daemon_given
                };
                server_cmdline_parser_config_file(cf, &conf, &params);
+               daemon_set_loglevel(conf.loglevel_arg);
                conf.daemon_given = tmp;
        }
        if (conf.logfile_given) {
                daemon_set_logfile(conf.logfile_arg);
                daemon_open_log_or_die();
        }
-       daemon_set_loglevel(conf.loglevel_arg);
        init_colors_or_die();
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
@@ -473,6 +474,15 @@ static int init_afs(int argc, char **argv)
        return afs_server_socket[0];
 }
 
+__noreturn static void print_help_and_die(void)
+{
+       struct ggo_help h = DEFINE_GGO_HELP(server);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       exit(0);
+}
+
 static void server_init(int argc, char **argv)
 {
        struct server_cmdline_parser_params params = {
@@ -488,7 +498,10 @@ static void server_init(int argc, char **argv)
        init_random_seed_or_die();
        /* parse command line options */
        server_cmdline_parser_ext(argc, argv, &conf, &params);
-       HANDLE_VERSION_FLAG("server", conf);
+       daemon_set_loglevel(conf.loglevel_arg);
+       version_handle_flag("server", conf.version_given);
+       if (conf.help_given || conf.detailed_help_given)
+               print_help_and_die();
        drop_privileges_or_die(conf.user_arg, conf.group_arg);
        /* parse config file, open log and set defaults */
        parse_config_or_die(0);
diff --git a/stdin.c b/stdin.c
index b25a0ba013fb03b19cb757d07ff483c9389a0d70..20b9250e9b7fbf3c252e20dff8891bfeeb5112dd 100644 (file)
--- a/stdin.c
+++ b/stdin.c
@@ -69,6 +69,12 @@ static int stdin_post_select(struct sched *s, struct task *t)
        sz = btr_pool_get_buffer(sit->btrp, &buf);
        if (sz == 0)
                return 0;
+       if (sit->must_set_nonblock_flag) {
+               ret = mark_fd_nonblocking(STDIN_FILENO);
+               if (ret < 0)
+                       goto err;
+               sit->must_set_nonblock_flag = false;
+       }
        /*
         * Do not use the maximal size to avoid having only a single buffer
         * reference for the whole pool. This is bad because if that single
@@ -82,6 +88,8 @@ static int stdin_post_select(struct sched *s, struct task *t)
                return 0;
 err:
        btr_remove_node(&sit->btrn);
+       /* Revert to blocking mode if necessary. */
+       fcntl(STDIN_FILENO, F_SETFL, sit->fd_flags);
        //btr_pool_free(sit->btrp);
        return ret;
 }
@@ -92,8 +100,7 @@ err:
  * \param sit The stdin task structure.
  *
  * This fills in the pre/post select function pointers of the task structure
- * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking
- * mode, and a buffer tree is created.
+ * given by \a sit and creates a buffer tree for I/O.
  */
 void stdin_set_defaults(struct stdin_task *sit)
 {
@@ -103,9 +110,18 @@ void stdin_set_defaults(struct stdin_task *sit)
        sit->task.post_select = stdin_post_select;
        sit->btrp = btr_pool_new("stdin", 128 * 1024);
        sprintf(sit->task.status, "stdin reader");
-       ret = mark_fd_nonblocking(STDIN_FILENO);
-       if (ret >= 0)
-               return;
-       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       exit(EXIT_FAILURE);
+       /*
+        * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file
+        * description (the terminal), and thus share the same file status
+        * flags. In order to not interfere with the stdout task, we only get
+        * the file status flags for STDIN here and save a copy. The nonblock
+        * flag is set later on the first read.
+        */
+       ret = fcntl(STDIN_FILENO, F_GETFL);
+       if (ret < 0) {
+               PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       sit->fd_flags = ret;
+       sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0;
 }
diff --git a/stdin.h b/stdin.h
index 54710ae561105bd7ad0ce907cf078d58d49a80f1..cdf0c67fd57a1078f38a9f175d598adf65ece520 100644 (file)
--- a/stdin.h
+++ b/stdin.h
@@ -14,6 +14,10 @@ struct stdin_task {
        struct btr_node *btrn;
        /** Use a buffer pool to minimize memcpy due to alignment problems. */
        struct btr_pool *btrp;
+       /** The descriptor flags of STDIN at startup. */
+       int fd_flags;
+       /** Whether we have to set STDIN to nonblocking mode. */
+       bool must_set_nonblock_flag;
 };
 
 void stdin_set_defaults(struct stdin_task *sit);
index abf3d06f17a64249671ffbfb0470aa995a599578..cf33bf6d0b67cf49625d27b4d602dec35922d32d 100644 (file)
--- a/stdout.c
+++ b/stdout.c
@@ -62,6 +62,12 @@ static int stdout_post_select(struct sched *s, struct task *t)
        if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
                return 0;
 
+       if (sot->must_set_nonblock_flag) {
+               ret = mark_fd_nonblocking(STDOUT_FILENO);
+               if (ret < 0)
+                       goto out;
+               sot->must_set_nonblock_flag = false;
+       }
        for (;;) {
                sz = btr_next_buffer(btrn, &buf);
                if (sz == 0)
@@ -72,8 +78,11 @@ static int stdout_post_select(struct sched *s, struct task *t)
                btr_consume(btrn, ret);
        }
 out:
-       if (ret < 0)
+       if (ret < 0) {
                btr_remove_node(&sot->btrn);
+               /* Revert to blocking mode if necessary. */
+               fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
+       }
        return ret;
 }
 /**
@@ -82,7 +91,7 @@ out:
  * \param sot The stdout task structure.
  *
  * This fills in the pre/post select function pointers of the task structure
- * given by \a sot and sets the stdout file descriptor to nonblocking mode.
+ * given by \a sot.
  */
 void stdout_set_defaults(struct stdout_task *sot)
 {
@@ -91,9 +100,13 @@ void stdout_set_defaults(struct stdout_task *sot)
        sot->task.pre_select = stdout_pre_select;
        sot->task.post_select = stdout_post_select;
        sprintf(sot->task.status, "stdout");
-       ret = mark_fd_nonblocking(STDOUT_FILENO);
-       if (ret >= 0)
-               return;
-       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       exit(EXIT_FAILURE);
+
+       /* See stdin.c for details. */
+       ret = fcntl(STDOUT_FILENO, F_GETFL);
+       if (ret < 0) {
+               PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       sot->fd_flags = ret;
+       sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0;
 }
index cc1f2626db049232601375688c8206ba24486177..a21b7cedfb699d9402f666a1a754a26d12cdcec1 100644 (file)
--- a/stdout.h
+++ b/stdout.h
@@ -16,6 +16,10 @@ struct stdout_task {
        struct task task;
        /** Stdout is always a leaf node in the buffer tree. */
        struct btr_node *btrn;
+       /** The descriptor flags of STDOUT at startup. */
+       int fd_flags;
+       /** Whether we have to set STDOUT to nonblocking mode. */
+       bool must_set_nonblock_flag;
 };
 
 void stdout_set_defaults(struct stdout_task *sot);
index dfcfa2cdf6eb776c63afdaec981aed55147a31b1..38e25b09c5f3ff2b289d6e943a0f7bde3bfc23ad 100644 (file)
--- a/string.c
+++ b/string.c
@@ -142,15 +142,18 @@ __must_check __malloc char *para_strdup(const char *s)
 __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
 {
        int ret;
-       size_t size;
+       size_t size = 150;
        va_list aq;
 
+       *result = para_malloc(size + 1);
        va_copy(aq, ap);
-       ret = vsnprintf(NULL, 0, fmt, aq);
+       ret = vsnprintf(*result, size, fmt, aq);
        va_end(aq);
        assert(ret >= 0);
+       if (ret < size) /* OK */
+               return ret;
        size = ret + 1;
-       *result = para_malloc(size);
+       *result = para_realloc(*result, size);
        va_copy(aq, ap);
        ret = vsnprintf(*result, size, fmt, aq);
        va_end(aq);
@@ -356,19 +359,35 @@ __malloc char *para_hostname(void)
 }
 
 /**
- * Used to distinguish between read-only and read-write mode.
+ * Call a custom function for each complete line.
+ *
+ * \param flags Any combination of flags defined in \ref for_each_line_flags.
+ * \param buf The buffer containing data separated by newlines.
+ * \param size The number of bytes in \a buf.
+ * \param line_handler The custom function.
+ * \param private_data Pointer passed to \a line_handler.
+ *
+ * For each complete line in \p buf, \p line_handler is called. The first
+ * argument to \p line_handler is (a copy of) the current line, and \p
+ * private_data is passed as the second argument.  If the \p FELF_READ_ONLY
+ * flag is unset, a pointer into \a buf is passed to the line handler,
+ * otherwise a pointer to a copy of the current line is passed instead. This
+ * copy is freed immediately after the line handler returns.
+ *
+ * The function returns if \p line_handler returns a negative value or no more
+ * lines are in the buffer.  The rest of the buffer (last chunk containing an
+ * incomplete line) is moved to the beginning of the buffer if FELF_READ_ONLY is
+ * unset.
  *
- * \sa for_each_line(), for_each_line_ro().
+ * \return On success this function returns the number of bytes not handled to
+ * \p line_handler. The only possible error is a negative return value from the
+ * line handler. In this case processing stops and the return value of the line
+ * handler is returned to indicate failure.
+ *
+ * \sa \ref for_each_line_flags.
  */
-enum for_each_line_modes{
-       /** Activate read-only mode. */
-       LINE_MODE_RO,
-       /** Activate read-write mode. */
-       LINE_MODE_RW
-};
-
-static int for_each_complete_line(enum for_each_line_modes mode, char *buf,
-               size_t size, line_handler_t *line_handler, void *private_data)
+int for_each_line(unsigned flags, char *buf, size_t size,
+               line_handler_t *line_handler, void *private_data)
 {
        char *start = buf, *end;
        int ret, i, num_lines = 0;
@@ -389,85 +408,29 @@ static int for_each_complete_line(enum for_each_line_modes mode, char *buf,
                } else
                        end = next_cr;
                num_lines++;
-               if (!line_handler) {
-                       start = ++end;
-                       continue;
-               }
-               if (mode == LINE_MODE_RO) {
-                       size_t s = end - start;
-                       char *b = para_malloc(s + 1);
-                       memcpy(b, start, s);
-                       b[s] = '\0';
-//                     PARA_NOTICE_LOG("b: %s, start: %s\n", b, start);
-                       ret = line_handler(b, private_data);
-                       free(b);
-               } else {
-                       *end = '\0';
-                       ret = line_handler(start, private_data);
+               if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
+                       if (flags & FELF_READ_ONLY) {
+                               size_t s = end - start;
+                               char *b = para_malloc(s + 1);
+                               memcpy(b, start, s);
+                               b[s] = '\0';
+                               ret = line_handler(b, private_data);
+                               free(b);
+                       } else {
+                               *end = '\0';
+                               ret = line_handler(start, private_data);
+                       }
+                       if (ret < 0)
+                               return ret;
                }
-               if (ret < 0)
-                       return ret;
                start = ++end;
        }
-       if (!line_handler || mode == LINE_MODE_RO)
-               return num_lines;
        i = buf + size - start;
-       if (i && i != size)
+       if (i && i != size && !(flags & FELF_READ_ONLY))
                memmove(buf, start, i);
        return i;
 }
 
-/**
- * Call a custom function for each complete line.
- *
- * \param buf The buffer containing data separated by newlines.
- * \param size The number of bytes in \a buf.
- * \param line_handler The custom function.
- * \param private_data Pointer passed to \a line_handler.
- *
- * If \p line_handler is \p NULL, the function returns the number of complete
- * lines in \p buf.  Otherwise, \p line_handler is called for each complete
- * line in \p buf.  The first argument to \p line_handler is the current line,
- * and \p private_data is passed as the second argument.  The function returns
- * if \p line_handler returns a negative value or no more lines are in the
- * buffer.  The rest of the buffer (last chunk containing an incomplete line)
- * is moved to the beginning of the buffer.
- *
- * \return If \p line_handler is not \p NULL, this function returns the number
- * of bytes not handled to \p line_handler on success, or the negative return
- * value of the \p line_handler on errors.
- *
- * \sa for_each_line_ro().
- */
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
-               void *private_data)
-{
-       return for_each_complete_line(LINE_MODE_RW, buf, size, line_handler,
-               private_data);
-}
-
-/**
- * Call a custom function for each complete line.
- *
- * \param buf Same meaning as in \p for_each_line().
- * \param size Same meaning as in \p for_each_line().
- * \param line_handler Same meaning as in \p for_each_line().
- * \param private_data Same meaning as in \p for_each_line().
- *
- * This function behaves like \p for_each_line(), but \a buf is left unchanged.
- *
- * \return On success, the function returns the number of complete lines in \p
- * buf, otherwise the (negative) return value of \p line_handler is returned.
- *
- * \sa for_each_line().
- */
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
-               void *private_data)
-{
-       return for_each_complete_line(LINE_MODE_RO, buf, size, line_handler,
-               private_data);
-}
-
 /** Return the hex characters of the lower 4 bits. */
 #define hex(a) (hexchar[(a) & 15])
 
index d45c9199822d993ab2594877a6cd45914a902f15..935c7d456c2c5b6910b356f2ddd52fd62848cf67 100644 (file)
--- a/string.h
+++ b/string.h
@@ -34,6 +34,23 @@ struct para_buffer {
        void *private_data;
 };
 
+/**
+ * Controls the behavior of for_each_line().
+ *
+ * \sa for_each_line().
+ */
+enum for_each_line_flags {
+       /** Activate read-only mode. */
+       FELF_READ_ONLY = 1 << 0,
+       /** Don't call line handler for the first input line. */
+       FELF_DISCARD_FIRST = 1 << 1,
+};
+
+/** Used for \ref for_each_line(). */
+typedef int line_handler_t(char *, void *);
+int for_each_line(unsigned flags, char *buf, size_t size,
+               line_handler_t *line_handler, void *private_data);
+
 /**
   * Write the contents of a status item to a para_buffer.
   *
@@ -72,12 +89,6 @@ __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
-/** Used for for_each_line() and for_each_line_ro(). */
-typedef int line_handler_t(char *, void *);
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
-       void *private_data);
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
-       void *private_data);
 int para_atoi64(const char *str, int64_t *result);
 int para_atoi32(const char *str, int32_t *value);
 int get_loglevel_by_name(const char *txt);
index f9c70ec83d9a32427574bd32984da14ee5269b7c..436b298cb8f467a25bf10c270a11ee08c74d6937 100644 (file)
@@ -100,15 +100,9 @@ static void udp_recv_close(struct receiver_node *rn)
 
 static void *udp_recv_parse_config(int argc, char **argv)
 {
-       int ret;
-       struct udp_recv_args_info *tmp =
-               para_calloc(sizeof(struct udp_recv_args_info));
-
-       ret = udp_recv_cmdline_parser(argc, argv, tmp)? -E_UDP_SYNTAX : 1;
-       if (ret >= 0)
-               return tmp;
-       free(tmp);
-       return NULL;
+       struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
+       udp_recv_cmdline_parser(argc, argv, tmp);
+       return tmp;
 }
 
 /*
@@ -226,9 +220,6 @@ void udp_recv_init(struct receiver *r)
        r->post_select = udp_recv_post_select;
        r->parse_config = udp_recv_parse_config;
        r->free_config = udp_recv_free_config;
-       r->help = (struct ggo_help) {
-               .short_help = udp_recv_args_info_help,
-               .detailed_help = udp_recv_args_info_detailed_help
-       };
+       r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv);
        udp_recv_cmdline_parser_free(&dummy);
 }
diff --git a/version.c b/version.c
new file mode 100644 (file)
index 0000000..ff6b7ca
--- /dev/null
+++ b/version.c
@@ -0,0 +1,78 @@
+#include "para.h"
+
+/** \file version.h Macros for printing the version string. */
+
+#include "git-version.h"
+
+/**
+ * Get the raw git version string
+ *
+ * \return The string generated by the GIT-VERSION-GEN script. It is passed
+ * as a preprocessor define during compilation.
+ */
+__a_const const char *version_git(void)
+{
+       return GIT_VERSION;
+}
+
+/**
+ * Get the version string for an executable.
+ *
+ * \param pfx The program name (without the leading "para_").
+ *
+ * \return A statically allocated string which contains the program name, the
+ * git version and the codename. It must not be freed by the caller.
+ */
+const char *version_single_line(const char *pfx)
+{
+       static char buf[100];
+       snprintf(buf, sizeof(buf) - 1,
+               "para_%s " GIT_VERSION, pfx);
+       return buf;
+}
+
+/**
+ * Get the full version text.
+ *
+ * \param pfx See \ref version_single_line().
+ *
+ * \return A string containing the same text as returned by \ref
+ * version_single_line(), augmented by additional build information, a
+ * copyright text and the email address of the author.
+ *
+ * Like \ref version_single_line(), this string is stored in a statically
+ * allocated buffer and must not be freed.
+ */
+const char *version_text(const char *pfx)
+{
+       static char buf[512];
+
+       snprintf(buf, sizeof(buf) - 1, "%s\n"
+               "Copyright (C) 2013 Andre Noll\n"
+               "This is free software with ABSOLUTELY NO WARRANTY."
+               " See COPYING for details.\n"
+               "Report bugs to <maan@systemlinux.org>.\n"
+               "build date: " BUILD_DATE ",\n"
+               "build system: " UNAME_RS ",\n"
+               "compiler: " CC_VERSION ".\n",
+               version_single_line(pfx)
+       );
+       return buf;
+}
+
+/**
+ * Print the version text and exit successfully.
+ *
+ * \param pfx See \ref version_single_line().
+ * \param flag Whether --version was given.
+ *
+ * If \a flag is false, this function does nothing. Otherwise it prints the
+ * full version text as returned by \ref version_text() and exits successfully.
+ */
+void version_handle_flag(const char *pfx, bool flag)
+{
+       if (!flag)
+               return;
+       printf("%s", version_text(pfx));
+       exit(EXIT_SUCCESS);
+}
index 3d865211788ac1ad516035af5c625dd2e24bd624..3dd5ba2177751e2c9716d4f0cdb99cfc5464c73a 100644 (file)
--- a/version.h
+++ b/version.h
@@ -1,19 +1,6 @@
-/** \file version.h Macros for printing the version string. */
-
-#include "git-version.h"
-
-/** Version text printed by all executables if -V was given. */
-#define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION \
-       " (" GIT_VERSION ": " CODENAME ")" "\n" \
-       "Copyright (C) 2013 Andre Noll\n" \
-       "This is free software with ABSOLUTELY NO WARRANTY." \
-       " See COPYING for details.\n" \
-       "Report bugs to <maan@systemlinux.org>.\n"
-
-/** Print out \p VERSION_TEXT and exit if version flag was given. */
-#define HANDLE_VERSION_FLAG(_prefix, _args_info_struct) \
-       if (_args_info_struct.version_given) { \
-               printf("%s", VERSION_TEXT(_prefix)); \
-               exit(EXIT_SUCCESS); \
-       }
+/** \file version.h Functions for printing the version string. */
 
+const char *version_git(void);
+const char *version_single_line(const char *pfx);
+const char *version_text(const char *pfx);
+void version_handle_flag(const char *pfx, bool flag);
index 563b61af5586402880d252cddcff5df1ebb4ce57..393fce9352d65a48f751735b886f5f57473dbfe3 100644 (file)
@@ -1,13 +1,73 @@
 <h1>Download</h1>
 <hr>
 
-<p> Clone the git repository by executing </p>
+Paraslash is only available as source code, no binary packages are
+provided at this point. There are several ways to download the source:
 
-<p> <b> git clone git://paraslash.systemlinux.org/git paraslash </b> </p>
+<ul>
+       <li> <em> git</em>.
 
-<p> Or grab the <a href="releases/paraslash-git.tar.bz2">tarball</a>
-of the current master branch, or download the latest version from the
-<a href="releases/">download directory</a>. All regular releases are
-<a href="PUBLIC_KEY">cryptographically signed</a>. Since development
-takes place in separate topic branches the master branch is expected
-to be more stable than any of the released versions. </p>
+               Clone the git repository by executing
+
+               <p> <pre> <kbd> git clone git://paraslash.systemlinux.org/git paraslash </kbd> </pre> </p>
+
+               <p> The repository contains the full history of the
+               project since 2006, all work in progress and the source
+               code for the web pages. Choosing this option allows to
+               check out any of the four integration branches maint,
+               master, next, pu (see the
+
+               <a href="manual.html#git_branches">git_branches</a>
+
+               section of the manual). All previous releases
+               correspond to tagged commits and may be checked out
+               as well. Since development takes place in separate
+               topic branches the master branch is expected to be
+               more stable than any of the released versions. </p>
+
+               <p> Compiling from git requires additional tools,
+               notably the autoconf package must be installed. </p>
+
+       </li>
+
+       <li> <em> regular releases</em>.
+
+               All released versions can be downloaded as compressed
+               tarballs from the
+
+               <a href="releases/">download directory</a>.
+
+               These tarballs are
+
+               <a href="PUBLIC_KEY">cryptographically signed</a>
+
+               and contain a pre-generated configure script. They
+               can be compiled and installed without autoconf.
+
+       </li>
+
+       <li> <em>master tarballs</em>.
+
+               Whenever significant changes are incorporated a
+
+               <a href="releases/paraslash-git.tar.bz2">tarball</a>
+
+               of the current master branch is created. All changes in
+               this tarball will be included in the next release. Like
+               for regular releases, a configure script is provided
+               for convenience.
+
+       </li>
+
+       <li> <em>gitweb snapshots</em>.
+
+               The
+
+                       <a href="/gitweb/gitweb.cgi?p=.git;a=summary">gitweb</a>
+
+               page contains a snapshot link for each revision. This
+               allows to get a specific revision without downloading
+               the full history.
+
+       </li>
+</ul>
diff --git a/write.c b/write.c
index 866ea43327b3d1389993e18b7277e29201a223da..6799db4175282b779c2fd454d11988be31b933d8 100644 (file)
--- a/write.c
+++ b/write.c
@@ -35,16 +35,11 @@ INIT_STDERR_LOGGING(loglevel)
 
 __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);
+       struct ggo_help h = DEFINE_GGO_HELP(write);
+       bool d = conf.detailed_help_given;
+
+       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+       print_writer_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
        exit(0);
 }
 
@@ -107,7 +102,6 @@ static int setup_and_schedule(void)
                },
        };
 
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
        sit.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdin"));
        stdin_set_defaults(&sit);
@@ -173,9 +167,10 @@ int main(int argc, char *argv[])
 {
        int ret;
 
-       writer_init();
        write_cmdline_parser(argc, argv, &conf);
-       HANDLE_VERSION_FLAG("write", conf);
+       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       writer_init();
+       version_handle_flag("write", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
 
index 44ccf88a16aed816cb6054e09419b232d5690895..33ef8be60d0a5fa04e2acd4d21f9b9ad7fc75d2a 100644 (file)
@@ -121,23 +121,24 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
 /**
  * Print the help text of all writers to stdout.
  *
- * \param detailed Whether to print the detailed help text.
+ * \param flags Passed to \ref ggo_print_help().
  */
-void print_writer_helps(int detailed)
+void print_writer_helps(unsigned flags)
 {
        int i;
 
-       printf_or_die("\nAvailable writers: \n\t");
+       printf_or_die("\nAvailable writers: ");
        FOR_EACH_WRITER(i)
                printf_or_die("%s%s", i? " " : "", writer_names[i]);
-       printf_or_die("\n\n");
+       printf_or_die("\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);
+               printf_or_die("\n%s: %s", writer_names[i],
+                       w->help.purpose);
+               ggo_print_help(&w->help, flags);
        }
 }
 
index 8788a4cf5053e2b4dd50ba3a4045d2a782125695..ecad2d18eb2fe37b9e9d39691238b9acefebb3a2 100644 (file)
@@ -8,7 +8,7 @@
 
 void writer_init(void);
 void *check_writer_arg_or_die(const char *wa, int *writer_num);
-void print_writer_helps(int detailed);
+void print_writer_helps(unsigned flags);
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s);
 void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);