From 870fcd0379ab096184f42481541d668694da7c83 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 10 Apr 2016 22:50:17 +0200 Subject: [PATCH] Convert writers to lopsub. Similar to the previous commits for receivers and filters, this commit replaces the five gengetopt parsers for the alsa, ao, file, oss, osx writers by a lopsub suite and links para_write with -llopsub. This allows to get rid of the WRITER_ENUM and the writers array as a reference to each writer structure are stored in the lopsub user_data pointer. Moreover, ->init(), ->parse_config(), ->free_config() and ->ggo_help() of struct writer are not needed any more and can be removed. The patch also removes write_common.h and moves the few prototypes write.h. Now that receivers, filters and writers have all been converted, we may also stop to include ggo.h from audiod_command.c and play.c. As for the receivers and filters, t0005 needs slight adjustments due to the new section header in the man page. --- Makefile.in | 1 - Makefile.real | 12 ++- alsa_write.c | 66 ++++--------- ao_write.c | 133 ++++++++++---------------- audiod.c | 50 ++++------ audiod_command.c | 1 - configure.ac | 41 +------- file_write.c | 83 ++++++----------- m4/gengetopt/alsa_write.m4 | 36 -------- m4/gengetopt/ao_write.m4 | 30 ------ m4/gengetopt/file_write.m4 | 13 --- m4/gengetopt/oss_write.m4 | 11 --- m4/gengetopt/osx_write.m4 | 17 ---- m4/lls/write_cmd.suite.m4 | 87 +++++++++++++++++ man_util.bash | 18 +--- oss_write.c | 53 +++-------- osx_write.c | 42 ++------- play.c | 14 ++- t/t0005-man.sh | 7 +- write.c | 66 ++++--------- write.h | 64 +++++-------- write_common.c | 185 ++++++++++++++++++++++--------------- write_common.h | 16 ---- 23 files changed, 395 insertions(+), 651 deletions(-) delete mode 100644 m4/gengetopt/alsa_write.m4 delete mode 100644 m4/gengetopt/ao_write.m4 delete mode 100644 m4/gengetopt/file_write.m4 delete mode 100644 m4/gengetopt/oss_write.m4 delete mode 100644 m4/gengetopt/osx_write.m4 create mode 100644 m4/lls/write_cmd.suite.m4 delete mode 100644 write_common.h diff --git a/Makefile.in b/Makefile.in index e72b518c..c283b035 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,6 @@ HELP2MAN := @HELP2MAN@ ggo_descriptions_declared := @ggo_descriptions_declared@ executables := @executables@ -writers := @writers@ recv_objs := @recv_objs@ filter_objs := @filter_objs@ diff --git a/Makefile.real b/Makefile.real index 2dec9847..26a4ae51 100644 --- a/Makefile.real +++ b/Makefile.real @@ -47,13 +47,14 @@ converted_executables := audioc client fade play unconverted_executables := $(filter-out $(converted_executables), $(executables)) audioc_objs += audioc.lsg.o -audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod) client.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 -play_objs += $(addsuffix _cmd.lsg.o, recv filter play) play.lsg.o +play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o recv_objs += recv_cmd.lsg.o server_objs += server_cmd.lsg.o +write_objs += write_cmd.lsg.o m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(unconverted_executables))) m4_lls_deps := \ @@ -62,6 +63,7 @@ m4_lls_deps := \ play_cmd \ recv_cmd \ filter_cmd \ + write_cmd \ $(converted_executables) m4_lls_deps := $(addprefix $(lls_suite_dir)/, $(addsuffix .m4d, $(m4_lls_deps))) @@ -158,20 +160,23 @@ else endif audiod_command_lists := $(addprefix $(lls_suite_dir)/, \ - $(addsuffix _cmd.lsg.man, audiod recv filter)) + $(addsuffix _cmd.lsg.man, audiod recv filter write)) filter_command_lists := $(lls_suite_dir)/filter_cmd.lsg.man play_command_lists := $(lls_suite_dir)/play_cmd.lsg.man recv_command_lists := $(lls_suite_dir)/recv_cmd.lsg.man server_command_lists := $(lls_suite_dir)/server_cmd.lsg.man +write_command_lists := $(lls_suite_dir)/write_cmd.lsg.man $(man_dir)/para_server.1: $(server_command_lists) $(man_dir)/para_filter.1: $(filter_command_lists) +$(man_dir)/para_write.1: $(write_command_lists) $(man_dir)/para_audiod.1: $(audiod_command_lists) $(man_dir)/para_play.1: $(play_command_lists) $(man_dir)/para_recv.1: $(recv_command_lists) $(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists) $(man_dir)/para_filter.1: man_util_command_lists := $(filter_command_lists) +$(man_dir)/para_write.1: man_util_command_lists := $(write_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_recv.1: man_util_command_lists := $(recv_command_lists) @@ -320,6 +325,7 @@ para_filter \ para_play \ para_recv \ para_server \ +para_write \ : LDFLAGS += $(lopsub_ldflags) para_server \ diff --git a/alsa_write.c b/alsa_write.c index fd3b404c..3935c8c6 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -15,17 +15,16 @@ #include #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "fd.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" -#include "alsa_write.cmdline.h" #include "error.h" /** Data specific to the alsa writer. */ @@ -71,22 +70,22 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf) } /* Install PCM software and hardware configuration. */ -static int alsa_init(struct private_alsa_write_data *pad, - struct alsa_write_args_info *conf) +static int alsa_init(struct writer_node *wn) { + struct private_alsa_write_data *pad = wn->private_data; snd_pcm_hw_params_t *hwparams = NULL; snd_pcm_sw_params_t *swparams = NULL; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_uframes_t buffer_size, period_size; snd_output_t *output_log; int ret; - const char *msg; + const char *msg, *dev = WRITE_CMD_OPT_STRING_VAL(ALSA, DEVICE, wn->lpr); unsigned period_time; - PARA_INFO_LOG("opening %s\n", conf->device_arg); + PARA_INFO_LOG("opening %s\n", dev); msg = "unable to open pcm"; - ret = snd_pcm_open(&pad->handle, conf->device_arg, - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + ret = snd_pcm_open(&pad->handle, dev, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK); if (ret < 0) goto fail; ret = snd_pcm_hw_params_malloc(&hwparams); @@ -116,7 +115,8 @@ static int alsa_init(struct private_alsa_write_data *pad, if (ret < 0) goto fail; /* alsa wants microseconds */ - pad->buffer_time = conf->buffer_time_arg * 1000; + pad->buffer_time = 1000U * WRITE_CMD_OPT_UINT32_VAL(ALSA, BUFFER_TIME, + wn->lpr); msg = "could not set buffer time"; ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, &pad->buffer_time, NULL); @@ -295,7 +295,7 @@ again: if (bytes == 0) /* no data available */ return 0; - pad = para_calloc(sizeof(*pad)); + pad = wn->private_data = para_calloc(sizeof(*pad)); get_btr_sample_rate(btrn, &val); pad->sample_rate = val; get_btr_channels(btrn, &val); @@ -305,12 +305,12 @@ again: PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels, pad->sample_rate); - ret = alsa_init(pad, wn->conf); + ret = alsa_init(wn); if (ret < 0) { - free(pad); + free(wn->private_data); + wn->private_data = NULL; goto err; } - wn->private_data = pad; wn->min_iqs = pad->bytes_per_frame; goto again; } @@ -340,37 +340,9 @@ err: return ret; } -__malloc static void *alsa_parse_config_or_die(int argc, char **argv) -{ - struct alsa_write_args_info *conf = para_calloc(sizeof(*conf)); - - /* exits on errors */ - alsa_write_cmdline_parser(argc, argv, conf); - return conf; -} - -static void alsa_free_config(void *conf) -{ - alsa_write_cmdline_parser_free(conf); -} - -/** - * The init function of the alsa writer. - * - * \param w Pointer to the writer to initialize. - * - * \sa struct \ref writer. - */ -void alsa_write_init(struct writer *w) -{ - struct alsa_write_args_info dummy; +struct writer lsg_write_cmd_com_alsa_user_data = { - alsa_write_cmdline_parser_init(&dummy); - w->close = alsa_close; - w->pre_select = alsa_write_pre_select; - 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)DEFINE_GGO_HELP(alsa_write); - alsa_write_cmdline_parser_free(&dummy); -} + .pre_select = alsa_write_pre_select, + .post_select = alsa_write_post_select, + .close = alsa_close, +}; diff --git a/ao_write.c b/ao_write.c index 82d98f3e..e469a394 100644 --- a/ao_write.c +++ b/ao_write.c @@ -9,17 +9,16 @@ #include #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "fd.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" -#include "ao_write.cmdline.h" #include "error.h" struct private_aow_data { @@ -44,6 +43,7 @@ static void aow_close(struct writer_node *wn) ao_close(pawd->dev); free(pawd); wn->private_data = NULL; + ao_shutdown(); } static void aow_pre_select(struct sched *s, void *context) @@ -147,6 +147,38 @@ static int aow_open_device(int id, ao_sample_format *asf, ao_option *options, return -E_AO_OPEN_LIVE; } +static void aow_show_drivers(void) +{ + int i, j, num_drivers; + ao_info **driver_list; + + PARA_DEBUG_LOG("libao drivers available on this host:\n"); + PARA_DEBUG_LOG("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + + driver_list = ao_driver_info_list(&num_drivers); + + for (i = 0; i < num_drivers; i++) { + ao_info *info = driver_list[i]; + char *keys = NULL, *tmp = NULL; + + if (info->type == AO_TYPE_FILE) + continue; + PARA_DEBUG_LOG("%s: %s", info->short_name, info->name); + PARA_DEBUG_LOG("priority: %d", info->priority); + for (j = 0; j < info->option_count; j++) { + tmp = make_message("%s%s%s", keys? keys : "", + keys? ", " : "", + info->options[j]); + free(keys); + keys = tmp; + } + PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]"); + free(keys); + PARA_DEBUG_LOG("comment: %s", info->comment? + info->comment : "[none]"); + } +} + static int aow_init(struct writer_node *wn, unsigned sample_rate, unsigned channels, int sample_format) { @@ -154,12 +186,15 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate, ao_option *aoo = NULL; ao_sample_format asf; ao_info *info; + const struct lls_opt_result *r; + unsigned n; struct private_aow_data *pawd = para_malloc(sizeof(*pawd)); - struct ao_write_args_info *conf = wn->conf; - if (conf->driver_given) { + ao_initialize(); + aow_show_drivers(); + if (WRITE_CMD_OPT_GIVEN(AO, DRIVER, wn->lpr)) { ret = -E_AO_BAD_DRIVER; - id = ao_driver_id(conf->driver_arg); + id = ao_driver_id(WRITE_CMD_OPT_STRING_VAL(AO, DRIVER, wn->lpr)); } else { ret = -E_AO_DEFAULT_DRIVER; id = ao_default_driver_id(); @@ -173,8 +208,11 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate, goto fail; } PARA_INFO_LOG("using %s driver\n", info->short_name); - for (i = 0; i < conf->ao_option_given; i++) { - char *o = para_strdup(conf->ao_option_arg[i]), *value; + r = WRITE_CMD_OPT_RESULT(AO, AO_OPTION, wn->lpr); + n = lls_opt_given(r); + for (i = 0; i < n; i++) { + char *o = para_strdup(lls_string_val(i, r)); + char *value; ret = -E_AO_BAD_OPTION; value = strchr(o, ':'); @@ -379,78 +417,9 @@ out: return ret; } -__malloc static void *aow_parse_config_or_die(int argc, char **argv) -{ - struct ao_write_args_info *conf = para_calloc(sizeof(*conf)); - - /* exits on errors */ - ao_write_cmdline_parser(argc, argv, conf); - return conf; -} - -static void aow_free_config(void *conf) -{ - ao_write_cmdline_parser_free(conf); -} - -/** - * The init function of the ao writer. - * - * \param w Pointer to the writer to initialize. - * - * \sa struct writer. - */ -void ao_write_init(struct writer *w) -{ - struct ao_write_args_info dummy; - int i, j, num_drivers, num_lines; - ao_info **driver_list; - char **dh; /* detailed help */ - - ao_write_cmdline_parser_init(&dummy); - w->close = aow_close; - w->pre_select = aow_pre_select; - 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)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 */ - num_lines = i; - dh = para_malloc((num_lines + 3) * sizeof(char *)); - for (i = 0; i < num_lines; i++) - dh[i] = para_strdup(ao_write_args_info_detailed_help[i]); - dh[num_lines++] = para_strdup("libao drivers available on this host:"); - dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - - ao_initialize(); - driver_list = ao_driver_info_list(&num_drivers); - - for (i = 0; i < num_drivers; i++) { - ao_info *info = driver_list[i]; - char *keys = NULL, *tmp = NULL; - - if (info->type == AO_TYPE_FILE) - continue; - for (j = 0; j < info->option_count; j++) { - tmp = make_message("%s%s%s", keys? keys : "", - keys? ", " : "", - info->options[j]); - free(keys); - keys = tmp; - } - dh = para_realloc(dh, (num_lines + 6) * sizeof(char *)); - dh[num_lines++] = make_message("%s: %s", info->short_name, info->name); - dh[num_lines++] = make_message("priority: %d", info->priority); - dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]"); - dh[num_lines++] = make_message("comment: %s", info->comment? - info->comment : "[none]"); - dh[num_lines++] = para_strdup(NULL); - free(keys); - } - dh[num_lines] = NULL; - w->help.detailed_help = (const char **)dh; - ao_write_cmdline_parser_free(&dummy); -} +struct writer lsg_write_cmd_com_ao_user_data = { + .close = aow_close, + .pre_select = aow_pre_select, + .post_select = aow_post_select, +}; diff --git a/audiod.c b/audiod.c index 5f4acb43..ca8cd13b 100644 --- a/audiod.c +++ b/audiod.c @@ -37,7 +37,6 @@ #include "string.h" #include "fd.h" #include "write.h" -#include "write_common.h" #include "signal.h" #include "version.h" @@ -64,10 +63,10 @@ struct audio_format_info { struct lls_parse_result **filter_lpr; /** the number of filters that should be activated for this audio format */ unsigned int num_writers; - /** Array of writer numbers to be activated. */ - int *writer_nums; - /** pointer to the array of writer configurations */ - void **writer_conf; + /** Array of writer IDs to be activated. */ + int *wids; + /** Parsed writer command line(s) */ + struct lls_parse_result **writer_lpr; /** do not start receiver/filters/writer before this time */ struct timeval restart_barrier; }; @@ -493,13 +492,10 @@ static void close_receiver(int slot_num) static void writer_cleanup(struct writer_node *wn) { - struct writer *w; - if (!wn) return; - w = writers + wn->writer_num; - PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]); - w->close(wn); + PARA_INFO_LOG("closing %s\n", writer_name(wn->wid)); + writer_get(wn->wid)->close(wn); btr_remove_node(&wn->btrn); task_reap(&wn->task); } @@ -632,11 +628,11 @@ static void open_writers(struct slot_info *s) * sizeof(struct writer_node)); for (i = 0; i < a->num_writers; i++) { wn = s->wns + i; - wn->conf = a->writer_conf[i]; - wn->writer_num = a->writer_nums[i]; + wn->wid = a->wids[i]; + wn->lpr = a->writer_lpr[i]; register_writer_node(wn, parent, &sched); PARA_NOTICE_LOG("%s writer started in slot %d\n", - writer_names[a->writer_nums[i]], (int)(s - slot)); + writer_name(a->wids[i]), (int)(s - slot)); } } @@ -883,8 +879,7 @@ static int parse_writer_args(void) struct audio_format_info *a; for (i = 0; i < conf.writer_given; i++) { - void *wconf; - int j, nw, writer_num, af_mask; + int j, nw, af_mask; ret = parse_stream_command(conf.writer_arg[i], &cmd); if (ret < 0) @@ -894,32 +889,28 @@ static int parse_writer_args(void) a = afi + j; if ((af_mask & (1 << j)) == 0) /* no match */ continue; - wconf = check_writer_arg_or_die(cmd, &writer_num); nw = a->num_writers; - a->writer_nums = para_realloc(a->writer_nums, (nw + 1) * sizeof(int)); - a->writer_conf = para_realloc(a->writer_conf, (nw + 1) * sizeof(void *)); - a->writer_nums[nw] = writer_num; - a->writer_conf[nw] = wconf; + a->wids = para_realloc(a->wids, (nw + 1) * sizeof(int)); + a->writer_lpr = para_realloc(a->writer_lpr, + (nw + 1) * sizeof(struct lls_parse_result *)); + a->wids[nw] = check_writer_arg_or_die(cmd, + a->writer_lpr + nw); PARA_INFO_LOG("%s writer #%d: %s\n", audio_formats[j], - nw, writer_names[writer_num]); + nw, writer_name(a->wids[nw])); a->num_writers++; } } /* Use default writer for audio formats which are not yet set up. */ FOR_EACH_AUDIO_FORMAT(i) { - void *writer_conf; - int writer_num; a = afi + i; if (a->num_writers > 0) continue; /* already set up */ - writer_conf = check_writer_arg_or_die(NULL, &writer_num); - a->writer_nums = para_malloc(sizeof(int)); - a->writer_nums[0] = writer_num; - a->writer_conf = para_malloc(sizeof(void *)); - a->writer_conf[0] = writer_conf; a->num_writers = 1; + a->wids = para_malloc(sizeof(int)); + a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *)); + a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr); PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i], - writer_names[writer_num]); + writer_name(a->wids[0])); } return 1; } @@ -1504,7 +1495,6 @@ int main(int argc, char *argv[]) version_handle_flag("audiod", conf.version_given); /* init receivers/filters/writers early to make help work */ recv_init(); - writer_init(); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); daemon_set_priority(conf.priority_arg); diff --git a/audiod_command.c b/audiod_command.c index 8a87d91e..a26d1931 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -20,7 +20,6 @@ #include "audiod.cmdline.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "grab_client.h" diff --git a/configure.ac b/configure.ac index 62aa16d3..648f2a63 100644 --- a/configure.ac +++ b/configure.ac @@ -508,7 +508,6 @@ if test -n "$CRYPTOLIB"; then audiod_audio_formats="wma" audiod_cmdline_objs="$audiod_cmdline_objs audiod - file_write " audiod_errlist_objs="$audiod_errlist_objs audiod @@ -556,7 +555,6 @@ if test -n "$CRYPTOLIB"; then fi if test "$have_core_audio" = "yes"; then audiod_errlist_objs="$audiod_errlist_objs osx_write ipc" - audiod_cmdline_objs="$audiod_cmdline_objs osx_write" fi NEED_VORBIS_OBJECTS && { audiod_errlist_objs="$audiod_errlist_objs oggdec_filter" @@ -584,15 +582,12 @@ if test -n "$CRYPTOLIB"; then fi if test $HAVE_OSS = yes; then audiod_errlist_objs="$audiod_errlist_objs oss_write" - audiod_cmdline_objs="$audiod_cmdline_objs oss_write" fi if test $HAVE_ALSA = yes; then audiod_errlist_objs="$audiod_errlist_objs alsa_write" - audiod_cmdline_objs="$audiod_cmdline_objs alsa_write" fi NEED_AO_OBJECTS && { audiod_errlist_objs="$audiod_errlist_objs ao_write" - audiod_cmdline_objs="$audiod_cmdline_objs ao_write" } if test $HAVE_SAMPLERATE = yes; then audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav" @@ -828,12 +823,8 @@ play_errlist_objs=" version sync_filter " -play_cmdline_objs=" - file_write -" if test "$have_core_audio" = "yes"; then play_errlist_objs="$play_errlist_objs osx_write ipc" - play_cmdline_objs="$play_cmdline_objs osx_write" fi NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common" NEED_VORBIS_OBJECTS && { @@ -865,15 +856,12 @@ if test $HAVE_MAD = yes; then fi if test $HAVE_OSS = yes; then play_errlist_objs="$play_errlist_objs oss_write" - play_cmdline_objs="$play_cmdline_objs oss_write" fi if test $HAVE_ALSA = yes; then play_errlist_objs="$play_errlist_objs alsa_write" - play_cmdline_objs="$play_cmdline_objs alsa_write" fi NEED_AO_OBJECTS && { play_errlist_objs="$play_errlist_objs ao_write" - play_cmdline_objs="$play_cmdline_objs ao_write" } if test $HAVE_READLINE = yes; then play_errlist_objs="$play_errlist_objs interactive" @@ -882,12 +870,11 @@ if test $HAVE_SAMPLERATE = yes; then play_errlist_objs="$play_errlist_objs resample_filter check_wav" fi -play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs" +play_objs="$play_errlist_objs" AC_SUBST(play_objs, add_dot_o($play_objs)) ######################################################################### write write_cmdline_objs=" write - file_write " write_errlist_objs=" write @@ -903,46 +890,21 @@ write_errlist_objs=" check_wav version " -writers="file" -default_writer="FILE_WRITE" if test "$have_core_audio" = "yes"; then write_errlist_objs="$write_errlist_objs osx_write ipc" - write_cmdline_objs="$write_cmdline_objs osx_write" - writers="$writers osx" - default_writer="OSX_WRITE" fi NEED_AO_OBJECTS && { write_errlist_objs="$write_errlist_objs ao_write" - write_cmdline_objs="$write_cmdline_objs ao_write" - writers="$writers ao" - default_writer="AO_WRITE" } if test $HAVE_OSS = yes; then write_errlist_objs="$write_errlist_objs oss_write" - write_cmdline_objs="$write_cmdline_objs oss_write" - writers="$writers oss" - default_writer="OSS_WRITE" fi if test $HAVE_ALSA = yes; then write_errlist_objs="$write_errlist_objs alsa_write" - write_cmdline_objs="$write_cmdline_objs alsa_write" - writers="$writers alsa" - default_writer="ALSA_WRITE" fi -AC_SUBST(writers) write_objs="add_cmdline($write_cmdline_objs) $write_errlist_objs" AC_SUBST(write_objs, add_dot_o($write_objs)) -enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)" -AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS, - enum of supported writers) -AC_DEFINE_UNQUOTED(DEFAULT_WRITER, $default_writer, use this writer if none was specified) -names="$(for i in $writers; do printf \"$i\",' ' ; done)" -AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names) -inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer *); '; done)" -AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers) -array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)" -AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers) ######################################################################## audioc audioc_errlist_objs=" audioc @@ -1001,7 +963,6 @@ id3 version 2 support: $HAVE_ID3TAG faad: $HAVE_FAAD mp4v2: $HAVE_MP4V2 audio format handlers: $audio_format_handlers -writers: $writers para_server: $build_server para_gui: $build_gui diff --git a/file_write.c b/file_write.c index 5e66607e..9837e810 100644 --- a/file_write.c +++ b/file_write.c @@ -8,17 +8,16 @@ #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" #include "string.h" #include "fd.h" -#include "file_write.cmdline.h" #include "error.h" /** Data specific to the file writer. */ @@ -47,31 +46,31 @@ __must_check __malloc static char *random_filename(void) static int prepare_output_file(struct writer_node *wn) { - struct file_write_args_info *conf = wn->conf; - char *filename; - int ret; - struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd)); - - if (conf->filename_given) - filename = conf->filename_arg; - else - filename = random_filename(); - ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (!conf->filename_given) - free(filename); - if (ret < 0) - goto out; - pfwd->fd = ret; - ret = mark_fd_blocking(pfwd->fd); + const unsigned flags = O_WRONLY | O_CREAT, mode = S_IRUSR | S_IWUSR; + int ret, fd; + struct private_file_write_data *pfwd; + + + if (WRITE_CMD_OPT_GIVEN(FILE, FILENAME, wn->lpr)) { + const char *path = WRITE_CMD_OPT_STRING_VAL(FILE, FILENAME, + wn->lpr); + ret = para_open(path, flags, mode); + } else { + char *path = random_filename(); + ret = para_open(path, flags, mode); + free(path); + } if (ret < 0) - goto out_close; - wn->private_data = pfwd; + return ret; + fd = ret; + ret = mark_fd_blocking(fd); + if (ret < 0) { + close(fd); + return ret; + } + pfwd = wn->private_data = para_calloc(sizeof(*pfwd)); + pfwd->fd = fd; return 1; -out_close: - close(pfwd->fd); -out: - free(pfwd); - return ret; } static void file_write_pre_select(struct sched *s, void *context) @@ -131,31 +130,9 @@ out: return ret; } -__malloc static void *file_write_parse_config_or_die(int argc, char **argv) -{ - struct file_write_args_info *conf = para_calloc(sizeof(*conf)); - - /* exits on errors */ - file_write_cmdline_parser(argc, argv, conf); - return conf; -} - -static void file_write_free_config(void *conf) -{ - file_write_cmdline_parser_free(conf); -} - /** the init function of the file writer */ -void file_write_init(struct writer *w) -{ - struct file_write_args_info dummy; - - file_write_cmdline_parser_init(&dummy); - w->pre_select = file_write_pre_select; - w->post_select = file_write_post_select; - 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)DEFINE_GGO_HELP(file_write); - file_write_cmdline_parser_free(&dummy); -} +struct writer lsg_write_cmd_com_file_user_data = { + .pre_select = file_write_pre_select, + .post_select = file_write_post_select, + .close = file_write_close, +}; diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4 deleted file mode 100644 index b2c56218..00000000 --- a/m4/gengetopt/alsa_write.m4 +++ /dev/null @@ -1,36 +0,0 @@ -args "--no-version --no-help" - -purpose "Native ALSA output plugin" - -include(header.m4) - - -option "device" d -#~~~~~~~~~~~~~~~~ -"set PCM device" -string typestr = "device" -default = "default" -optional -details = " - Check for the presence of a /proc/asound/ directory to see if - ALSA is present in your kernel. The file /proc/asound/devices - contains all devices ALSA knows about. -" - -option "buffer-time" B -#~~~~~~~~~~~~~~~~~~~~~ -"duration of the ALSA buffer" -int typestr = "milliseconds" -default = "170" -optional -details = " - This is only a hint as ALSA might pick a slightly different - time, depending on the sound hardware. The chosen value is - shown in debug output as BUFFER_TIME. - - If synchronization between multiple clients is desired, - the same buffer time should be configured for all clients. -" - - - diff --git a/m4/gengetopt/ao_write.m4 b/m4/gengetopt/ao_write.m4 deleted file mode 100644 index 29112d71..00000000 --- a/m4/gengetopt/ao_write.m4 +++ /dev/null @@ -1,30 +0,0 @@ -args "--no-version --no-help" - -purpose "Output plugin for libao" - -include(header.m4) - - -option "driver" d -#~~~~~~~~~~~~~~~~ -"Select a output driver by name" -string typestr = "name" -optional -details = " - If this is not given, the driver with the highest priority - (see below) will be used. -" - -option "ao-option" o -#~~~~~~~~~~~~~~~~~~~ -"Pass a key-value pair to the libao driver" -string typestr = "key:value" -optional -multiple -details = " - For each time this option is given, the supplied key-value - pair is appended to the list of options for the driver. Invalid - keys are silently ignored. -" - - diff --git a/m4/gengetopt/file_write.m4 b/m4/gengetopt/file_write.m4 deleted file mode 100644 index 4f98884f..00000000 --- a/m4/gengetopt/file_write.m4 +++ /dev/null @@ -1,13 +0,0 @@ -args "--no-version --no-help" - -purpose "Output plugin that writes to a local file" - -option "filename" f -#~~~~~~~~~~~~~~~~~~ -"specify output file name" -string typestr="filename" -optional -details=" - Defaults to a random filename in ~/.paraslash. -" - diff --git a/m4/gengetopt/oss_write.m4 b/m4/gengetopt/oss_write.m4 deleted file mode 100644 index 578d8138..00000000 --- a/m4/gengetopt/oss_write.m4 +++ /dev/null @@ -1,11 +0,0 @@ -args "--no-version --no-help" - -purpose "Output plugin for the Open Sound System" - -option "device" d -#~~~~~~~~~~~~~~~~ -"set PCM device" -string typestr="device" -default="/dev/dsp" -optional -details = "" diff --git a/m4/gengetopt/osx_write.m4 b/m4/gengetopt/osx_write.m4 deleted file mode 100644 index 83ed737c..00000000 --- a/m4/gengetopt/osx_write.m4 +++ /dev/null @@ -1,17 +0,0 @@ -args "--no-version --no-help" - -purpose "Output plugin for Mac OS coreaudio" - -section "osx options" -##################### - -option "numbuffers" n -#~~~~~~~~~~~~~~~~~~~~~ - -"number of audio buffers to allocate (increase if -you get buffer underruns)" - - int typestr="num" - default="20" - optional - details = "" diff --git a/m4/lls/write_cmd.suite.m4 b/m4/lls/write_cmd.suite.m4 new file mode 100644 index 00000000..9d8ee75a --- /dev/null +++ b/m4/lls/write_cmd.suite.m4 @@ -0,0 +1,87 @@ +[suite write_cmd] +caption = writers +[subcommand alsa] + purpose = native ALSA output plugin + [option device] + short_opt = d + summary = set PCM device + typestr = device + arg_info = required_arg + arg_type = string + default_val = default + [help] + Check for the presence of a /proc/asound/ directory to see if ALSA + is present in your kernel. The file /proc/asound/devices contains + all devices ALSA knows about. + [/help] + [option buffer-time] + short_opt = B + summary = duration of the ALSA buffer + typestr = milliseconds + arg_info = required_arg + arg_type = uint32 + default_val = 170 + [help] + This is only a hint as ALSA might pick a slightly different time, + depending on the sound hardware. The chosen value is shown in debug + output as BUFFER_TIME. + + If synchronization between multiple clients is desired, the same + buffer time should be configured for all clients. + [/help] +[subcommand ao] + purpose = output plugin for libao + [option driver] + short_opt = d + summary = select a output driver by name + typestr = name + arg_info = required_arg + arg_type = string + [help] + If this is not given, the driver with the highest priority (see below) + will be used. + [/help] + [option ao-option] + short_opt = o + summary = pass a key-value pair to the libao driver + typestr = key:value + arg_info = required_arg + arg_type = string + flag multiple + [help] + For each time this option is given, the supplied key-value pair is + appended to the list of options for the driver. Invalid keys are + silently ignored. + [/help] +[subcommand oss] + purpose = output plugin for the Open Sound System + [option device] + short_opt = d + summary = set PCM device + typestr = path + arg_info = required_arg + arg_type = string + default_val = /dev/dsp +[subcommand osx] + purpose = output plugin for Mac OS coreaudio + [option numbuffers] + short_opt = n + summary = number of audio buffers to allocate + typestr = num + arg_info = required_arg + arg_type = uint32 + default_val = 20 + [help] + Increase if you get buffer underruns. + [/help] +[subcommand file] + purpose = output plugin that writes to a local file + [option filename] + short_opt = f + summary = specify output file name + typestr = path + arg_info = required_arg + arg_type = string + [help] + Defaults to a random filename in ~/.paraslash. + [/help] diff --git a/man_util.bash b/man_util.bash index 2a12737e..c9882786 100755 --- a/man_util.bash +++ b/man_util.bash @@ -39,14 +39,6 @@ make_help() --set-package "para_$1" \ < "$ggo" - if [[ "$target" == 'write' || "$target" == 'audiod' ]]; then - for module in $WRITERS; do - ggo="$GGO_DIR/${module}_write.ggo" - [[ ! -f "$ggo" ]] && continue - printf "\nOptions for the $module writer" - print_modhelp "$ggo" - done - fi } set -u @@ -76,13 +68,9 @@ link="$HELP2MAN_DIR/para_$target" cl_opts= tempfiles= for cl in $COMMAND_LISTS; do - 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 + tempfiles+=" $cl.man_util.$$" + sed -e '/^\.SH / s/$/]/1' -e '/^\.SH / s/^\.SH /[/1' "$cl" > "$cl.man_util.$$" + cl_opts+=" --include $cl.man_util.$$" done # Create a symlink para_$target, pointing to this script. This hack is diff --git a/oss_write.c b/oss_write.c index 20186667..6f646c90 100644 --- a/oss_write.c +++ b/oss_write.c @@ -9,17 +9,16 @@ #include #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "fd.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" -#include "oss_write.cmdline.h" #include "error.h" /** Data specific to the oss writer. */ @@ -106,11 +105,11 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate, { int ret, format; unsigned ch, rate; - struct oss_write_args_info *conf = wn->conf; struct private_oss_write_data *powd = para_calloc(sizeof(*powd)); + const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr); - PARA_INFO_LOG("opening %s\n", conf->device_arg); - ret = para_open(conf->device_arg, O_WRONLY, 0); + PARA_INFO_LOG("opening %s\n", dev); + ret = para_open(dev, O_WRONLY, 0); if (ret < 0) goto err_free; powd->fd = ret; @@ -175,8 +174,7 @@ err: close(powd->fd); err_free: free(powd); - PARA_ERROR_LOG("failed to init %s: %s\n", conf->device_arg, - para_strerror(-ret)); + PARA_ERROR_LOG("failed to init %s: %s\n", dev, para_strerror(-ret)); return ret; } @@ -240,37 +238,8 @@ out: return ret; } -__malloc static void *oss_parse_config_or_die(int argc, char **argv) -{ - struct oss_write_args_info *conf = para_calloc(sizeof(*conf)); - - /* exits on errors */ - oss_write_cmdline_parser(argc, argv, conf); - return conf; -} - -static void oss_free_config(void *conf) -{ - oss_write_cmdline_parser_free(conf); -} - -/** - * The init function of the oss writer. - * - * \param w Pointer to the writer to initialize. - * - * \sa struct writer. - */ -void oss_write_init(struct writer *w) -{ - struct oss_write_args_info dummy; - - oss_write_cmdline_parser_init(&dummy); - w->close = oss_close; - w->pre_select = oss_pre_select; - 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)DEFINE_GGO_HELP(oss_write); - oss_write_cmdline_parser_free(&dummy); -} +const struct writer lsg_write_cmd_com_oss_user_data = { + .pre_select = oss_pre_select, + .post_select = oss_post_select, + .close = oss_close, +}; diff --git a/osx_write.c b/osx_write.c index 18a2c084..0517892e 100644 --- a/osx_write.c +++ b/osx_write.c @@ -13,17 +13,16 @@ #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "fd.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" -#include "osx_write.cmdline.h" #include "ipc.h" #include "error.h" @@ -240,20 +239,6 @@ e0: return ret; } -__malloc static void *osx_write_parse_config_or_die(int argc, char **argv) -{ - struct osx_write_args_info *conf = para_calloc(sizeof(*conf)); - - /* exits on errors */ - osx_write_cmdline_parser(argc, argv, conf); - return conf; -} - -static void osx_free_config(void *conf) -{ - osx_write_cmdline_parser_free(conf); -} - static void osx_write_close(struct writer_node *wn) { struct private_osx_write_data *powd = wn->private_data; @@ -351,21 +336,8 @@ fail: return ret; } -/** - * The init function of the osx writer. - * - * \param w Filled in by the function. - */ -void osx_write_init(struct writer *w) -{ - struct osx_write_args_info dummy; - - osx_write_cmdline_parser_init(&dummy); - w->close = osx_write_close; - w->pre_select = osx_write_pre_select; - 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)DEFINE_GGO_HELP(osx_write); - osx_write_cmdline_parser_free(&dummy); -} +struct writer lsg_write_cmd_com_osx_user_data = { + .close = osx_write_close, + .pre_select = osx_write_pre_select, + .post_select = osx_write_post_select, +}; diff --git a/play.c b/play.c index ca584b99..10451039 100644 --- a/play.c +++ b/play.c @@ -13,11 +13,11 @@ #include "recv_cmd.lsg.h" #include "play_cmd.lsg.h" +#include "write_cmd.lsg.h" #include "play.lsg.h" #include "para.h" #include "list.h" #include "error.h" -#include "ggo.h" #include "buffer_tree.h" #include "version.h" #include "string.h" @@ -26,7 +26,6 @@ #include "afh.h" #include "recv.h" #include "write.h" -#include "write_common.h" #include "fd.h" /** @@ -295,8 +294,8 @@ static int get_playback_error(struct play_task *pt) static int eof_cleanup(struct play_task *pt) { - struct writer *w = writers + DEFAULT_WRITER; const struct filter *decoder; + const struct writer *w = writer_get(-1); /* default writer */ int ret; ret = get_playback_error(pt); @@ -306,7 +305,7 @@ static int eof_cleanup(struct play_task *pt) task_reap(&pt->wn.task); w->close(&pt->wn); btr_remove_node(&pt->wn.btrn); - w->free_config(pt->wn.conf); + lls_free_parse_result(pt->wn.lpr, WRITE_CMD(pt->wn.wid)); memset(&pt->wn, 0, sizeof(struct writer_node)); decoder = filter_get(pt->fn.filter_num); @@ -411,7 +410,7 @@ static int load_file(struct play_task *pt) char *tmp, buf[20]; int ret; const struct filter *decoder; - static struct lls_parse_result *filter_lpr; + static struct lls_parse_result *filter_lpr, *writer_lpr; btr_remove_node(&pt->rn.btrn); if (!pt->rn.receiver || pt->next_file != pt->current_file) { @@ -448,8 +447,8 @@ static int load_file(struct play_task *pt) btr_log_tree(pt->rn.btrn, LL_INFO); /* setup default writer */ - pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num); - + pt->wn.wid = check_writer_arg_or_die(NULL, &writer_lpr); + pt->wn.lpr = writer_lpr; /* success, register tasks */ pt->rn.task = task_register( &(struct task_info) { @@ -1316,7 +1315,6 @@ int main(int argc, char *argv[]) /* needed this early to make help work */ recv_init(); - writer_init(); sched.default_timeout.tv_sec = 5; parse_config_or_die(argc, argv); diff --git a/t/t0005-man.sh b/t/t0005-man.sh index de3a81df..ee1949c0 100755 --- a/t/t0005-man.sh +++ b/t/t0005-man.sh @@ -13,8 +13,6 @@ filter/receiver/writer options as appropriate ' . ${0%/*}/test-lib.sh -rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer - grep_man() { local regex="$1" exe="$2" @@ -24,10 +22,9 @@ grep_man() # check that options of all reveivers/filters/writers are contained # in the man pages -regex="$rfw_regex" test_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv" test_expect_success 'para_filter: filter options' "grep_man 'FILTERS' filter" -test_expect_success 'para_write: writer options' "grep_man '$regex' write" +test_expect_success 'para_write: writer options' "grep_man 'WRITERS' write" test_require_objects "audiod" if [[ -n "$result" ]]; then test_skip 'para_audiod' "missing object(s): $result" @@ -37,7 +34,7 @@ else test_expect_success 'para_audiod: filters' \ "grep_man 'FILTERS' audiod" test_expect_success 'para_audiod: writers' \ - "grep_man 'Options for the file writer' audiod" + "grep_man 'WRITERS' audiod" fi # check various command lists diff --git a/write.c b/write.c index 62caf097..98cd3557 100644 --- a/write.c +++ b/write.c @@ -8,7 +8,9 @@ #include #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "string.h" #include "write.cmdline.h" @@ -18,7 +20,6 @@ #include "stdin.h" #include "buffer_tree.h" #include "write.h" -#include "write_common.h" #include "fd.h" #include "error.h" #include "version.h" @@ -40,37 +41,10 @@ __noreturn static void print_help_and_die(void) 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); + print_writer_helps(d); exit(0); } -/* - * Parse config and register a task for a writer node. - * - * \param arg Command line arguments. - * \param parent The new node will be a child of \a parent. - * \param wn The writer node. - * - * If arg is \p NULL, the OS-dependent default writer is used with no - * arguments. The default writers are alsa for Linux, osx for OS X, oss for - * *BSD, and the file writer if the default writer is not supported. - * - * Once the writer configuration has been retrieved from the ->parse_config - * callback a writer node is created, its buffer tree node is added to the - * buffer tree as a child of the given parent. - * - * Finally, the new writer node's task structure is initialized and registered - * to the paraslash scheduler. - * - * \return Standard. - */ -static void setup_writer_node(const char *arg, struct btr_node *parent, - struct writer_node *wn, struct sched *s) -{ - wn->conf = check_writer_arg_or_die(arg, &wn->writer_num); - register_writer_node(wn, parent, s); -} - struct write_task { struct task *task; struct check_wav_context *cwc; @@ -90,7 +64,7 @@ static int write_post_select(__a_unused struct sched *s, void *context) static int setup_and_schedule(void) { - int i, ret; + int i, n, ret; struct btr_node *cw_btrn; struct writer_node *wns; static struct sched s; @@ -109,28 +83,25 @@ static int setup_and_schedule(void) .post_select = write_post_select, .context = &wt, }, &s); - if (!conf.writer_given) { - wns = para_calloc(sizeof(*wns)); - setup_writer_node(NULL, cw_btrn, wns, &s); - i = 1; - } else { - wns = para_calloc(conf.writer_given * sizeof(*wns)); - for (i = 0; i < conf.writer_given; i++) - setup_writer_node(conf.writer_arg[i], cw_btrn, - wns + i, &s); - } + n = conf.writer_given > 0? conf.writer_given : 1; + wns = para_calloc(n * sizeof(*wns)); + for (i = 0; i < n; i++) { + char *arg = i < conf.writer_given? conf.writer_arg[i] : NULL; + wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr); + register_writer_node(wns + i, cw_btrn, &s); + } s.default_timeout.tv_sec = 10; s.default_timeout.tv_usec = 50000; ret = schedule(&s); if (ret >= 0) { int j, ts; - for (j = 0; j < i; j++) { + for (j = 0; j < n; j++) { struct writer_node *wn = wns + j; ts = task_status(wn->task); assert(ts < 0); if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) { - const char *name = writer_names[wn->writer_num]; + const char *name = writer_name(wn->wid); PARA_ERROR_LOG("%s: %s\n", name, para_strerror(-ts)); if (ret >= 0) @@ -138,14 +109,12 @@ static int setup_and_schedule(void) } } } - for (i--; i >= 0; i--) { + for (i = n - 1; i >= 0; i--) { struct writer_node *wn = wns + i; - struct writer *w = writers + wn->writer_num; - - w->close(wn); + writer_get(wn->wid)->close(wn); btr_remove_node(&wn->btrn); - w->free_config(wn->conf); - free(wn->conf); + lls_free_parse_result(wns[i].lpr, + lls_cmd(wn->wid, write_cmd_suite)); } free(wns); check_wav_shutdown(wt.cwc); @@ -170,7 +139,6 @@ int main(int argc, char *argv[]) write_cmdline_parser(argc, argv, &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(); diff --git a/write.h b/write.h index c7c227ee..fd3983dd 100644 --- a/write.h +++ b/write.h @@ -6,19 +6,16 @@ /** \file write.h Writer-related structures. */ -/** The list of supported writers. */ -enum writer_enum {WRITER_ENUM}; - /** * Describes one running instance of a writer. */ struct writer_node { - /** The number of this writer. */ - int writer_num; + /** The ID of this writer. */ + int wid; /** Writer-specific data. */ void *private_data; - /** The writer-specific configuration of this node. */ - void *conf; + /** The parsed command line, merged with options given in the config file. */ + struct lls_parse_result *lpr; /** The buffer tree node associated with this writer node. */ struct btr_node *btrn; /** The task of this writer node. */ @@ -29,30 +26,6 @@ struct writer_node { /** Describes one supported writer. */ struct writer { - /** - * The init function of the writer. - * - * It must fill in all other function pointers of the given - * writer structure. - */ - void (*init)(struct writer *w); - /** - * The command line parser of the writer. - * - * It should check whether the command line options given by \a argv - * and \a argc are valid and return a pointer to the writer-specific - * configuration data determined by these options. This function must - * either succeed or call exit(). Note that parse_config_or_die() might - * be called more than once with different values of \a options. \sa - * \ref free_config(). - */ - void *(*parse_config_or_die)(int argc, char **argv); - /** - * Deallocate all configuration resources. - * - * This should free whatever was allocated by \ref parse_config_or_die(). - */ - void (*free_config)(void *config); /** * Prepare the fd sets for select. * @@ -72,8 +45,6 @@ struct writer { * This function is assumed to succeed. */ void (*close)(struct writer_node *); - /** The short and the log help text of this writer. */ - struct ggo_help help; /** * The callback handler. * @@ -83,14 +54,23 @@ struct writer { btr_command_handler execute; }; -/** Loop over each supported writer. */ -#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++) - -/** Declare the init functions of all supported writers. */ -DECLARE_WRITER_INITS; +#define WRITE_CMD(_num) (lls_cmd(_num, write_cmd_suite)) -/** Array containing the name of each writer. */ -extern const char *writer_names[]; +#define WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr) \ + (lls_opt_result(LSG_WRITE_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr)) +#define WRITE_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \ + (lls_opt_given(WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr))) +#define WRITE_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \ + (lls_uint32_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr)))) +#define WRITE_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \ + (lls_string_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr)))) -/** The writer structure for each supported writer. */ -extern struct writer writers[NUM_SUPPORTED_WRITERS]; +int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp); +const struct writer *writer_get(int wid); +const char *writer_name(int wid); +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); +void get_btr_channels(struct btr_node *btrn, int32_t *result); +void get_btr_sample_format(struct btr_node *btrn, int32_t *result); +void print_writer_helps(bool detailed); diff --git a/write_common.c b/write_common.c index cdb67e58..d4d1b10b 100644 --- a/write_common.c +++ b/write_common.c @@ -7,90 +7,123 @@ /** \file write_common.c common functions of para_audiod and para_write */ #include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" #include "error.h" -#include "write_common.h" -/** the array containing the names of all supported writers */ -const char *writer_names[] ={WRITER_NAMES}; +/** Loop over all writers. */ +#define FOR_EACH_WRITER(i) for (i = 1; lls_cmd(i, write_cmd_suite); i++) -/** the array of supported writers */ -struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY}; -/** - * Call the init function of each supported paraslash writer. - */ -void writer_init(void) +static inline bool writer_supported(int wid) +{ + return lls_user_data(WRITE_CMD(wid)); +} + +/* simply return the first available writer */ +static int default_writer_id(void) { int i; FOR_EACH_WRITER(i) - writers[i].init(&writers[i]); + if (writer_supported(i)) + return i; + assert(0); /* the file writer should always be available */ } + /** - * Check if given string is a valid command line for any writer. + * Return the writer structure from a writer ID. * - * \param \wa String of the form writer_name:options. - * \param writer_num Contains the number of the writer upon success. + * \param wid If non-positive, a pointer to the default writer is returned. * - * This function checks whether \a wa starts with the name of a supported - * paraslash writer, optionally followed by a colon and any options for that - * writer. If a valid writer name was found and further are present, the - * remaining part of \a wa is passed to that writer's config parser. + * \return Pointer to a (constant) struct writer. + */ +const struct writer *writer_get(int wid) +{ + if (wid < 0) + wid = default_writer_id(); + return lls_user_data(WRITE_CMD(wid)); +} + +/** + * Return name of the writer identified by a writer ID. + * + * \param wid If non-positive, the name of the default writer is returned. * - * \return On success, a pointer to the gengetopt args info struct is returned - * and \a writer_num contains the number of the writer. Otherwise this function - * prints an error message and calls exit(). + * \return The returned buffer must not be freed by the caller. */ -void *check_writer_arg_or_die(const char *wa, int *writer_num) +const char *writer_name(int wid) { - int i, ret, argc; - const char *cmdline; - char **argv; - void *conf; + if (wid <= 0) + wid = default_writer_id(); + return lls_command_name(WRITE_CMD(wid)); +} - if (!wa || !*wa) { - i = DEFAULT_WRITER; - cmdline = NULL; - goto check; - } - PARA_INFO_LOG("checking %s\n", wa); - FOR_EACH_WRITER(i) { - const char *name = writer_names[i]; - size_t len = strlen(name); - char c; +/** + * Check if the given string is a valid command line for any writer. + * + * \param wa String of the form writer_name options. + * \param lprp Contains the parsed command line on success. + * + * If wa is \p NULL, the (configuration-dependent) default writer is assumed. + * Otherwise, the function checks whether \a wa starts with the name of a + * supported writer. If a valid writer name was found, the rest of the command + * line is passed to the config parser of this writer. + * + * \return On success, the positive writer ID is returned. Otherwise the + * function prints an error message and calls exit(). + */ +int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp) +{ + int ret, writer_num, argc; + char **argv = NULL, *errctx = NULL; + const struct lls_command *cmd; - if (strlen(wa) < len) - continue; - if (strncmp(name, wa, len)) - continue; - c = wa[len]; - if (!c || c == ' ') { - cmdline = c? wa + len + 1 : NULL; - goto check; - } - } - PARA_EMERG_LOG("invalid writer %s\n", wa); - exit(EXIT_FAILURE); -check: - ret = create_shifted_argv(cmdline, " \t", &argv); - if (ret < 0) { - PARA_EMERG_LOG("%s: %s\n", wa, para_strerror(-ret)); - exit(EXIT_FAILURE); + if (!wa || !*wa) { + writer_num = default_writer_id(); + cmd = WRITE_CMD(writer_num); + argv = para_malloc(2 * sizeof(char *)); + argc = 1; + argv[0] = para_strdup(lls_command_name(cmd)); + argv[1] = NULL; + goto parse; } + ret = create_argv(wa, " \t\n", &argv); + if (ret < 0) + goto fail; argc = ret; - argv[0] = make_message("%s_write", writer_names[i]); - *writer_num = i; - conf = writers[i].parse_config_or_die(argc, argv); + ret = lls(lls_lookup_subcmd(argv[0], write_cmd_suite, &errctx)); + if (ret < 0) + goto free_argv; + writer_num = ret; + cmd = WRITE_CMD(writer_num); + if (!writer_supported(writer_num)) { + ret = -ERRNO_TO_PARA_ERROR(EINVAL); + errctx = make_message("%s writer is not supported", + lls_command_name(cmd)); + goto free_argv; + } +parse: + ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx)); + if (ret >= 0) + ret = writer_num; +free_argv: free_argv(argv); - return conf; + if (ret >= 0) + return ret; +fail: + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); } /** @@ -99,20 +132,17 @@ check: * \param wn The writer node to open. * \param parent The parent btr node (the source for the writer node). * \param s The scheduler instance to register the task to. - * - * The configuration of the writer node stored in \p wn->conf must be - * initialized before this function may be called. */ void register_writer_node(struct writer_node *wn, struct btr_node *parent, struct sched *s) { - struct writer *w = writers + wn->writer_num; + const struct writer *w = writer_get(wn->wid); wn->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = writer_names[wn->writer_num], .parent = parent, + EMBRACE(.name = writer_name(wn->wid), .parent = parent, .handler = w->execute, .context = wn)); wn->task = task_register(&(struct task_info) { - .name = writer_names[wn->writer_num], + .name = writer_name(wn->wid), .pre_select = w->pre_select, .post_select = w->post_select, .context = wn, @@ -122,24 +152,29 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent, /** * Print the help text of all writers to stdout. * - * \param flags Passed to \ref ggo_print_help(). + * \param detailed Whether to print the short or the detailed help. */ -void print_writer_helps(unsigned flags) +void print_writer_helps(bool detailed) { int i; - printf_or_die("\nAvailable writers: "); - FOR_EACH_WRITER(i) - printf_or_die("%s%s", i? " " : "", writer_names[i]); - printf_or_die("\n"); + printf("\nAvailable writers: "); FOR_EACH_WRITER(i) { - struct writer *w = writers + i; - - if (!w->help.short_help) + if (!writer_supported(i)) + continue; + printf("%s%s", i? " " : "", writer_name(i)); + } + printf("\n"); + FOR_EACH_WRITER(i) { + const struct lls_command *cmd = WRITE_CMD(i); + char *help; + if (!writer_supported(i)) + continue; + help = detailed? lls_long_help(cmd) : lls_short_help(cmd); + if (!help) continue; - printf_or_die("\n%s: %s", writer_names[i], - w->help.purpose); - ggo_print_help(&w->help, flags); + printf("%s\n", help); + free(help); } } diff --git a/write_common.h b/write_common.h deleted file mode 100644 index 18288753..00000000 --- a/write_common.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2006 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file write_common.h Exported symbols from write_common.c. */ - -void writer_init(void); -void *check_writer_arg_or_die(const char *wa, int *writer_num); -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); -void get_btr_channels(struct btr_node *btrn, int32_t *result); -void get_btr_sample_format(struct btr_node *btrn, int32_t *result); -- 2.39.2