static int command_fds[2] = {-1, -1};
static int stat_pipe = -1;
static struct gui_args_info conf;
+static int loglevel;
enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
return buf;
}
-static char *configfile_exists(void)
-{
- static char *config_file;
- char *tmp;
-
- if (!conf.config_file_given) {
- if (!config_file) {
- char *home = para_homedir();
- config_file = make_message("%s/.paraslash/gui.conf",
- home);
- free(home);
- }
- tmp = config_file;
- } else
- tmp = conf.config_file_arg;
- return file_exists(tmp)? tmp: NULL;
-}
-
/* Print given number of spaces to curses window. */
static void add_spaces(WINDOW* win, unsigned int num)
{
return 1;
}
-static int loglevel;
-
static __printf_2_3 void curses_log(int ll, const char *fmt,...)
{
va_list ap;
return 1;
}
-static int read_stat_pipe(fd_set *rfds)
+static void print_all_items(void)
+{
+ int i;
+
+ if (!curses_active())
+ return;
+ FOR_EACH_STATUS_ITEM(i)
+ print_stat_item(i);
+}
+
+static void clear_all_items(void)
+{
+ int i;
+
+ FOR_EACH_STATUS_ITEM(i) {
+ free(stat_content[i]);
+ stat_content[i] = para_strdup("");
+ }
+}
+
+static void status_post_select(fd_set *rfds)
{
static char *buf;
static int bufsize, loaded;
size_t sz;
if (stat_pipe < 0)
- return 0;
+ return;
if (loaded >= bufsize) {
if (bufsize > 1000 * 1000) {
loaded = 0;
- return 0;
+ return;
}
bufsize += bufsize + 1000;
buf = para_realloc(buf, bufsize);
ret2 = for_each_stat_item(buf, loaded, update_item);
if (ret < 0 || ret2 < 0) {
loaded = 0;
- return ret2 < 0? ret2 : ret;
+ PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
+ close(stat_pipe);
+ stat_pipe = -1;
+ clear_all_items();
+ free(stat_content[SI_BASENAME]);
+ stat_content[SI_BASENAME] =
+ para_strdup("stat command terminated!?");
+ print_all_items();
+ return;
}
sz = ret2; /* what is left */
if (sz > 0 && sz < loaded)
memmove(buf, buf + loaded - sz, sz);
loaded = sz;
- return 1;
-}
-
-static void print_all_items(void)
-{
- int i;
-
- if (!curses_active())
- return;
- FOR_EACH_STATUS_ITEM(i)
- print_stat_item(i);
-}
-
-static void clear_all_items(void)
-{
- int i;
-
- FOR_EACH_STATUS_ITEM(i) {
- free(stat_content[i]);
- stat_content[i] = para_strdup("");
- }
}
/*
free(tmp);
}
+static void parse_config_file_or_die(bool override)
+{
+ bool err;
+ char *config_file;
+ struct gui_cmdline_parser_params params = {
+ .override = override,
+ .initialize = 0,
+ .check_required = !override,
+ .check_ambiguity = 0,
+ .print_errors = 1,
+ };
+
+ if (conf.config_file_given)
+ config_file = para_strdup(conf.config_file_arg);
+ else {
+ char *home = para_homedir();
+ config_file = 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;
+ }
+ gui_cmdline_parser_config_file(config_file, &conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ check_key_map_args_or_die();
+ theme_init(conf.theme_arg, &theme);
+ err = false;
+out:
+ free(config_file);
+ if (err)
+ exit(EXIT_FAILURE);
+}
+
+/* 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.
+ */
+ shutdown_curses();
+ parse_config_file_or_die(true /* override */);
+ init_curses();
+ print_in_bar(COLOR_MSG, "config file reloaded\n");
+}
+
/*
* React to various signal-related events
*/
-static void handle_signal(int sig)
+static void signal_post_select(fd_set *rfds)
{
- switch (sig) {
+ int ret = para_next_signal(rfds);
+ if (ret <= 0)
+ return;
+ switch (ret) {
case SIGTERM:
die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
return;
return;
case SIGUSR1:
PARA_NOTICE_LOG("got SIGUSR1, rereading configuration\n");
- com_reread_conf();
+ reread_conf();
return;
case SIGCHLD:
check_sigchld();
#define COMMAND_BUF_SIZE 32768
-/*
- * This is the core select loop. Besides the (internal) signal
- * pipe, the following other fds are checked according to the mode:
- *
- * GETCH_MODE: check stdin, return when key is pressed
- *
- * COMMAND_MODE: check command fds and stdin. Return when peer has closed both
- * stdout and stderr or when any key is pressed.
- *
- * EXTERNAL_MODE: Check only signal pipe. Used when an external command
- * is running. During that time curses is disabled. Returns when
- * cmd_pid == 0.
- */
-static int do_select(int mode)
+static void command_pre_select(int mode, fd_set *rfds, int *max_fileno)
{
- fd_set rfds;
- int ret, i, max_fileno;
- char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
- int cbo[2] = {0, 0}; /* command buf offsets */
- struct timeval tv;
- unsigned flags[2] = {0, 0}; /* for for_each_line() */
+ /* check command pipe only in COMMAND_MODE */
+ if (mode != COMMAND_MODE)
+ return;
+ if (command_fds[0] >= 0)
+ para_fd_set(command_fds[0], rfds, max_fileno);
+ if (command_fds[1] >= 0)
+ para_fd_set(command_fds[1], rfds, max_fileno);
+}
-repeat:
- tv.tv_sec = conf.timeout_arg / 1000;
- tv.tv_usec = (conf.timeout_arg % 1000) * 1000;
-// ret = refresh_status();
- FD_ZERO(&rfds);
- max_fileno = 0;
- status_pre_select(&rfds, &max_fileno, &tv);
- /* signal pipe */
- para_fd_set(signal_pipe, &rfds, &max_fileno);
- /* command pipe only for COMMAND_MODE */
- if (mode == COMMAND_MODE) {
- if (command_fds[0] >= 0)
- para_fd_set(command_fds[0], &rfds, &max_fileno);
- if (command_fds[1] >= 0)
- para_fd_set(command_fds[1], &rfds, &max_fileno);
- }
- if (mode == GETCH_MODE || mode == COMMAND_MODE)
- para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
- ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
- if (ret <= 0)
- goto check_return; /* skip fd checks */
- /* signals */
- ret = para_next_signal(&rfds);
- if (ret > 0)
- handle_signal(ret);
- /* read command pipe if ready */
- if (mode == COMMAND_MODE) {
- for (i = 0; i < 2; i++) {
- size_t sz;
- if (command_fds[i] < 0)
- continue;
- ret = read_nonblock(command_fds[i],
- command_buf[i] + cbo[i],
- COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
- cbo[i] += sz;
- sz = cbo[i];
- cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
- add_output_line, &i);
- if (sz != cbo[i]) { /* at least one line found */
- wrefresh(bot.win);
- flags[i] = 0;
- }
- if (ret < 0) {
- PARA_NOTICE_LOG("closing command fd %d: %s\n",
- i, para_strerror(-ret));
- close(command_fds[i]);
- command_fds[i] = -1;
- flags[i] = 0;
- cbo[i] = 0;
- if (command_fds[!i] < 0) /* both fds closed */
- return 0;
- }
- if (cbo[i] == COMMAND_BUF_SIZE - 1) {
- PARA_NOTICE_LOG("discarding overlong line\n");
- cbo[i] = 0;
- flags[i] = FELF_DISCARD_FIRST;
- }
+static int command_post_select(int mode, fd_set *rfds)
+{
+ int i, ret;
+ static char command_buf[2][COMMAND_BUF_SIZE];
+ static int cbo[2]; /* command buf offsets */
+ static unsigned flags[2]; /* for for_each_line() */
+
+ if (mode != COMMAND_MODE)
+ return 0;
+ for (i = 0; i < 2; i++) {
+ size_t sz;
+ if (command_fds[i] < 0)
+ continue;
+ ret = read_nonblock(command_fds[i],
+ command_buf[i] + cbo[i],
+ COMMAND_BUF_SIZE - 1 - cbo[i], rfds, &sz);
+ cbo[i] += sz;
+ sz = cbo[i];
+ cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
+ add_output_line, &i);
+ if (sz != cbo[i]) { /* at least one line found */
+ wrefresh(bot.win);
+ flags[i] = 0;
}
- }
- ret = read_stat_pipe(&rfds);
- if (ret < 0) {
- PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
- close(stat_pipe);
- stat_pipe = -1;
- clear_all_items();
- free(stat_content[SI_BASENAME]);
- stat_content[SI_BASENAME] =
- para_strdup("stat command terminated!?");
- print_all_items();
- }
-check_return:
- switch (mode) {
- case COMMAND_MODE:
- ret = wgetch(top.win);
- if (ret != ERR && ret != KEY_RESIZE) {
- if (cmd_pid)
- kill(cmd_pid, SIGTERM);
- return -1;
+ if (ret < 0) {
+ PARA_NOTICE_LOG("closing command fd %d: %s",
+ i, para_strerror(-ret));
+ close(command_fds[i]);
+ command_fds[i] = -1;
+ flags[i] = 0;
+ cbo[i] = 0;
+ if (command_fds[!i] < 0) /* both fds closed */
+ return -1;
+ }
+ if (cbo[i] == COMMAND_BUF_SIZE - 1) {
+ PARA_NOTICE_LOG("discarding overlong line");
+ cbo[i] = 0;
+ flags[i] = FELF_DISCARD_FIRST;
}
- break;
- case GETCH_MODE:
- ret = wgetch(top.win);
- if (ret != ERR && ret != KEY_RESIZE)
- return ret;
- break;
- case EXTERNAL_MODE:
- if (cmd_pid == 0)
- return 0;
}
- goto repeat;
+ return 1;
}
+static int do_select(int mode);
+
/* read from command pipe and print data to bot window */
static void exec_and_display_cmd(const char *cmd)
{
init_curses();
}
+static void handle_command(int c)
+{
+ int i;
+
+ /* first check user-defined key bindings */
+ for (i = 0; i < conf.key_map_given; ++i) {
+ char *tmp, *handler, *arg;
+
+ tmp = para_strdup(conf.key_map_arg[i]);
+ if (!split_key_map(tmp, &handler, &arg)) {
+ free(tmp);
+ return;
+ }
+ if (strcmp(tmp, km_keyname(c))) {
+ free(tmp);
+ continue;
+ }
+ if (*handler == 'd')
+ display_cmd(arg);
+ else if (*handler == 'x')
+ external_cmd(arg);
+ else if (*handler == 'p')
+ para_cmd(arg);
+ else if (*handler == 'i') {
+ int num = find_cmd_byname(arg);
+ if (num >= 0)
+ command_list[num].handler();
+ }
+ free(tmp);
+ return;
+ }
+ /* not found, check internal key bindings */
+ for (i = 0; command_list[i].handler; i++) {
+ if (!strcmp(km_keyname(c), command_list[i].key)) {
+ command_list[i].handler();
+ return;
+ }
+ }
+ print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
+ km_keyname(c));
+}
+
+static void input_pre_select(int mode, fd_set *rfds, int *max_fileno)
+{
+ if (mode == GETCH_MODE || mode == COMMAND_MODE)
+ para_fd_set(STDIN_FILENO, rfds, max_fileno);
+}
+
+static int input_post_select(int mode)
+{
+ int ret;
+
+ switch (mode) {
+ case COMMAND_MODE:
+ ret = wgetch(top.win);
+ if (ret != ERR && ret != KEY_RESIZE) {
+ if (cmd_pid)
+ kill(cmd_pid, SIGTERM);
+ return -1;
+ }
+ return 0;
+ case GETCH_MODE:
+ ret = wgetch(top.win);
+ if (ret != ERR && ret != KEY_RESIZE)
+ return ret;
+ return 0;
+ case EXTERNAL_MODE:
+ if (cmd_pid == 0)
+ return -1;
+ return 0;
+ default:
+ assert(false); /* bug */
+ }
+}
+
+static void signal_pre_select(fd_set *rfds, int *max_fileno)
+{
+ para_fd_set(signal_pipe, rfds, max_fileno);
+}
+
+/*
+ * This is the core select loop. Besides the (internal) signal
+ * pipe, the following other fds are checked according to the mode:
+ *
+ * GETCH_MODE: check stdin, return when key is pressed
+ *
+ * COMMAND_MODE: check command fds and stdin. Return when peer has closed both
+ * stdout and stderr or when any key is pressed.
+ *
+ * EXTERNAL_MODE: Check only signal pipe. Used when an external command
+ * is running. During that time curses is disabled. Returns when
+ * cmd_pid == 0.
+ */
+static int do_select(int mode)
+{
+ fd_set rfds;
+ int ret, max_fileno;
+ struct timeval tv;
+
+repeat:
+ tv.tv_sec = conf.timeout_arg / 1000;
+ tv.tv_usec = (conf.timeout_arg % 1000) * 1000;
+// ret = refresh_status();
+ FD_ZERO(&rfds);
+ max_fileno = 0;
+ status_pre_select(&rfds, &max_fileno, &tv);
+ signal_pre_select(&rfds, &max_fileno);
+ command_pre_select(mode, &rfds, &max_fileno);
+ input_pre_select(mode, &rfds, &max_fileno);
+ ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
+ if (ret <= 0)
+ goto check_return; /* skip fd checks */
+ signal_post_select(&rfds);
+ /* read command pipe if ready */
+ ret = command_post_select(mode, &rfds);
+ if (ret < 0)
+ return 0;
+ status_post_select(&rfds);
+check_return:
+ ret = input_post_select(mode);
+ if (ret != 0)
+ return ret;
+ goto repeat;
+}
+
static void print_scroll_msg(void)
{
unsigned lines_total, filled = ringbuffer_filled(bot_win_rb);
print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
}
-/*
- * reread configuration, terminate on errors
- */
static void com_reread_conf(void)
{
- char *cf = configfile_exists();
- struct gui_cmdline_parser_params params = {
- .override = 1,
- .initialize = 1,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 0,
- };
-
- if (!cf) {
- PARA_WARNING_LOG("there is no configuration to read\n");
- return;
- }
- PARA_INFO_LOG("rereading command line options and config file\n");
- /*
- * Despite .print_errors is set to 0, gengetopt will print to stderr
- * anyway, and exit on errors. So we have to shutdown curses first.
- */
- shutdown_curses();
- gui_cmdline_parser_config_file(cf, &conf, ¶ms);
- init_curses();
- PARA_NOTICE_LOG("config file reloaded\n");
- check_key_map_args_or_die();
+ reread_conf();
}
static void com_help(void)
com_refresh();
}
-static void handle_command(int c)
-{
- int i;
-
- /* first check user-defined key bindings */
- for (i = 0; i < conf.key_map_given; ++i) {
- char *tmp, *handler, *arg;
-
- tmp = para_strdup(conf.key_map_arg[i]);
- if (!split_key_map(tmp, &handler, &arg)) {
- free(tmp);
- return;
- }
- if (strcmp(tmp, km_keyname(c))) {
- free(tmp);
- continue;
- }
- if (*handler == 'd')
- display_cmd(arg);
- else if (*handler == 'x')
- external_cmd(arg);
- else if (*handler == 'p')
- para_cmd(arg);
- else if (*handler == 'i') {
- int num = find_cmd_byname(arg);
- if (num >= 0)
- command_list[num].handler();
- }
- free(tmp);
- return;
- }
- /* not found, check internal key bindings */
- for (i = 0; command_list[i].handler; i++) {
- if (!strcmp(km_keyname(c), command_list[i].key)) {
- command_list[i].handler();
- return;
- }
- }
- print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
- km_keyname(c));
-}
-
__noreturn static void print_help_and_die(void)
{
struct ggo_help h = DEFINE_GGO_HELP(gui);
int main(int argc, char *argv[])
{
int ret;
- char *cf;
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();
- cf = configfile_exists();
- if (!cf && conf.config_file_given)
- die(EXIT_FAILURE, "can not read config file %s\n",
- conf.config_file_arg);
- if (cf) {
- struct gui_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1,
- };
- gui_cmdline_parser_config_file(cf, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- }
- check_key_map_args_or_die();
- theme_init(conf.theme_arg, &theme);
+ parse_config_file_or_die(false /* override */);
setup_signal_handling();
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
setlocale(LC_CTYPE, "");