gui: Catch SIGWINCH.
[paraslash.git] / gui.c
diff --git a/gui.c b/gui.c
index 8e83cc5..69a9243 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -1,8 +1,4 @@
-/*
- * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
 /** \file gui.c Curses-based interface for paraslash. */
 
@@ -12,8 +8,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 {
@@ -47,11 +52,8 @@ struct rb_entry {
 static struct ringbuffer *bot_win_rb;
 
 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. */
@@ -245,7 +247,7 @@ static char *km_keyname(int c)
 }
 
 /* Print given number of spaces to curses window. */
-static void add_spaces(WINDOWwin, unsigned int num)
+static void add_spaces(WINDOW *win, unsigned int num)
 {
        const char space[] = "                                ";
        const unsigned sz = sizeof(space) - 1; /* number of spaces */
@@ -261,10 +263,10 @@ static void add_spaces(WINDOW* win, unsigned int num)
 }
 
 /*
- * print aligned string to curses window. This function always prints
+ * Print aligned string to curses window. This function always prints
  * exactly len chars.
  */
-static int align_str(WINDOWwin, const char *str, unsigned int len,
+static int align_str(WINDOW *win, const char *str, unsigned int len,
                unsigned int align)
 {
        int ret, num; /* of spaces */
@@ -450,10 +452,8 @@ static void rb_add_entry(int color, char *msg)
        waddstr(bot.win, msg);
 }
 
-/*
- * print formated output to bot win and refresh
- */
-__printf_2_3 static void outputf(int color, const char* fmt,...)
+/* Print formatted output to bot win and refresh. */
+__printf_2_3 static void outputf(int color, const char *fmt,...)
 {
        char *msg;
        va_list ap;
@@ -496,8 +496,9 @@ static __printf_2_3 void curses_log(int ll, const char *fmt,...)
                vfprintf(stderr, fmt, ap);
        va_end(ap);
 }
+
 /** The log function of para_gui, always set to curses_log(). */
-__printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
+__printf_2_3 void (*para_log)(int, const char *, ...) = curses_log;
 
 /* Call endwin() to reset the terminal into non-visual mode. */
 static void shutdown_curses(void)
@@ -514,8 +515,8 @@ static void shutdown_curses(void)
        endwin();
 }
 
-/* disable curses, print a message, kill running processes and exit */
-__noreturn __printf_2_3 static void die(int exit_code, const charfmt, ...)
+/* Disable curses, print a message, kill running processes and exit. */
+__noreturn __printf_2_3 static void die(int exit_code, const char *fmt, ...)
 {
        va_list argp;
 
@@ -538,9 +539,7 @@ __noreturn __printf_2_3 static void die(int exit_code, const char* fmt, ...)
        exit(exit_code);
 }
 
-/*
- * Print stat item #i to curses window
- */
+/* Print stat item #i to curses window. */
 static void print_stat_item(int i)
 {
        char *tmp;
@@ -566,19 +565,19 @@ static int update_item(int item_num, char *buf)
        if (buf && buf[0])
                goto dup;
        switch (item_num) {
-       case SI_ARTIST:
+       case SI_artist:
                *c = para_strdup("(artist tag not set)");
                goto print;
-       case SI_TITLE:
+       case SI_title:
                *c = para_strdup("(title tag not set)");
                goto print;
-       case SI_YEAR:
+       case SI_year:
                *c = para_strdup("????");
                goto print;
-       case SI_ALBUM:
+       case SI_album:
                *c = para_strdup("(album tag not set)");
                goto print;
-       case SI_COMMENT:
+       case SI_comment:
                *c = para_strdup("(comment tag not set)");
                goto print;
        }
@@ -644,7 +643,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]);
@@ -676,8 +676,8 @@ static int status_post_select(struct sched *s, void *context)
                close(st->fd);
                st->fd = -1;
                clear_all_items();
-               free(stat_content[SI_BASENAME]);
-               stat_content[SI_BASENAME] =
+               free(stat_content[SI_basename]);
+               stat_content[SI_basename] =
                        para_strdup("stat command terminated!?");
                print_all_items();
                return 0;
@@ -689,9 +689,7 @@ static int status_post_select(struct sched *s, void *context)
        return 0;
 }
 
-/*
- * init all windows
- */
+/* Initialize all windows. */
 static void init_wins(int top_lines)
 {
        int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
@@ -831,12 +829,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,84 +848,90 @@ 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 override 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 */
+/* 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");
 }
 
-/*
- * React to various signal-related events
- */
+/* React to various signal-related events. */
 static int signal_post_select(struct sched *s, __a_unused void *context)
 {
        int ret = para_next_signal(&s->rfds);
@@ -936,6 +941,14 @@ static int signal_post_select(struct sched *s, __a_unused void *context)
        switch (ret) {
        case SIGTERM:
                die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
+       case SIGWINCH:
+               PARA_NOTICE_LOG("got SIGWINCH\n");
+               if (curses_active()) {
+                       shutdown_curses();
+                       init_curses();
+                       redraw_bot_win();
+               }
+               return 1;
        case SIGINT:
                return 1;
        case SIGUSR1:
@@ -1027,7 +1040,7 @@ static void input_pre_select(struct sched *s, __a_unused void *context)
                sched_min_delay(s);
 }
 
-/* read from command pipe and print data to bot window */
+/* Read from command pipe and print data to bot window. */
 static void exec_and_display(const char *file_and_args)
 {
        int ret, fds[3] = {0, 1, 1};
@@ -1061,9 +1074,7 @@ static void exec_para(const char *args)
        free(file_and_args);
 }
 
-/*
- * shutdown curses and stat pipe before executing external commands
- */
+/* Shutdown curses and stat pipe before executing external commands. */
 static void exec_external(char *file_and_args)
 {
        int fds[3] = {-1, -1, -1};
@@ -1077,12 +1088,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;
@@ -1116,7 +1128,8 @@ static void handle_command(int c)
                km_keyname(c));
 }
 
-static int input_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int input_post_select(__a_unused struct sched *s,
+               __a_unused void *context)
 {
        int ret;
        enum exec_status exs = exec_status();
@@ -1141,14 +1154,8 @@ static int input_post_select(__a_unused struct sched *s, __a_unused void *contex
        ret = wgetch(top.win);
        if (ret == ERR)
                return 0;
-       if (ret == KEY_RESIZE) {
-               if (curses_active()) {
-                       shutdown_curses();
-                       init_curses();
-                       redraw_bot_win();
-               }
+       if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
                return 0;
-       }
        if (exs == EXEC_IDLE)
                handle_command(ret);
        else if (exec_pid > 0)
@@ -1329,9 +1336,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 +1423,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;
@@ -1431,12 +1430,7 @@ static int setup_tasks_and_schedule(void)
        struct status_task status_task = {.fd = -1};
        struct input_task input_task = {.task = NULL};
        struct signal_task *signal_task;
-       struct sched sched = {
-               .default_timeout = {
-                       .tv_sec = conf.timeout_arg  / 1000,
-                       .tv_usec = (conf.timeout_arg % 1000) * 1000,
-               },
-       };
+       struct sched sched = {.default_timeout = {.tv_sec = 1}};
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
@@ -1464,6 +1458,7 @@ static int setup_tasks_and_schedule(void)
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGCHLD);
        para_install_sighandler(SIGUSR1);
+       para_install_sighandler(SIGWINCH);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
                .pre_select = signal_pre_select,
@@ -1476,6 +1471,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.
  *
@@ -1503,15 +1513,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;
 }