Convert writers to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 10 Apr 2016 20:50:17 +0000 (22:50 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
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.

23 files changed:
Makefile.in
Makefile.real
alsa_write.c
ao_write.c
audiod.c
audiod_command.c
configure.ac
file_write.c
m4/gengetopt/alsa_write.m4 [deleted file]
m4/gengetopt/ao_write.m4 [deleted file]
m4/gengetopt/file_write.m4 [deleted file]
m4/gengetopt/oss_write.m4 [deleted file]
m4/gengetopt/osx_write.m4 [deleted file]
m4/lls/write_cmd.suite.m4 [new file with mode: 0644]
man_util.bash
oss_write.c
osx_write.c
play.c
t/t0005-man.sh
write.c
write.h
write_common.c
write_common.h [deleted file]

index e72b518..c283b03 100644 (file)
@@ -16,7 +16,6 @@ HELP2MAN := @HELP2MAN@
 ggo_descriptions_declared := @ggo_descriptions_declared@
 
 executables := @executables@
-writers := @writers@
 
 recv_objs := @recv_objs@
 filter_objs := @filter_objs@
index 2dec984..26a4ae5 100644 (file)
@@ -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 \
index fd3b404..3935c8c 100644 (file)
 #include <regex.h>
 #include <sys/types.h>
 #include <alsa/asoundlib.h>
+#include <lopsub.h>
 
+#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,
+};
index 82d98f3..e469a39 100644 (file)
@@ -9,17 +9,16 @@
 #include <pthread.h>
 #include <ao/ao.h>
 #include <regex.h>
+#include <lopsub.h>
 
+#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,
+};
 
index 5f4acb4..ca8cd13 100644 (file)
--- 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);
index 8a87d91..a26d193 100644 (file)
@@ -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"
index 62aa16d..648f2a6 100644 (file)
@@ -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
index 5e66607..9837e81 100644 (file)
@@ -8,17 +8,16 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#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 (file)
index b2c5621..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Native ALSA output plugin"
-
-include(header.m4)
-
-<qu>
-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.
-"
-
-</qu>
-
diff --git a/m4/gengetopt/ao_write.m4 b/m4/gengetopt/ao_write.m4
deleted file mode 100644 (file)
index 29112d7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for libao"
-
-include(header.m4)
-<qu>
-
-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.
-"
-
-</qu>
diff --git a/m4/gengetopt/file_write.m4 b/m4/gengetopt/file_write.m4
deleted file mode 100644 (file)
index 4f98884..0000000
+++ /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 (file)
index 578d813..0000000
+++ /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 (file)
index 83ed737..0000000
+++ /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 (file)
index 0000000..9d8ee75
--- /dev/null
@@ -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]
index 2a12737..c988278 100755 (executable)
@@ -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
index 2018666..6f646c9 100644 (file)
@@ -9,17 +9,16 @@
 #include <regex.h>
 #include <sys/ioctl.h>
 #include <sys/soundcard.h>
+#include <lopsub.h>
 
+#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,
+};
index 18a2c08..0517892 100644 (file)
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#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 ca584b9..1045103 100644 (file)
--- a/play.c
+++ b/play.c
 
 #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);
index de3a81d..ee1949c 100755 (executable)
@@ -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 62caf09..98cd355 100644 (file)
--- a/write.c
+++ b/write.c
@@ -8,7 +8,9 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#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 c7c227e..fd3983d 100644 (file)
--- 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);
index cdb67e5..d4d1b10 100644 (file)
 /** \file write_common.c common functions of para_audiod and para_write */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#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 (file)
index 1828875..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * 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);