]> git.tue.mpg.de Git - paraslash.git/commitdiff
Convert audiod commands to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 7 May 2016 08:59:01 +0000 (10:59 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
The four command lists (server, afs, audiod, play) and all executables
will be converted to the long option parser library (lopsub). This
first patch converts the audiod commands (on, off, cycle...) and adds
the necessary infrastructure to the build system. The option parser
for para_audiod is still generated by gengetopt and will be converted
in a subsequent patch.

The build system is updated to include an autoconf test which
checks for the lopsub library and the lopsubgen executable. If the
check fails, it prints instructions on how to download the lopsub
package. Moreover, a section on lopsub is added to the INSTALL file
and the library is listed as a required tool in the manual.

The options and help texts of all audiod commands are moved from
audiod.cmd to the new file audiod_cmd.suite.m4. Until all command
lists are converted, man_util.bash needs an ugly hack to deal with
the two kinds of files.

The help texts have been reworked slightly, but no syntactical
changes were performed. However, one side effect of the change is
that options to audiod commands now accept short and long options,
and that short options may be combined in the usual way.

The error subsystem of paraslash is extended to treat lopsub errors
analogous to errors from the osl libary: we reserve a new bit for
error codes returned from lopsub library functions and a lls() wrapper
function that must be used for all lopsub functions which return a
lopsub error code on failure. The E_INVALID_AUDIOD_CMD error code
can be removed since invalid commands are now detected by the lopsub
library, which returns its own error code in this case.

As a result of the conversion, struct audiod_command can be removed.
Command handlers now take a pointer to a lopsub parse result instead
of the (argc, argv) pair.

The patch also changes the completers for audiod commands in
audioc.c. to use the information in the generated audioc_cmd.lsg.h
header file instead of duplicating this information.

With the patch applied, para_audiod and para_audioc need to be linked
with -llopsub.

We still need to include ggo.h from audiod_command.c until receivers,
filters and writers have been converted as well.

15 files changed:
INSTALL
Makefile.in
Makefile.real
audioc.c
audiod.c
audiod.cmd
audiod_command.c
configure.ac
error.h
grab_client.c
grab_client.h
m4/lls/audiod_cmd.suite.m4 [new file with mode: 0644]
m4/lls/makefile [new file with mode: 0644]
man_util.bash
web/manual.md

diff --git a/INSTALL b/INSTALL
index 85b4fab1ff04219e136ea00fe6cfb2d1a5ea97ab..d0e8a7908cba07e2fbd5a2138d6fef3599c4982f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,11 @@
 Any knowledge of how to work with mouse and icons is not required.
 
+Installing lopsub
+~~~~~~~~~~~~~~~~~
+       git clone git://git.tuebingen.mpg.de/lopsub
+       cd lopsub && make && sudo make install
+       (see http://people.tuebingen.mpg.de/maan/lopsub/)
+
 Installing osl
 ~~~~~~~~~~~~~~
        git clone git://git.tuebingen.mpg.de/osl
index ec55c8e388a78a4fb88164aa0030320738e62c6e..986877bc2a20358c454cbeda0eda6e92b038346b 100644 (file)
@@ -39,6 +39,7 @@ osl_cppflags := @osl_cppflags@
 id3tag_cppflags := @id3tag_cppflags@
 openssl_cppflags := @openssl_cppflags@
 gcrypt_cppflags := @gcrypt_cppflags@
+lopsub_cppflags := @lopsub_cppflags@
 ogg_cppflags := @ogg_cppflags@
 mad_cppflags := @mad_cppflags@
 faad_cppflags := @faad_cppflags@
@@ -61,6 +62,7 @@ opus_ldflags := @opus_ldflags@
 faad_ldflags := @faad_ldflags@
 mad_ldflags := @mad_ldflags@
 flac_ldflags := @flac_ldflags@
+lopsub_ldflags := @lopsub_ldflags@
 oss_ldflags := @oss_ldflags@
 alsa_ldflags := @alsa_ldflags@
 pthread_ldflags := @pthread_ldflags@
index 8ededf6aff8300608a5939429779ecd7b1753626..6ff5abf02575722e4205e1ef941fa07e2edb972d 100644 (file)
@@ -33,6 +33,8 @@ cmdline_dir := $(build_dir)/cmdline
 cmdlist_dir := $(build_dir)/cmdlist
 m4depdir := $(build_dir)/m4deps
 help2man_dir := $(build_dir)/help2man
+lls_suite_dir := $(build_dir)/lls
+lls_m4_dir := m4/lls
 m4_ggo_dir := m4/gengetopt
 test_dir := t
 
@@ -43,6 +45,8 @@ all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \
 deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d)))
 m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(executables)))
 
+audiod_objs += audiod_cmd.lsg.o
+
 # now prefix all objects with object dir
 recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
 filter_objs := $(addprefix $(object_dir)/, $(filter_objs))
@@ -68,6 +72,7 @@ all: $(prefixed_executables) $(man_pages)
 man: $(man_pages)
 tarball: $(tarball)
 
+include $(lls_m4_dir)/makefile
 include $(m4_ggo_dir)/makefile
 include $(test_dir)/makefile.test
 ifeq ($(findstring clean, $(MAKECMDGOALS)),)
@@ -76,7 +81,7 @@ ifeq ($(findstring clean, $(MAKECMDGOALS)),)
 endif
 
 $(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir) \
-               $(help2man_dir) $(cmdlist_dir):
+               $(help2man_dir) $(cmdlist_dir) $(lls_suite_dir):
        $(Q) $(MKDIR_P) $@
 
 # When in doubt, use brute force (Ken Thompson)
@@ -96,6 +101,8 @@ CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -I/usr/local/include
 CPPFLAGS += -I$(cmdline_dir)
 CPPFLAGS += -I$(cmdlist_dir)
+CPPFLAGS += -I$(lls_suite_dir)
+CPPFLAGS += $(lopsub_cppflags)
 
 CFLAGS += -Os
 CFLAGS += -Wuninitialized
@@ -168,7 +175,7 @@ $(cmdlist_dir)/audiod.completion.h \
 
 server_command_lists := $(cmdlist_dir)/server.command_list.man \
        $(cmdlist_dir)/afs.command_list.man
-audiod_command_lists := $(cmdlist_dir)/audiod.command_list.man
+audiod_command_lists := $(lls_suite_dir)/audiod_cmd.lsg.man
 play_command_lists := $(cmdlist_dir)/play.command_list.man
 
 $(man_dir)/para_server.1: $(server_command_lists)
@@ -179,8 +186,9 @@ $(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists)
 $(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
 $(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
 
-$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
-               git-version.h | $(man_dir) $(help2man_dir)
+$(man_dir)/para_%.1: $(man_util_command_lists) git-version.h \
+               $(ggo_dir)/%.ggo man_util.bash \
+               | $(man_dir) $(help2man_dir)
        @[ -z "$(Q)" ] || echo 'MAN $<'
        $(Q) \
                COMMAND_LISTS="$(man_util_command_lists)" \
@@ -278,6 +286,7 @@ $(dep_dir)/%.d: %.c | $(dep_dir)
        $(Q) $(CC) $(CPPFLAGS) -MM -MG -MP -MT $@ -MT $(object_dir)/$(*F).o $< \
                | sed -e "s@ \([a-zA-Z0-9_]\{1,\}\.cmdline.h\)@ $(cmdline_dir)/\1@g" \
                -e "s@ \([a-zA-Z0-9_]\{1,\}\.command_list.h\)@ $(cmdlist_dir)/\1@g" \
+               -e "s@ \([a-zA-Z0-9_]\{1,\}.lsg.h\)@ $(lls_suite_dir)/\1@g" \
                -e "s@ \([a-zA-Z0-9_]\{1,\}\.completion.h\)@ $(cmdlist_dir)/\1@g" > $@
 
 para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
@@ -309,6 +318,10 @@ para_fade \
        $(oss_ldflags) \
        $(alsa_ldflags)
 
+para_audioc \
+para_audiod \
+: LDFLAGS += $(lopsub_ldflags)
+
 para_server \
 para_filter \
 para_audiod \
index f8fd80faa77bdea4a4f1139bb8168268ce59ddc3..dabc2f77eb18d8b705241acc0753fd2fbbe34736 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -16,6 +16,8 @@
 #include <signal.h>
 
 #include "audioc.cmdline.h"
+#include "audiod_cmd.lsg.h"
+
 #include "para.h"
 #include "error.h"
 #include "net.h"
@@ -72,7 +74,6 @@ fail:
 #include "sched.h"
 #include "buffer_tree.h"
 #include "interactive.h"
-#include "audiod.completion.h"
 
 static struct sched sched;
 
@@ -100,7 +101,7 @@ static void help_completer(struct i9e_completion_info *ci,
 static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_VERSION_OPTS, NULL};
 
        if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -110,7 +111,7 @@ static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
        char *sia[] = {STATUS_ITEM_ARRAY NULL};
-       char *opts[] = {"-p", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_STAT_OPTS, NULL};
 
        if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -121,12 +122,16 @@ static void stat_completer(struct i9e_completion_info *ci,
 static void grab_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-ms", "-ms", "-ma", "-p=", "-n=", "-o", NULL};
+       char *opts[] = {LSG_AUDIOD_CMD_GRAB_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
 static struct i9e_completer audiod_completers[] = {
-       AUDIOD_COMPLETERS
+#define LSG_AUDIOD_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_AUDIOD_CMD_SUBCOMMANDS
+#undef LSG_AUDIOD_CMD_CMD
        {.name = NULL}
 };
 
index db69cf141a3833a5227c12ccba3f81a9e357e3b3..fa4019160bf8148a8503fa856c5dac3f9103b260 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -15,6 +15,7 @@
 #include <netdb.h>
 #include <signal.h>
 #include <pwd.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
index 18c802de8857788449cb6dfbfa53823c7ef0da0e..692249703f07e30e3df1cb38444bf1ef3a786d7f 100644 (file)
@@ -2,78 +2,3 @@ BN: audiod
 SF: audiod_command.c
 SN: list of audiod commands
 ---
-N: cycle
-D: switch to next mode
-U: cycle
-H: on -> standby -> off -> on
----
-N: grab
-D: grab the audio stream
-L:
-U: -- grab [-m[{s|p|a}]] [-p=PARENT] [-n=NAME] [-o]
-H:
-H: grab ('splice') the audio stream at any position in the buffer
-H: tree and send that data back to the client.
-H:
-H: Options:
-H:
-H: -m  Change grab mode. Defaults to sloppy grab if not given.
-H:
-H:    -ms: sloppy grab
-H:    -mp: pedantic grab
-H:    -ma: aggressive grab
-H:
-H: The various grab modes only differ in what happens if an attempt to
-H: write the grabbed audio data would block. Sloppy mode ignores the
-H: write, pedantic mode aborts and aggressive mode tries to write anyway.
-H:
-H: -p  Grab output of node PARENT of the buffer tree.
-H:
-H: -n  Name of the new buffer tree node. Defaults to 'grab'.
-H:
-H: -o  One-shot mode: Stop grabbing if audio file changes.
----
-N: help
-D: display command list or help for given command
-U: help [command]
-H: When I was younger, so much younger than today, I never needed anybody's help
-H: in any way. But now these days are gone, I'm not so self assured. Now I find
-H: I've changed my mind and opened up the doors.
-H:                                                              -- Beatles: Help
----
-N: off
-D: deactivate para_audiod
-U: off
-H: Close connection to para_server and stop all decoders.
----
-N: on
-D: activate para_audiod
-U: on
-H: Establish connection to para_server, retrieve para_server's current status. If
-H: playing, start corresponding decoder. Otherwise stop all decoders.
----
-N: sb
-D: enter standby mode
-U: sb
-H: Stop all decoders but leave connection to para_server open.
----
-N: stat
-D: print status information
-U: stat [-p] [item1 ...]
-H: Dump given status items (all if none given) to stdout. If -p is given, use
-H: parser-friendly mode.
----
-N: tasks
-D: list current tasks
-U: tasks
-H: Print the list of task ids together with the status of each task.
----
-N: term
-D: terminate audiod
-U: term
-H: Stop all decoders, shut down connection to para_server and exit.
----
-N: version
-D: print the version of para_audiod
-U: version [-v]
-H: If the -v option is given, a more detailed version text is printed.
index 3a39027523a72c686d997d1b358912dc5dfd2363..8a87d91e108f4470f127d35538bb16ef920f5c76 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
 #include "para.h"
+#include "audiod_cmd.lsg.h"
 #include "audiod.cmdline.h"
-#include "audiod.command_list.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
-typedef int audiod_command_handler_t(int, int, char **);
-static audiod_command_handler_t AUDIOD_COMMAND_HANDLERS;
-
-/* Defines one command of para_audiod. */
-struct audiod_command {
-       const char *name;
-       /* Pointer to the function that handles the command. */
-       /*
-        * Command handlers must never never close their file descriptor. A
-        * positive return value tells audiod that the status items have
-        * changed. In this case audiod will send an updated version of all
-        * status items to to each connected stat client.
-        */
-       audiod_command_handler_t *handler;
-       /* One-line description. */
-       const char *description;
-       /* Summary of the command line options. */
-       const char *usage;
-       /* The long help text. */
-       const char *help;
-};
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
 
-static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
 
-/** Iterate over the array of all audiod commands. */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+       audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
 
-/** The maximal number of simultaneous connections. */
-#define MAX_STAT_CLIENTS 50
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+       /** Implementation of _cmd. */ \
+       const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 /** Flags used for the stat command of para_audiod. */
 enum stat_client_flags {
@@ -245,10 +234,11 @@ static int dump_commands(int fd)
        char *buf = para_strdup(""), *tmp = NULL;
        int i;
        ssize_t ret;
+       const struct lls_command *cmd;
 
-       FOR_EACH_COMMAND(i) {
-               tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
-                       audiod_cmds[i].description);
+       for (i = 1; (cmd = lls_cmd(i, audiod_cmd_suite)); i++) {
+               tmp = make_message("%s%s\t%s\n", buf, lls_command_name(cmd),
+                       lls_purpose(cmd));
                free(buf);
                buf = tmp;
        }
@@ -257,76 +247,80 @@ static int dump_commands(int fd)
        return ret;
 }
 
-static int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret;
-       char *buf;
-
-       if (argc < 2) {
-               ret = dump_commands(fd);
-               goto out;
+       int ret;
+       char *buf, *errctx;
+       const struct lls_command *cmd;
+
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx) {
+                       buf = make_message("%s\n", errctx);
+                       client_write(fd, buf);
+                       free(buf);
+                       free(errctx);
+               }
+               return ret;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[1]))
-                       continue;
-               buf = make_message(
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\tpara_audioc %s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       audiod_cmds[i].description,
-                       audiod_cmds[i].usage,
-                       audiod_cmds[i].help
-               );
-               ret = client_write(fd, buf);
+       if (lls_num_inputs(lpr) == 0)
+               return dump_commands(fd);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), audiod_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               buf = make_message("%s: %s\nAvailable commands:\n", errctx?
+                       errctx : lls_input(0, lpr), para_strerror(-ret));
+               if (client_write(fd, buf) >= 0)
+                       dump_commands(fd);
+               free(errctx);
                free(buf);
                goto out;
        }
-       ret = client_write(fd, "No such command. Available commands:\n");
-       if (ret > 0)
-               ret = dump_commands(fd);
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       ret = client_write(fd, buf);
+       free(buf);
 out:
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(help)
 
-static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
+       int ret;
        char *tl = get_task_list(&sched);
-       int ret = 1;
 
-       if (tl)
-               ret = client_write(fd, tl);
+       if (!tl) /* no tasks registered yet */
+               return 0;
+       ret = client_write(fd, tl);
        free(tl);
-       return ret < 0? ret : 0;
+       return ret;
 }
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
-static int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
+       const struct lls_opt_result *r;
+       unsigned num_inputs;
 
        ret = mark_fd_nonblocking(fd);
        if (ret < 0)
                return ret;
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-p", 2)) {
-                       parser_friendly = 1;
-                       b.flags = PBF_SIZE_PREFIX;
-               }
+       r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+       if (lls_opt_given(r) > 0) {
+               parser_friendly = 1;
+               b.flags = PBF_SIZE_PREFIX;
        }
-       if (i >= argc)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                mask--; /* set all bits */
-       for (; i < argc; i++) {
-               ret = stat_item_valid(argv[i]);
+       for (i = 0; i < num_inputs; i++) {
+               ret = stat_item_valid(lls_input(i, lpr));
                if (ret < 0)
                        return ret;
                mask |= (one << ret);
@@ -344,58 +338,61 @@ static int com_stat(int fd, int argc, char **argv)
        free(b.buf);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(stat)
 
-static int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
 {
-       int ret = grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, lpr, &sched);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(grab)
 
-static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        return -E_AUDIOD_TERM;
 }
+EXPORT_AUDIOD_CMD_HANDLER(term)
 
-static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_ON;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(on)
 
-static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_OFF;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(off)
 
-static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_STANDBY;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(sb)
 
-static int com_cycle(__a_unused int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        switch (audiod_status) {
-               case  AUDIOD_ON:
-                       return com_sb(fd, argc, argv);
-                       break;
-               case  AUDIOD_OFF:
-                       return com_on(fd, argc, argv);
-                       break;
-               case  AUDIOD_STANDBY:
-                       return com_off(fd, argc, argv);
-                       break;
+               case  AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+               case  AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+               case  AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
        }
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
 
-static int com_version(int fd, int argc, char **argv)
+static int com_version(int fd, struct lls_parse_result *lpr)
 {
        int ret;
        char *msg;
+       const struct lls_opt_result *r_v;
 
-       if (argc > 1 && strcmp(argv[1], "-v") == 0)
+       r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+       if (lls_opt_given(r_v))
                msg = make_message("%s", version_text("audiod"));
        else
                msg = make_message("%s\n", version_single_line("audiod"));
@@ -403,6 +400,7 @@ static int com_version(int fd, int argc, char **argv)
        free(msg);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(version)
 
 /**
  * Handle arriving connections on the local socket.
@@ -423,10 +421,14 @@ static int com_version(int fd, int argc, char **argv)
  * */
 int handle_connect(int accept_fd, fd_set *rfds)
 {
-       int i, argc, ret, clifd;
+       int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr;
+       char *errctx;
+       const struct audiod_command_info *aci;
 
        ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
@@ -443,18 +445,27 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret <= 0)
                goto out;
        argc = ret;
-       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[0]))
-                       continue;
-               ret = audiod_cmds[i].handler(clifd, argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       ret = -E_INVALID_AUDIOD_CMD;
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       aci = lls_user_data(cmd);
+       ret = aci->handler(clifd, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
        free_argv(argv);
        if (ret < 0 && ret != -E_CLIENT_WRITE) {
-               char *tmp = make_message("%s\n", para_strerror(-ret));
+               char *tmp;
+               if (errctx) {
+                       tmp = make_message("%s\n", errctx);
+                       free(errctx);
+                       client_write(clifd, tmp);
+                       free(tmp);
+               }
+               tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
        }
index 0b00cc2af9f006d492975a54210ce26f4e5aa56e..36a1f4bdd88ab90797d2802848eec02b6da70001 100644 (file)
@@ -67,6 +67,10 @@ AC_PATH_PROG([INSTALL], [install])
 test -z "$INSTALL" && AC_MSG_ERROR(
        [The install program is required to build this package])
 
+AC_PATH_PROG([lopsubgen], [lopsubgen])
+test -z "$lopsubgen" && AC_MSG_ERROR(
+       [lopsubgen is required to build this package])
+
 AC_PROG_CC
 AC_PROG_CPP
 
@@ -92,6 +96,22 @@ AC_CHECK_HEADER(osl.h, [], [HAVE_OSL=no])
 AC_CHECK_LIB([osl], [osl_open_table], [], [HAVE_OSL=no])
 LIB_SUBST_FLAGS(osl)
 UNSTASH_FLAGS
+######################################################################## lopsub
+STASH_FLAGS
+LIB_ARG_WITH([lopsub], [-llopsub])
+HAVE_LOPSUB=yes
+AC_CHECK_HEADER(lopsub.h, [], [HAVE_LOPSUB=no])
+AC_CHECK_LIB([lopsub], [lls_merge], [], [HAVE_LOPSUB=yes])
+if test $HAVE_LOPSUB = no; then AC_MSG_ERROR([
+       The lopsub library is required to build this software, but
+       the above checks indicate it is not installed on your system.
+       Run the following command to download a copy.
+               git clone git://git.tuebingen.mpg.de/lopsub.git
+       Install the library, then run this configure script again.
+])
+fi
+LIB_SUBST_FLAGS([lopsub])
+UNSTASH_FLAGS
 ######################################################################## openssl
 STASH_FLAGS
 HAVE_OPENSSL=yes
diff --git a/error.h b/error.h
index 899c574b315cd9e8ee7356eb29c1828e1dd6ae04..0af0dec9828858979ab5c8100d2423a851b2b11d 100644 (file)
--- a/error.h
+++ b/error.h
        PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
        PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
        PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \
-       PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
        PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
        PARA_ERROR(KEY_PERM, "unprotected private key"), \
        PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
@@ -287,19 +286,28 @@ extern const char * const para_errlist[];
  */
 #define OSL_ERROR_BIT 29
 
+#define LLS_ERROR_BIT 28
+
 /** Check whether the system error bit is set. */
 #define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT)))
 
 /** Check whether the osl error bit is set. */
 #define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT)))
 
+/** Check whether the lopsub error bit is set. */
+#define IS_LLS_ERROR(num) (!!((num) & (1 << LLS_ERROR_BIT)))
+
 /** Set the system error bit for the given number. */
 #define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT))
 
 /** Set the osl error bit for the given number. */
 #define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
 
+/** Set the lopsub error bit for the error code. */
+#define LLS_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << LLS_ERROR_BIT))
+
 static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
+static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror")));
 /**
  * Paraslash's version of strerror(3).
  *
@@ -314,6 +322,10 @@ _static_inline_ const char *para_strerror(int num)
                assert(weak_osl_strerror);
                return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
        }
+       if (IS_LLS_ERROR(num)) {
+               assert(weak_lls_strerror);
+               return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+       }
        if (IS_SYSTEM_ERROR(num))
                return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
        return para_errlist[num];
@@ -336,3 +348,16 @@ _static_inline_ int osl(int ret)
                return ret;
        return -OSL_ERRNO_TO_PARA_ERROR(-ret);
 }
+
+/**
+ * Wrapper for lopsub library calls.
+ *
+ * \param ret See osl().
+ * \return See osl().
+ */
+_static_inline_ int lls(int ret)
+{
+       if (ret >= 0)
+               return ret;
+       return -LLS_ERRNO_TO_PARA_ERROR(-ret);
+}
index 926f47927c7617f0aa53cae4eeec7f42026ce9f6..11fff4cc985db6716d14ee663f59facf764a2eeb 100644 (file)
@@ -8,11 +8,14 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <inttypes.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "grab_client.h"
 #include "audiod.h"
@@ -210,77 +213,62 @@ err:
        return ret;
 }
 
-static int gc_check_args(int argc, char **argv, struct grab_client *gc)
+static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
 {
-       int i;
+       const struct lls_opt_result *r;
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-m", 2)) {
-                       if (*(arg + 3))
-                               return -E_GC_SYNTAX;
-                       switch(*(arg + 2)) {
-                       case 's':
-                               gc->mode = GM_SLOPPY;
-                               continue;
-                       case 'a':
-                               gc->mode = GM_AGGRESSIVE;
-                               continue;
-                       case 'p':
-                               gc->mode = GM_PEDANTIC;
-                               continue;
-                       default:
-                               return -E_GC_SYNTAX;
-                       }
-               }
-               if (!strcmp(arg, "-o")) {
-                       gc->flags |= GF_ONE_SHOT;
-                       continue;
-               }
-               if (!strncmp(arg, "-p=", 3)) {
-                       gc->parent = para_strdup(arg + 3);
-                       continue;
-               }
-               if (!strncmp(arg, "-n=", 3)) {
-                       gc->name = para_strdup(arg + 3);
-                       continue;
-               }
-               return -E_GC_SYNTAX;
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_MODE, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               if (strcmp(arg, "s") == 0)
+                       gc->mode = GM_SLOPPY;
+               else if (strcmp(arg, "a") == 0)
+                       gc->mode = GM_AGGRESSIVE;
+               else if (strcmp(arg, "p") == 0)
+                       gc->mode = GM_PEDANTIC;
+               else
+                       return -E_GC_SYNTAX;
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_ONE_SHOT, lpr);
+       if (lls_opt_given(r) > 0)
+               gc->flags |= GF_ONE_SHOT;
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_PARENT, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->parent = para_strdup(arg);
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_NAME, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->name = para_strdup(arg);
        }
-       if (i != argc)
-               return -E_GC_SYNTAX;
        return 1;
 }
 
 /**
- * Check the command line options and allocate a grab_client structure.
+ * Create and activate a grab client.
  *
  * \param fd The file descriptor of the client.
- * \param argc Argument count.
- * \param argv Argument vector.
+ * \param lpr The parsed command line of the grab command.
  * \param s The scheduler to register the grab client task to.
  *
- * If the command line options given by \a argc and \a argv are valid.
- * allocate a struct grab_client and initialize it with this valid
- * configuration.
- *
- * If the new grab client can be added to an existing buffer tree, activate it.
- * Otherwise, add it to the inactive list for later activation.
+ * This function semantically parses the arguments given as options to the grab
+ * command. On success it allocates a struct grab_client, associates it with
+ * the given file descriptor and activates it. If the new grab client can not
+ * be attached to an existing buffer tree node it is put into the inactive list
+ * for later activation.
  *
  * \return Standard.
  */
-int grab_client_new(int fd, int argc, char **argv, struct sched *s)
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
        struct grab_client *gc = para_calloc(sizeof(struct grab_client));
 
-       ret = gc_check_args(argc, argv, gc);
+       ret = gc_check_args(lpr, gc);
        if (ret < 0)
                goto err_out;
        ret = dup(fd);
index 7a752cee96052fcc7516f9e4e58a1abe252e3e9b..3f3a0c036206de61e55cf2ca6c7f4dda23b839eb 100644 (file)
@@ -6,5 +6,5 @@
 
 /** \file grab_client.h exported symbols from grab_client.c */
 
-int grab_client_new(int fd, int argc, char **argv, struct sched *s);
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s);
 void activate_grab_clients(struct sched *s);
diff --git a/m4/lls/audiod_cmd.suite.m4 b/m4/lls/audiod_cmd.suite.m4
new file mode 100644 (file)
index 0000000..11de987
--- /dev/null
@@ -0,0 +1,100 @@
+[suite audiod_cmd]
+caption = list of audiod commands
+[subcommand cycle]
+       purpose = switch to next operating mode
+       [description]
+               Cycle through the three operation modes (on, standby, off).
+       [/description]
+
+[subcommand help]
+       purpose = display command list or help for given command
+       non-opts-name = [subcommand]
+       [description]
+               When I was younger, so much younger than today, I never needed
+               anybody's help in any way. But now these days are gone, I'm not so self
+               assured. Now I find I've changed my mind and opened up the doors. --
+               Beatles: Help
+       [/description]
+
+[subcommand grab]
+       purpose = grab the audio stream
+       [description]
+               grab ('splice') the audio stream at any position in the buffer tree
+               and send that data back to the client.
+       [/description]
+       [option mode]
+               short_opt = m
+               summary = change grab mode
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               default_val = s
+               [help]
+                       The various grab modes only differ in what happens if an attempt to
+                       write the grabbed audio data would block. Sloppy mode ("s") ignores
+                       the write, pedantic mode ("p") aborts and aggressive mode ("a")
+                       tries to write anyway.
+               [/help]
+       [option parent]
+               short_opt = p
+               summary = Grab output of the given node of the buffer tree
+               arg_info = required_arg
+               arg_type = string
+               typestr = node
+       [option name]
+               short_opt = n
+               summary = Name of the new buffer tree node. Defaults to 'grab'
+               arg_info = optional_arg
+               arg_type = string
+               typestr = string
+       [option one-shot]
+               short_opt = o
+               summary = One-shot mode: Stop grabbing if audio file changes
+
+[subcommand off]
+       purpose = deactivate para_audiod
+       [description]
+               Close connection to para_server and stop all decoders.
+       [/description]
+
+[subcommand on]
+       purpose = activate para_audiod
+       [description]
+               Establish connection to para_server, retrieve para_server's current
+               status. If playing, start corresponding decoder. Otherwise stop
+               all decoders.
+       [/description]
+
+[subcommand sb]
+       purpose = enter standby mode
+       [description]
+               Stop all decoders but leave connection to para_server open.
+       [/description]
+
+[subcommand stat]
+       purpose = print status information
+       non-opts-name = [item...]
+       [description]
+               Dump given status items (all if none given) to stdout.
+       [/description]
+       [option parser-friendly]
+               short_opt = p
+               summary = use parser-friendly output format
+
+[subcommand version]
+       purpose = print the version of para_audiod
+       [option verbose]
+               short_opt = v
+               summary = print detailed (multi-line) version text
+
+[subcommand tasks]
+       purpose = list current tasks
+       [description]
+               Print the list of task ids together with the status of each task.
+       [/description]
+
+[subcommand term]
+       purpose = terminate para_audiod
+       [description]
+               Stop all decoders, shut down connection to para_server and exit.
+       [/description]
diff --git a/m4/lls/makefile b/m4/lls/makefile
new file mode 100644 (file)
index 0000000..bd36add
--- /dev/null
@@ -0,0 +1,21 @@
+.PRECIOUS: $(lls_suite_dir)/%.suite
+
+$(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+       @[ -z "$(Q)" ] || echo 'M4 $<'
+       $(Q) $(M4) -Pg $< > $@
+
+$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGC $<'
+       $(Q) lopsubgen --gen-c --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGH $<'
+       $(Q) lopsubgen --gen-header --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGM $<'
+       $(Q) lopsubgen --gen-man --output-dir $(lls_suite_dir) < $<
+
+$(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
index cb7519c9c6fb0098c6a1f9dc8c39d29c3ead1bee..687acaab5510dab25cc83a2e65b319359d85b3d0 100755 (executable)
@@ -90,8 +90,15 @@ target="${target%.*}" # server, audiod, filter, ...
 link="$HELP2MAN_DIR/para_$target"
 
 cl_opts=
+tempfiles=
 for cl in $COMMAND_LISTS; do
-       cl_opts+=" --include $cl"
+       if [[ "$cl" =~ lsg ]]; then
+               tempfiles+=" $cl.man_util.$$"
+               sed -e '/^\.SH / s/$/]/1' -e '/^\.SH / s/^\.SH /[/1' "$cl" > "$cl.man_util.$$"
+               cl_opts+=" --include $cl.man_util.$$"
+       else
+               cl_opts+=" --include $cl"
+       fi
 done
 
 # Create a symlink para_$target, pointing to this script. This hack is
@@ -102,7 +109,9 @@ ln -sf "$PWD/$0" "$link"
 # This will call us again twice, with either --help-$target or --version given.
 $HELP2MAN --no-info --help-option "--help-$target" $cl_opts \
        "$link" > "$output_file"
-if (($? != 0)); then
+ret=$?
+rm -f $tempfiles
+if (($ret != 0)); then
        rm -f "$output_file"
        exit 1
 fi
index 12454ee2ae55ba131165ae2d442041879dad33b1..f8d35e548791f2ab3325fc71336f075edd13095f 100644 (file)
@@ -203,6 +203,13 @@ code repository, execute
 
                git clone git://git.tuebingen.mpg.de/osl
 
+- [lopsub](http://people.tuebingen.mpg.de/maan/lopsub/). The long
+option parser for subcommands generates the command line and config
+file parsers for all paraslash executables. Clone the source code
+repository with
+
+               git clone git://git.tuebingen.mpg.de/lopsub
+
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
 [clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
 supported. Clang version 1.1 or newer should work as well.