Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 
     21 #include <ctype.h>
     22 #include <errno.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 
     27 #include "tmux.h"
     28 
     29 struct client		 *cfg_client;
     30 int			  cfg_finished;
     31 static char		**cfg_causes;
     32 static u_int		  cfg_ncauses;
     33 static struct cmdq_item	 *cfg_item;
     34 
     35 int                       cfg_quiet = 1;
     36 char                    **cfg_files;
     37 u_int                     cfg_nfiles;
     38 
     39 static enum cmd_retval
     40 cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
     41 {
     42 	if (!cfg_finished)
     43 		return (CMD_RETURN_WAIT);
     44 	return (CMD_RETURN_NORMAL);
     45 }
     46 
     47 static enum cmd_retval
     48 cfg_done(__unused struct cmdq_item *item, __unused void *data)
     49 {
     50 	if (cfg_finished)
     51 		return (CMD_RETURN_NORMAL);
     52 	cfg_finished = 1;
     53 
     54 	cfg_show_causes(NULL);
     55 
     56 	if (cfg_item != NULL)
     57 		cmdq_continue(cfg_item);
     58 
     59 	status_prompt_load_history();
     60 
     61 	return (CMD_RETURN_NORMAL);
     62 }
     63 
     64 void
     65 start_cfg(void)
     66 {
     67 	struct client	 *c;
     68 	u_int		  i;
     69 	int		  flags = 0;
     70 
     71 	/*
     72 	 * Configuration files are loaded without a client, so commands are run
     73 	 * in the global queue with item->client NULL.
     74 	 *
     75 	 * However, we must block the initial client (but just the initial
     76 	 * client) so that its command runs after the configuration is loaded.
     77 	 * Because start_cfg() is called so early, we can be sure the client's
     78 	 * command queue is currently empty and our callback will be at the
     79 	 * front - we need to get in before MSG_COMMAND.
     80 	 */
     81 	cfg_client = c = TAILQ_FIRST(&clients);
     82 	if (c != NULL) {
     83 		cfg_item = cmdq_get_callback(cfg_client_done, NULL);
     84 		cmdq_append(c, cfg_item);
     85 	}
     86 
     87 	if (cfg_quiet)
     88 		flags = CMD_PARSE_QUIET;
     89 	for (i = 0; i < cfg_nfiles; i++)
     90 		load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
     91 
     92 	cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
     93 }
     94 
     95 int
     96 load_cfg(const char *path, struct client *c, struct cmdq_item *item,
     97     struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
     98 {
     99 	FILE			*f;
    100 	struct cmd_parse_input	 pi;
    101 	struct cmd_parse_result	*pr;
    102 	struct cmdq_item	*new_item0;
    103 	struct cmdq_state	*state;
    104 
    105 	if (new_item != NULL)
    106 		*new_item = NULL;
    107 
    108 	log_debug("loading %s", path);
    109 	if ((f = fopen(path, "rb")) == NULL) {
    110 		if (errno == ENOENT && (flags & CMD_PARSE_QUIET))
    111 			return (0);
    112 		cfg_add_cause("%s: %s", path, strerror(errno));
    113 		return (-1);
    114 	}
    115 
    116 	memset(&pi, 0, sizeof pi);
    117 	pi.flags = flags;
    118 	pi.file = path;
    119 	pi.line = 1;
    120 	pi.item = item;
    121 	pi.c = c;
    122 
    123 	pr = cmd_parse_from_file(f, &pi);
    124 	fclose(f);
    125 	if (pr->status == CMD_PARSE_ERROR) {
    126 		cfg_add_cause("%s", pr->error);
    127 		free(pr->error);
    128 		return (-1);
    129 	}
    130 	if (flags & CMD_PARSE_PARSEONLY) {
    131 		cmd_list_free(pr->cmdlist);
    132 		return (0);
    133 	}
    134 
    135 	if (item != NULL)
    136 		state = cmdq_copy_state(cmdq_get_state(item), current);
    137 	else
    138 		state = cmdq_new_state(NULL, NULL, 0);
    139 	cmdq_add_format(state, "current_file", "%s", pi.file);
    140 
    141 	new_item0 = cmdq_get_command(pr->cmdlist, state);
    142 	if (item != NULL)
    143 		new_item0 = cmdq_insert_after(item, new_item0);
    144 	else
    145 		new_item0 = cmdq_append(NULL, new_item0);
    146 	cmd_list_free(pr->cmdlist);
    147 	cmdq_free_state(state);
    148 
    149 	if (new_item != NULL)
    150 		*new_item = new_item0;
    151 	return (0);
    152 }
    153 
    154 int
    155 load_cfg_from_buffer(const void *buf, size_t len, const char *path,
    156     struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
    157     int flags, struct cmdq_item **new_item)
    158 {
    159 	struct cmd_parse_input	 pi;
    160 	struct cmd_parse_result	*pr;
    161 	struct cmdq_item	*new_item0;
    162 	struct cmdq_state	*state;
    163 
    164 	if (new_item != NULL)
    165 		*new_item = NULL;
    166 
    167 	log_debug("loading %s", path);
    168 
    169 	memset(&pi, 0, sizeof pi);
    170 	pi.flags = flags;
    171 	pi.file = path;
    172 	pi.line = 1;
    173 	pi.item = item;
    174 	pi.c = c;
    175 
    176 	pr = cmd_parse_from_buffer(buf, len, &pi);
    177 	if (pr->status == CMD_PARSE_ERROR) {
    178 		cfg_add_cause("%s", pr->error);
    179 		free(pr->error);
    180 		return (-1);
    181 	}
    182 	if (flags & CMD_PARSE_PARSEONLY) {
    183 		cmd_list_free(pr->cmdlist);
    184 		return (0);
    185 	}
    186 
    187 	if (item != NULL)
    188 		state = cmdq_copy_state(cmdq_get_state(item), current);
    189 	else
    190 		state = cmdq_new_state(NULL, NULL, 0);
    191 	cmdq_add_format(state, "current_file", "%s", pi.file);
    192 
    193 	new_item0 = cmdq_get_command(pr->cmdlist, state);
    194 	if (item != NULL)
    195 		new_item0 = cmdq_insert_after(item, new_item0);
    196 	else
    197 		new_item0 = cmdq_append(NULL, new_item0);
    198 	cmd_list_free(pr->cmdlist);
    199 	cmdq_free_state(state);
    200 
    201 	if (new_item != NULL)
    202 		*new_item = new_item0;
    203 	return (0);
    204 }
    205 
    206 void
    207 cfg_add_cause(const char *fmt, ...)
    208 {
    209 	va_list	 ap;
    210 	char	*msg;
    211 
    212 	va_start(ap, fmt);
    213 	xvasprintf(&msg, fmt, ap);
    214 	va_end(ap);
    215 
    216 	cfg_ncauses++;
    217 	cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes);
    218 	cfg_causes[cfg_ncauses - 1] = msg;
    219 }
    220 
    221 void
    222 cfg_print_causes(struct cmdq_item *item)
    223 {
    224 	struct client	*c = cmdq_get_client(item);
    225 	u_int		 i;
    226 
    227 	for (i = 0; i < cfg_ncauses; i++) {
    228 		if (c != NULL && (c->flags & CLIENT_CONTROL))
    229 			control_write(c, "%%config-error %s", cfg_causes[i]);
    230 		else
    231 			cmdq_print(item, "%s", cfg_causes[i]);
    232 		free(cfg_causes[i]);
    233 	}
    234 
    235 	free(cfg_causes);
    236 	cfg_causes = NULL;
    237 	cfg_ncauses = 0;
    238 }
    239 
    240 void
    241 cfg_show_causes(struct session *s)
    242 {
    243 	struct client			*c = TAILQ_FIRST(&clients);
    244 	struct window_pane		*wp;
    245 	struct window_mode_entry	*wme;
    246 	u_int				 i;
    247 
    248 	if (cfg_ncauses == 0)
    249 		return;
    250 
    251 	if (c != NULL && (c->flags & CLIENT_CONTROL)) {
    252 		for (i = 0; i < cfg_ncauses; i++) {
    253 			control_write(c, "%%config-error %s", cfg_causes[i]);
    254 			free(cfg_causes[i]);
    255 		}
    256 		goto out;
    257 	}
    258 
    259 	if (s == NULL) {
    260 		if (c != NULL && c->session != NULL)
    261 			s = c->session;
    262 		else
    263 			s = RB_MIN(sessions, &sessions);
    264 	}
    265 	if (s == NULL || s->attached == 0) /* wait for an attached session */
    266 		return;
    267 	wp = s->curw->window->active;
    268 
    269 	wme = TAILQ_FIRST(&wp->modes);
    270 	if (wme == NULL || wme->mode != &window_view_mode)
    271 		window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
    272 	for (i = 0; i < cfg_ncauses; i++) {
    273 		window_copy_add(wp, 0, "%s", cfg_causes[i]);
    274 		free(cfg_causes[i]);
    275 	}
    276 
    277 out:
    278 	free(cfg_causes);
    279 	cfg_causes = NULL;
    280 	cfg_ncauses = 0;
    281 }
    282