]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Convert para_gui to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 18 Jun 2016 14:57:39 +0000 (16:57 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
This replaces the gengetopt file for para_gui by a lopsub suite and
links para_gui with -llopsub.

The new FOR_EACH_KEY_MAP() macro is introduced for iterating over
all key bindings given at the command line or in the config file.

The bulk of the change is parse_config_file_or_die() where we get rid
of the gengetopt specific hack which removed all key bindings due to
gengetopt appending the options when the config parser is called a
second time.

The patch also adds a description section to the man page of para_gui.

Makefile.real
configure.ac
gui.c
m4/gengetopt/gui.m4 [deleted file]
m4/lls/gui.suite.m4 [new file with mode: 0644]

index 2051c3be14118c40c1bfc590087f16a4de275103..e4a35194a8edb3733e496512b786754fdda8e720 100644 (file)
@@ -43,7 +43,7 @@ all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \
        $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \
        $(write_objs) $(afh_objs) $(play_objs))
 deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d)))
-converted_executables := audioc client fade play recv write filter
+converted_executables := audioc client fade play recv write filter gui
 unconverted_executables := $(filter-out $(converted_executables), $(executables))
 
 audioc_objs += audioc.lsg.o
@@ -51,6 +51,7 @@ audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) client.lsg.o
 client_objs += client.lsg.o
 fade_objs += fade.lsg.o
 filter_objs += filter_cmd.lsg.o filter.lsg.o
+gui_objs += gui.lsg.o
 play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
 recv_objs += recv_cmd.lsg.o recv.lsg.o
 server_objs += server_cmd.lsg.o
@@ -322,6 +323,7 @@ para_audiod \
 para_client \
 para_fade \
 para_filter \
+para_gui \
 para_play \
 para_recv \
 para_server \
index e310e2f89303f4f115299d4ec9f0c14053a1f378..4ce860e55fadfd93892bb2d6b801fdb396a2350e 100644 (file)
@@ -648,7 +648,6 @@ fi
 if test $HAVE_CURSES = yes; then
        build_gui="yes"
        executables="$executables gui"
-       gui_cmdline_objs="gui"
        gui_errlist_objs="
                exec
                signal
@@ -661,9 +660,8 @@ if test $HAVE_CURSES = yes; then
                time
                sched
                version
-               ggo
        "
-       gui_objs="add_cmdline($gui_cmdline_objs) $gui_errlist_objs"
+       gui_objs="$gui_errlist_objs"
        AC_SUBST(gui_objs, add_dot_o($gui_objs))
 else
        build_gui="no"
diff --git a/gui.c b/gui.c
index 6a601f81124a2a4adef0680ec8bcaff22a7de0b7..24f1c7276d9e551da6dc05a039a2171007e97c25 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -12,8 +12,9 @@
 #include <curses.h>
 #include <locale.h>
 #include <sys/time.h>
+#include <lopsub.h>
 
-#include "gui.cmdline.h"
+#include "gui.lsg.h"
 #include "para.h"
 #include "gui.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
 #include "signal.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *cmdline_lpr, *lpr;
+
+#define CMD_PTR (lls_cmd(0, gui_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_GUI_PARA_GUI_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define FOR_EACH_KEY_MAP(_i) for (_i = 0; _i < OPT_GIVEN(KEY_MAP); _i++)
+
 static char *stat_content[NUM_STAT_ITEMS];
 
 static struct gui_window {
@@ -51,7 +60,6 @@ static unsigned scroll_position;
 static pid_t exec_pid;
 
 static int exec_fds[2] = {-1, -1};
-static struct gui_args_info conf;
 static int loglevel;
 
 /** Type of the process currently being executed. */
@@ -644,7 +652,8 @@ static int status_post_select(struct sched *s, void *context)
                if (tv_diff(&st->next_exec, now, NULL) > 0)
                        return 0;
                st->next_exec.tv_sec = now->tv_sec + 2;
-               ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
+               ret = para_exec_cmdline_pid(&st->pid,
+                       OPT_STRING_VAL(STAT_CMD), fds);
                if (ret < 0)
                        return 0;
                ret = mark_fd_nonblocking(fds[1]);
@@ -831,12 +840,13 @@ static void check_key_map_args_or_die(void)
 {
        int i;
        char *tmp = NULL;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *handler, *arg;
 
                free(tmp);
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg))
                        break;
                if (strlen(handler) != 1)
@@ -849,77 +859,85 @@ static void check_key_map_args_or_die(void)
                if (find_cmd_byname(arg) < 0)
                        break;
        }
-       if (i != conf.key_map_given)
-               die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
+       if (i != OPT_GIVEN(KEY_MAP))
+               die(EXIT_FAILURE, "invalid key map: %s\n",
+                       lls_string_val(i, lor));
        free(tmp);
 }
 
-static void parse_config_file_or_die(bool override)
+static void parse_config_file_or_die(bool reload)
 {
-       bool err;
-       char *config_file;
-       struct gui_cmdline_parser_params params = {
-               .override = override,
-               .initialize = 0,
-               .check_required = !override,
-               .check_ambiguity = 0,
-               .print_errors = 1,
-       };
+       int ret;
+       char *cf = NULL, *errctx = NULL;
+       void *map;
+       size_t sz;
+       int cf_argc;
+       char **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
 
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/gui.conf", home);
+               cf = make_message("%s/.paraslash/gui.conf", home);
                free(home);
        }
-       if (!file_exists(config_file)) {
-               if (!conf.config_file_given)
-                       err = false;
-               else {
-                       PARA_EMERG_LOG("config file %s does not exist\n",
-                               config_file);
-                       err = true;
-               }
-               goto out;
-       }
-       /*
-        * When the gengetopt config file parser is called more than once, any
-        * key map arguments found in the config file are _appended_ to the old
-        * values, even though we turn on ->override. We want the new arguments
-        * to replace the old ones, so we must empty the key_map_arg array
-        * first. Unfortunately, this also clears any key map arguments given
-        * at the command line.
-        */
-       if (override) {
-               int i;
-               for (i = 0; i < conf.key_map_given; i++) {
-                       free(conf.key_map_arg[i]);
-                       conf.key_map_arg[i] = NULL;
-               }
-               conf.key_map_given = 0;
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto free_cf;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+                       goto free_cf;
+               ret = 0;
+               lpr = cmdline_lpr;
+               goto success;
        }
-
-       gui_cmdline_parser_config_file(config_file, &conf, &params);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+       para_munmap(map, sz);
+       if (ret < 0)
+               goto free_cf;
+       cf_argc = ret;
+       ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+       lls_free_argv(cf_argv);
+       if (ret < 0)
+               goto free_cf;
+       if (reload) /* config file overrides command line */
+               ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       else /* command line options overrride config file options */
+               ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       lls_free_parse_result(cf_lpr, CMD_PTR);
+       if (ret < 0)
+               goto free_cf;
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(lpr, CMD_PTR);
+       lpr = merged_lpr;
+success:
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
        check_key_map_args_or_die();
-       err = false;
-out:
-       free(config_file);
-       if (err)
+       theme_init(OPT_STRING_VAL(THEME), &theme);
+free_cf:
+       free(cf);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
-       theme_init(conf.theme_arg, &theme);
+       }
 }
 
 /* reread configuration, terminate on errors */
 static void reread_conf(void)
 {
        /*
-        * gengetopt might print to stderr and exit on errors. So we have to
-        * shutdown curses first.
+        * If the reload of the config file fails, we are about to exit. In
+        * this case we print the error message to stderr rather than to the
+        * curses window.  So we have to shutdown curses first.
         */
        shutdown_curses();
-       parse_config_file_or_die(true /* override */);
+       parse_config_file_or_die(true);
        init_curses();
        print_in_bar(COLOR_MSG, "config file reloaded\n");
 }
@@ -1077,12 +1095,13 @@ static void exec_external(char *file_and_args)
 static void handle_command(int c)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
        /* first check user-defined key bindings */
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *tmp, *handler, *arg;
 
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg)) {
                        free(tmp);
                        return;
@@ -1329,9 +1348,10 @@ static void com_reread_conf(void)
 static void com_help(void)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
-               char *handler, *arg, *tmp = para_strdup(conf.key_map_arg[i]);
+       FOR_EACH_KEY_MAP(i) {
+               char *handler, *arg, *tmp = para_strdup(lls_string_val(i, lor));
                const char *handler_text = "???", *desc = NULL;
 
                if (!split_key_map(tmp, &handler, &arg)) {
@@ -1415,15 +1435,6 @@ static void com_prev_theme(void)
        com_refresh();
 }
 
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(gui);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
-}
-
 static int setup_tasks_and_schedule(void)
 {
        int ret;
@@ -1471,6 +1482,21 @@ static int setup_tasks_and_schedule(void)
        return ret;
 }
 
+static void handle_help_flags(void)
+{
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
+}
+
 /**
  * The main function of para_gui.
  *
@@ -1498,15 +1524,31 @@ static int setup_tasks_and_schedule(void)
  */
 int main(int argc, char *argv[])
 {
-       gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("gui", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       parse_config_file_or_die(false /* override */);
+       int ret;
+       char *errctx;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       lpr = cmdline_lpr;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("gui", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_file_or_die(false);
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
        setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
-       return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
+       ret = setup_tasks_and_schedule();
+out:
+       lls_free_parse_result(lpr, CMD_PTR);
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       }
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/m4/gengetopt/gui.m4 b/m4/gengetopt/gui.m4
deleted file mode 100644 (file)
index 373416c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Show para_audiod status in a curses window"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_gui)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/gui.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(config_file.m4)
-include(loglevel.m4)
-
-<qu>
-
-option "theme" T
-#~~~~~~~~~~~~~~~
-"select startup theme"
-string typestr = "name"
-optional
-details = "
-       If this option is not given the default theme is used.
-       If the given name is not a valid theme name, the list of
-       available themes is printed and the program terminates.
-"
-
-option "stat-cmd" s
-#~~~~~~~~~~~~~~~~~~
-"command to read status items from"
-string typestr = "command"
-default = "para_audioc -- stat -p"
-optional
-details = "
-       On a host on which the para_audiod service is not is running, the
-       default command will fail. An alternative is
-
-               para_client -- stat -p
-
-       This command connects para_server instead of para_audiod. However,
-       no timing information about the current audio file is printed.
-"
-
-#---------------------------------
-section "Mapping keys to commands"
-#---------------------------------
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to command c using mode m."
-
-string typestr = "k:m:c"
-optional
-multiple
-details = "
-       Mode may be d, x or p for display, external and paraslash
-       commands, respectively. Of course, this option may be given
-       multiple times, one for each key mapping.
-"
-</qu>
diff --git a/m4/lls/gui.suite.m4 b/m4/lls/gui.suite.m4
new file mode 100644 (file)
index 0000000..e0e45ef
--- /dev/null
@@ -0,0 +1,58 @@
+m4_define(PROGRAM, para_gui)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/gui.conf)
+[suite gui]
+version-string = GIT_VERSION()
+[supercommand para_gui]
+       purpose = show para_audiod status in a curses window
+       [description]
+               para_gui is a curses program which displays status information
+               about para_server and para_audiod in a terminal. Keys may be
+               mapped to user-defined commands which are executed when the key is
+               pressed. Command output is shown in a simple pager.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option theme]
+               short_opt = T
+               summary = select startup theme
+               arg_info = required_arg
+               arg_type = string
+               typestr = name
+               [help]
+                       If this option is not given the default theme is used. If the given
+                       name is not a valid theme name, the list of available themes is
+                       printed and the program terminates.
+               [/help]
+       [option stat-cmd]
+               short_opt = s
+               summary = command to read status items from
+               arg_info = required_arg
+               arg_type = string
+               typestr = command
+               default_val = para_audioc -- stat -p
+               [help]
+                       On a host on which the para_audiod service is not is running, the
+                       default command will fail. An alternative is
+
+                               para_client -- stat -p
+
+                       This command connects para_server instead of para_audiod. However,
+                       no timing information about the current audio file is printed.
+               [/help]
+       [option key-map]
+               short_opt = k
+               summary = map a key to a command using one of three possible modes
+               arg_info = required_arg
+               arg_type = string
+               typestr = k:m:c
+               flag multiple
+               [help]
+                       Mode may be d, x or p for display, external and paraslash commands,
+                       respectively. Of course, this option may be given multiple times,
+                       one for each key mapping. See the manual for more information.
+               [/help]