From 2b9f5ff978bcbbe0a101a4cdf4ca7f69f14a148d Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 12 Jun 2016 20:46:10 +0200 Subject: [PATCH] Convert para_filter to lopsub. Changes the semantics in case --filter is not given. We used to print an error, but it makes more sense to show the list of supported filters in this case. If no filters are given, we now print the list of available filters. A new function in filter_common.c, print_filter_list(), is introduced for this purpose. The manual page of para_filter has been extended to include an overall description of the command. The only error code of filter.c, E_NO_FILTERS, becomes unused and is removed. --- Makefile.real | 4 +- configure.ac | 6 +- error.h | 1 - filter.c | 125 ++++++++++++++++++++++++++++------------- filter.h | 1 + filter_common.c | 20 +++++++ m4/gengetopt/filter.m4 | 25 --------- m4/lls/filter.suite.m4 | 37 ++++++++++++ 8 files changed, 146 insertions(+), 73 deletions(-) delete mode 100644 m4/gengetopt/filter.m4 create mode 100644 m4/lls/filter.suite.m4 diff --git a/Makefile.real b/Makefile.real index c229a1f0..2051c3be 100644 --- a/Makefile.real +++ b/Makefile.real @@ -43,14 +43,14 @@ all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \ $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \ $(write_objs) $(afh_objs) $(play_objs)) deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d))) -converted_executables := audioc client fade play recv write +converted_executables := audioc client fade play recv write filter unconverted_executables := $(filter-out $(converted_executables), $(executables)) audioc_objs += audioc.lsg.o audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) client.lsg.o client_objs += client.lsg.o fade_objs += fade.lsg.o -filter_objs += filter_cmd.lsg.o +filter_objs += filter_cmd.lsg.o filter.lsg.o play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o recv_objs += recv_cmd.lsg.o recv.lsg.o server_objs += server_cmd.lsg.o diff --git a/configure.ac b/configure.ac index d700323f..e310e2f8 100644 --- a/configure.ac +++ b/configure.ac @@ -681,7 +681,6 @@ filter_errlist_objs=" sched fd amp_filter - ggo fecdec_filter fec version @@ -695,9 +694,6 @@ filter_errlist_objs=" net sync_filter " -filter_cmdline_objs=" - filter -" NEED_VORBIS_OBJECTS && filter_errlist_objs="$filter_errlist_objs oggdec_filter" NEED_SPEEX_OBJECTS && filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common" NEED_OPUS_OBJECTS && filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common" @@ -711,7 +707,7 @@ fi if test $HAVE_SAMPLERATE = yes; then filter_errlist_objs="$filter_errlist_objs resample_filter check_wav" fi -filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs" +filter_objs="$filter_errlist_objs" AC_SUBST(filter_objs, add_dot_o($filter_objs)) ########################################################################## recv diff --git a/error.h b/error.h index 9c8d7d34..56dbea2b 100644 --- a/error.h +++ b/error.h @@ -153,7 +153,6 @@ PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \ PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \ PARA_ERROR(NOFD, "did not receive open fd from afs"), \ - PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \ PARA_ERROR(NO_MATCH, "no matches"), \ PARA_ERROR(NO_MOOD, "no mood available"), \ PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \ diff --git a/filter.c b/filter.c index 1deaf6da..d5dac675 100644 --- a/filter.c +++ b/filter.c @@ -7,22 +7,31 @@ /** \file filter.c The stand-alone filter program. */ #include +#include +#include "filter.lsg.h" +#include "filter_cmd.lsg.h" #include "para.h" -#include "filter.cmdline.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "stdin.h" #include "stdout.h" #include "error.h" +#include "fd.h" #include "version.h" /** Array of error strings. */ DEFINE_PARA_ERRLIST; +static struct lls_parse_result *lpr; /* command line options */ + +#define CMD_PTR (lls_cmd(0, filter_suite)) +#define OPT_RESULT(_name) \ + (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr)) +#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name))) +#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name))) /** The list of all status items used by para_{server,audiod,gui}. */ const char *status_item_list[] = {STATUS_ITEM_ARRAY}; @@ -45,48 +54,75 @@ static struct stdout_task stdout_task_struct; /** Pointer to the stdout task. */ static struct stdout_task *sot = &stdout_task_struct; -/** Gengetopt struct that holds the command line args. */ -static struct filter_args_info conf; - static int loglevel; INIT_STDERR_LOGGING(loglevel); -__noreturn static void print_help_and_die(void) +static void handle_help_flag(void) { - 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); + char *help; + + if (OPT_GIVEN(DETAILED_HELP)) + help = lls_long_help(CMD_PTR); + else if (OPT_GIVEN(HELP)) + help = lls_short_help(CMD_PTR); + else + return; + printf("%s\n", help); + free(help); + print_filter_helps(OPT_GIVEN(DETAILED_HELP)); exit(EXIT_SUCCESS); } static int parse_config(void) { - static char *cf; /* config file */ + char *home, *cf; /* config file */ struct stat statbuf; + int ret; - version_handle_flag("filter", conf.version_given); - if (conf.help_given || conf.detailed_help_given) - print_help_and_die(); - if (!cf) { - char *home = para_homedir(); - cf = make_message("%s/.paraslash/filter.conf", home); - free(home); - } + version_handle_flag("filter", OPT_GIVEN(VERSION)); + handle_help_flag(); + home = para_homedir(); + cf = make_message("%s/.paraslash/filter.conf", home); + free(home); if (!stat(cf, &statbuf)) { - struct filter_cmdline_parser_params params = { - .override = 0, - .initialize = 0, - .check_required = 0, - .check_ambiguity = 0, - .print_errors = 1 - }; - filter_cmdline_parser_config_file(cf, &conf, ¶ms); - loglevel = get_loglevel_by_name(conf.loglevel_arg); + void *map; + size_t sz; + int cf_argc; + char **cf_argv, *errctx; + struct lls_parse_result *cf_lpr, *merged_lpr; + + ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); + if (ret != -E_EMPTY) { + if (ret < 0) + return ret; + ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, + &errctx)); + para_munmap(map, sz); + if (ret < 0) { + PARA_ERROR_LOG("syntax error in %s\n", cf); + return ret; + } + cf_argc = ret; + ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, + &errctx)); + lls_free_argv(cf_argv); + if (ret < 0) { + PARA_ERROR_LOG("parse error in %s\n", cf); + return ret; + } + ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, &errctx)); + lls_free_parse_result(cf_lpr, CMD_PTR); + if (ret < 0) + return ret; + lls_free_parse_result(lpr, CMD_PTR); + lpr = merged_lpr; + loglevel = OPT_UINT32_VAL(LOGLEVEL); + } + } + if (!OPT_GIVEN(FILTER)) { + print_filter_list(); + exit(EXIT_SUCCESS); } - if (!conf.filter_given) - return -E_NO_FILTERS; return 1; } @@ -109,20 +145,23 @@ int main(int argc, char *argv[]) const struct filter *f; struct btr_node *parent; struct filter_node **fns; - struct lls_parse_result *filter_lpr; + struct lls_parse_result *filter_lpr; /* per-filter options */ + char *errctx; - filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */ - loglevel = get_loglevel_by_name(conf.loglevel_arg); - ret = parse_config(); + ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx)); if (ret < 0) goto out; + loglevel = OPT_UINT32_VAL(LOGLEVEL); + ret = parse_config(); + if (ret < 0) + goto free_lpr; sit->btrn = btr_new_node(&(struct btr_node_description) EMBRACE(.name = "stdin")); stdin_task_register(sit, &s); - fns = para_malloc(conf.filter_given * sizeof(*fns)); - for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) { - char *fa = conf.filter_arg[i]; + fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns)); + for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) { + const char *fa = lls_string_val(i, OPT_RESULT(FILTER)); const char *name; struct filter_node *fn; struct task_info ti; @@ -168,8 +207,14 @@ int main(int argc, char *argv[]) free(fns); btr_remove_node(&sit->btrn); btr_remove_node(&sot->btrn); +free_lpr: + lls_free_parse_result(lpr, CMD_PTR); out: - if (ret < 0) - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + if (ret < 0) { + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + } exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/filter.h b/filter.h index ef8669a8..4537b220 100644 --- a/filter.h +++ b/filter.h @@ -113,6 +113,7 @@ struct filter { }; void print_filter_helps(bool detailed); +void print_filter_list(void); int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp); #define FILTER_CMD(_num) (lls_cmd(_num, filter_cmd_suite)) #define FILTER_CMD_OPT(_cmd, _opt) (lls_opt( \ diff --git a/filter_common.c b/filter_common.c index 5a5e9d03..991b3a1e 100644 --- a/filter_common.c +++ b/filter_common.c @@ -142,6 +142,26 @@ void print_filter_helps(bool detailed) } } +/** + * Print a short summary of all available filters to stdout. + * + * For each supported filter, the filter name and the purpose text is printed + * in a single line. Since no options are shown, the filter list is more + * concise than the text obtained from print_filter_helps(). + */ +void print_filter_list(void) +{ + int i; + + printf("Available filters:\n"); + FOR_EACH_FILTER(i) { + const struct lls_command *cmd = FILTER_CMD(i); + if (!filter_supported(i)) + continue; + printf("%-9s %s\n", filter_name(i), lls_purpose(cmd)); + } +} + /** * Set select timeout of the scheduler. * diff --git a/m4/gengetopt/filter.m4 b/m4/gengetopt/filter.m4 deleted file mode 100644 index b8b49f62..00000000 --- a/m4/gengetopt/filter.m4 +++ /dev/null @@ -1,25 +0,0 @@ -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) - -option "filter" f -#~~~~~~~~~~~~~~~~ -"Specify filter." -string typestr="filter_spec" -optional -multiple -details=" - May be given multiple times to 'pipe' the stream through - arbitrary many filters (without copying the data). The same - filter may appear more than once, order matters. - - Options for a particular filter may be specified for each - given '--filter' option separately. You will have to quote - these options like this: - - --filter 'compress --inertia 5 --damp 2' -" - diff --git a/m4/lls/filter.suite.m4 b/m4/lls/filter.suite.m4 new file mode 100644 index 00000000..0cd69ebd --- /dev/null +++ b/m4/lls/filter.suite.m4 @@ -0,0 +1,37 @@ +m4_define(PROGRAM, para_filter) +[suite filter] +version-string = GIT_VERSION() +[supercommand para_filter] + purpose = decode or process audio data from STDIN to STDOUT + [description] + This program allows to specify a chain of filters which transform the + audio stream read from STDIN. A common mode of operation is to decode + an mp3 file with the mp3dec filter, but many other filters exist which + transform the audio stream in different ways. + [/description] + m4_include(common-option-section.m4) + m4_include(help.m4) + m4_include(detailed-help.m4) + m4_include(version.m4) + m4_include(loglevel.m4) + m4_include(per-command-options-section.m4) + [option filter] + short_opt = f + summary = add one filter to the filter chain + arg_info = required_arg + arg_type = string + flag multiple + typestr = filter_spec + [help] + A filter specifier begins with the name of a supported filter, + optionally followed by zero or more options for the named filter. + Filter name and options must be separated by whitespace. If the + there is at least one option, the filter specifier needs to be quoted + like this: + + --filter 'compress --inertia 5 --damp 2' + + This option may be specified multiple times to 'pipe' the stream + through all given filters (in a single thread without copying the + data). The same filter may appear more than once, and order matters. + [/help] -- 2.39.5