From f5cf47f2bc4bb76d0d21e2467c5846cade38558f Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sat, 7 May 2016 10:59:01 +0200 Subject: [PATCH] Convert audiod commands to lopsub. 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. --- INSTALL | 6 ++ Makefile.in | 2 + Makefile.real | 21 +++- audioc.c | 15 ++- audiod.c | 1 + audiod.cmd | 75 ------------- audiod_command.c | 213 +++++++++++++++++++------------------ configure.ac | 20 ++++ error.h | 27 ++++- grab_client.c | 96 ++++++++--------- grab_client.h | 2 +- m4/lls/audiod_cmd.suite.m4 | 100 +++++++++++++++++ m4/lls/makefile | 21 ++++ man_util.bash | 13 ++- web/manual.md | 7 ++ 15 files changed, 376 insertions(+), 243 deletions(-) create mode 100644 m4/lls/audiod_cmd.suite.m4 create mode 100644 m4/lls/makefile diff --git a/INSTALL b/INSTALL index 85b4fab1..d0e8a790 100644 --- 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 diff --git a/Makefile.in b/Makefile.in index ec55c8e3..986877bc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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@ diff --git a/Makefile.real b/Makefile.real index 8ededf6a..6ff5abf0 100644 --- a/Makefile.real +++ b/Makefile.real @@ -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 \ diff --git a/audioc.c b/audioc.c index f8fd80fa..dabc2f77 100644 --- a/audioc.c +++ b/audioc.c @@ -16,6 +16,8 @@ #include #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} }; diff --git a/audiod.c b/audiod.c index db69cf14..fa401916 100644 --- a/audiod.c +++ b/audiod.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "para.h" #include "error.h" diff --git a/audiod.cmd b/audiod.cmd index 18c802de..69224970 100644 --- a/audiod.cmd +++ b/audiod.cmd @@ -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. diff --git a/audiod_command.c b/audiod_command.c index 3a390275..8a87d91e 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -13,10 +13,11 @@ #include #include #include +#include #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" @@ -35,35 +36,23 @@ 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); } diff --git a/configure.ac b/configure.ac index 0b00cc2a..36a1f4bd 100644 --- a/configure.ac +++ b/configure.ac @@ -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 899c574b..0af0dec9 100644 --- a/error.h +++ b/error.h @@ -135,7 +135,6 @@ 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); +} diff --git a/grab_client.c b/grab_client.c index 926f4792..11fff4cc 100644 --- a/grab_client.c +++ b/grab_client.c @@ -8,11 +8,14 @@ #include #include +#include +#include + +#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); diff --git a/grab_client.h b/grab_client.h index 7a752cee..3f3a0c03 100644 --- a/grab_client.h +++ b/grab_client.h @@ -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 index 00000000..11de9872 --- /dev/null +++ b/m4/lls/audiod_cmd.suite.m4 @@ -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 index 00000000..bd36addb --- /dev/null +++ b/m4/lls/makefile @@ -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) $< diff --git a/man_util.bash b/man_util.bash index cb7519c9..687acaab 100755 --- a/man_util.bash +++ b/man_util.bash @@ -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 diff --git a/web/manual.md b/web/manual.md index 12454ee2..f8d35e54 100644 --- a/web/manual.md +++ b/web/manual.md @@ -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. -- 2.39.5