xref: /openbsd-src/usr.bin/tmux/cfg.c (revision 04d313c5ecc70eb438155861372bc182aea5c3a7)
1*04d313c5Snicm /* $OpenBSD: cfg.c,v 1.87 2023/09/15 06:31:49 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
2139c0059eSnicm #include <ctype.h>
22311827fbSnicm #include <errno.h>
23311827fbSnicm #include <stdio.h>
247d053cf9Snicm #include <stdlib.h>
25311827fbSnicm #include <string.h>
26b9a69c48Stobias #include <util.h>
27311827fbSnicm 
28311827fbSnicm #include "tmux.h"
29311827fbSnicm 
3016f55116Snicm struct client		 *cfg_client;
319a53e128Snicm int			  cfg_finished;
32c97fab4eSnicm static char		**cfg_causes;
33c97fab4eSnicm static u_int		  cfg_ncauses;
344e616812Snicm static struct cmdq_item	 *cfg_item;
354e616812Snicm 
36b8e6286cSnicm int                       cfg_quiet = 1;
37b8e6286cSnicm char                    **cfg_files;
38b8e6286cSnicm u_int                     cfg_nfiles;
39b8e6286cSnicm 
404e616812Snicm static enum cmd_retval
cfg_client_done(__unused struct cmdq_item * item,__unused void * data)414e616812Snicm cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
424e616812Snicm {
434e616812Snicm 	if (!cfg_finished)
444e616812Snicm 		return (CMD_RETURN_WAIT);
454e616812Snicm 	return (CMD_RETURN_NORMAL);
464e616812Snicm }
47311827fbSnicm 
48765b9a58Snicm static enum cmd_retval
cfg_done(__unused struct cmdq_item * item,__unused void * data)4968e0a7f2Snicm cfg_done(__unused struct cmdq_item *item, __unused void *data)
50765b9a58Snicm {
51765b9a58Snicm 	if (cfg_finished)
52765b9a58Snicm 		return (CMD_RETURN_NORMAL);
53765b9a58Snicm 	cfg_finished = 1;
54765b9a58Snicm 
5540e79f18Snicm 	cfg_show_causes(NULL);
56765b9a58Snicm 
574e616812Snicm 	if (cfg_item != NULL)
58780ca620Snicm 		cmdq_continue(cfg_item);
594e616812Snicm 
60aa908d99Snicm 	status_prompt_load_history();
61aa908d99Snicm 
62765b9a58Snicm 	return (CMD_RETURN_NORMAL);
63765b9a58Snicm }
6484cce0efSnicm 
6584cce0efSnicm void
start_cfg(void)6684cce0efSnicm start_cfg(void)
6784cce0efSnicm {
684e616812Snicm 	struct client	 *c;
69b8e6286cSnicm 	u_int		  i;
70*04d313c5Snicm 	int		  flags = 0;
7184cce0efSnicm 
727a823ed6Snicm 	/*
73df6ab229Snicm 	 * Configuration files are loaded without a client, so commands are run
74df6ab229Snicm 	 * in the global queue with item->client NULL.
754e616812Snicm 	 *
764e616812Snicm 	 * However, we must block the initial client (but just the initial
774e616812Snicm 	 * client) so that its command runs after the configuration is loaded.
784e616812Snicm 	 * Because start_cfg() is called so early, we can be sure the client's
794e616812Snicm 	 * command queue is currently empty and our callback will be at the
804e616812Snicm 	 * front - we need to get in before MSG_COMMAND.
817a823ed6Snicm 	 */
8216f55116Snicm 	cfg_client = c = TAILQ_FIRST(&clients);
834e616812Snicm 	if (c != NULL) {
844e616812Snicm 		cfg_item = cmdq_get_callback(cfg_client_done, NULL);
854e616812Snicm 		cmdq_append(c, cfg_item);
864e616812Snicm 	}
8784cce0efSnicm 
88b8e6286cSnicm 	if (cfg_quiet)
89*04d313c5Snicm 		flags = CMD_PARSE_QUIET;
90*04d313c5Snicm 	for (i = 0; i < cfg_nfiles; i++)
91*04d313c5Snicm 		load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
9284cce0efSnicm 
937a823ed6Snicm 	cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
9484cce0efSnicm }
9584cce0efSnicm 
96175d36ccSnicm int
load_cfg(const char * path,struct client * c,struct cmdq_item * item,struct cmd_find_state * current,int flags,struct cmdq_item ** new_item)97*04d313c5Snicm load_cfg(const char *path, struct client *c, struct cmdq_item *item,
98*04d313c5Snicm     struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
99311827fbSnicm {
100311827fbSnicm 	FILE			*f;
101df6ab229Snicm 	struct cmd_parse_input	 pi;
102df6ab229Snicm 	struct cmd_parse_result	*pr;
10344c91ad4Snicm 	struct cmdq_item	*new_item0;
1045a773109Snicm 	struct cmdq_state	*state;
10504c37fadSnicm 
106df6ab229Snicm 	if (new_item != NULL)
107df6ab229Snicm 		*new_item = NULL;
108311827fbSnicm 
109977fb5b4Snicm 	log_debug("loading %s", path);
110311827fbSnicm 	if ((f = fopen(path, "rb")) == NULL) {
111df6ab229Snicm 		if (errno == ENOENT && (flags & CMD_PARSE_QUIET))
11259c70a5fStim 			return (0);
11359c70a5fStim 		cfg_add_cause("%s: %s", path, strerror(errno));
114175d36ccSnicm 		return (-1);
115311827fbSnicm 	}
116311827fbSnicm 
117df6ab229Snicm 	memset(&pi, 0, sizeof pi);
118df6ab229Snicm 	pi.flags = flags;
119df6ab229Snicm 	pi.file = path;
120df989ddeSnicm 	pi.line = 1;
1215304b409Snicm 	pi.item = item;
122e32e262aSnicm 	pi.c = c;
123b8d5b6cdSnicm 
124df6ab229Snicm 	pr = cmd_parse_from_file(f, &pi);
125311827fbSnicm 	fclose(f);
126df6ab229Snicm 	if (pr->status == CMD_PARSE_ERROR) {
127df6ab229Snicm 		cfg_add_cause("%s", pr->error);
128df6ab229Snicm 		free(pr->error);
129df6ab229Snicm 		return (-1);
130d4c20211Snicm 	}
131df6ab229Snicm 	if (flags & CMD_PARSE_PARSEONLY) {
132df6ab229Snicm 		cmd_list_free(pr->cmdlist);
133df6ab229Snicm 		return (0);
134df6ab229Snicm 	}
135df6ab229Snicm 
1365a773109Snicm 	if (item != NULL)
137*04d313c5Snicm 		state = cmdq_copy_state(cmdq_get_state(item), current);
1385a773109Snicm 	else
1395a773109Snicm 		state = cmdq_new_state(NULL, NULL, 0);
1405a773109Snicm 	cmdq_add_format(state, "current_file", "%s", pi.file);
1415a773109Snicm 
1425a773109Snicm 	new_item0 = cmdq_get_command(pr->cmdlist, state);
143df6ab229Snicm 	if (item != NULL)
14495cd24f8Snicm 		new_item0 = cmdq_insert_after(item, new_item0);
145df6ab229Snicm 	else
14695cd24f8Snicm 		new_item0 = cmdq_append(NULL, new_item0);
147df6ab229Snicm 	cmd_list_free(pr->cmdlist);
1485a773109Snicm 	cmdq_free_state(state);
149d4c20211Snicm 
15044c91ad4Snicm 	if (new_item != NULL)
151df6ab229Snicm 		*new_item = new_item0;
152df6ab229Snicm 	return (0);
153311827fbSnicm }
15438b63d96Snicm 
1552ea347ccSnicm int
load_cfg_from_buffer(const void * buf,size_t len,const char * path,struct client * c,struct cmdq_item * item,struct cmd_find_state * current,int flags,struct cmdq_item ** new_item)1562ea347ccSnicm load_cfg_from_buffer(const void *buf, size_t len, const char *path,
157*04d313c5Snicm     struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
158*04d313c5Snicm     int flags, struct cmdq_item **new_item)
1592ea347ccSnicm {
1602ea347ccSnicm 	struct cmd_parse_input	 pi;
1612ea347ccSnicm 	struct cmd_parse_result	*pr;
1622ea347ccSnicm 	struct cmdq_item	*new_item0;
1635a773109Snicm 	struct cmdq_state	*state;
1642ea347ccSnicm 
1652ea347ccSnicm 	if (new_item != NULL)
1662ea347ccSnicm 		*new_item = NULL;
1672ea347ccSnicm 
1682ea347ccSnicm 	log_debug("loading %s", path);
1692ea347ccSnicm 
1702ea347ccSnicm 	memset(&pi, 0, sizeof pi);
1712ea347ccSnicm 	pi.flags = flags;
1722ea347ccSnicm 	pi.file = path;
1732ea347ccSnicm 	pi.line = 1;
1742ea347ccSnicm 	pi.item = item;
1752ea347ccSnicm 	pi.c = c;
1762ea347ccSnicm 
1772ea347ccSnicm 	pr = cmd_parse_from_buffer(buf, len, &pi);
1782ea347ccSnicm 	if (pr->status == CMD_PARSE_ERROR) {
1792ea347ccSnicm 		cfg_add_cause("%s", pr->error);
1802ea347ccSnicm 		free(pr->error);
1812ea347ccSnicm 		return (-1);
1822ea347ccSnicm 	}
1832ea347ccSnicm 	if (flags & CMD_PARSE_PARSEONLY) {
1842ea347ccSnicm 		cmd_list_free(pr->cmdlist);
1852ea347ccSnicm 		return (0);
1862ea347ccSnicm 	}
1872ea347ccSnicm 
1885a773109Snicm 	if (item != NULL)
189*04d313c5Snicm 		state = cmdq_copy_state(cmdq_get_state(item), current);
1905a773109Snicm 	else
1915a773109Snicm 		state = cmdq_new_state(NULL, NULL, 0);
1925a773109Snicm 	cmdq_add_format(state, "current_file", "%s", pi.file);
1935a773109Snicm 
1945a773109Snicm 	new_item0 = cmdq_get_command(pr->cmdlist, state);
1952ea347ccSnicm 	if (item != NULL)
19695cd24f8Snicm 		new_item0 = cmdq_insert_after(item, new_item0);
1972ea347ccSnicm 	else
19895cd24f8Snicm 		new_item0 = cmdq_append(NULL, new_item0);
1992ea347ccSnicm 	cmd_list_free(pr->cmdlist);
2005a773109Snicm 	cmdq_free_state(state);
2012ea347ccSnicm 
2022ea347ccSnicm 	if (new_item != NULL)
2032ea347ccSnicm 		*new_item = new_item0;
2042ea347ccSnicm 	return (0);
2052ea347ccSnicm }
2062ea347ccSnicm 
207175d36ccSnicm void
cfg_add_cause(const char * fmt,...)208ffa87c31Snicm cfg_add_cause(const char *fmt, ...)
209ffa87c31Snicm {
210ffa87c31Snicm 	va_list	 ap;
211ffa87c31Snicm 	char	*msg;
212ffa87c31Snicm 
213ffa87c31Snicm 	va_start(ap, fmt);
214ffa87c31Snicm 	xvasprintf(&msg, fmt, ap);
215ffa87c31Snicm 	va_end(ap);
216ffa87c31Snicm 
21760395c8fSnicm 	cfg_ncauses++;
21860395c8fSnicm 	cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes);
21960395c8fSnicm 	cfg_causes[cfg_ncauses - 1] = msg;
220ffa87c31Snicm }
221ffa87c31Snicm 
222ffa87c31Snicm void
cfg_print_causes(struct cmdq_item * item)22368e0a7f2Snicm cfg_print_causes(struct cmdq_item *item)
224ffa87c31Snicm {
225ffa87c31Snicm 	u_int	 i;
226ffa87c31Snicm 
22760395c8fSnicm 	for (i = 0; i < cfg_ncauses; i++) {
22868e0a7f2Snicm 		cmdq_print(item, "%s", cfg_causes[i]);
22960395c8fSnicm 		free(cfg_causes[i]);
230ffa87c31Snicm 	}
23160395c8fSnicm 
23260395c8fSnicm 	free(cfg_causes);
23360395c8fSnicm 	cfg_causes = NULL;
23494bd52cdSnicm 	cfg_ncauses = 0;
235ffa87c31Snicm }
236ffa87c31Snicm 
237ffa87c31Snicm void
cfg_show_causes(struct session * s)238175d36ccSnicm cfg_show_causes(struct session *s)
23938b63d96Snicm {
24040e79f18Snicm 	struct client			*c = TAILQ_FIRST(&clients);
24138b63d96Snicm 	struct window_pane		*wp;
2422c8678f7Snicm 	struct window_mode_entry	*wme;
24338b63d96Snicm 	u_int				 i;
24438b63d96Snicm 
24540e79f18Snicm 	if (cfg_ncauses == 0)
24640e79f18Snicm 		return;
24740e79f18Snicm 
24840e79f18Snicm 	if (c != NULL && (c->flags & CLIENT_CONTROL)) {
24940e79f18Snicm 		for (i = 0; i < cfg_ncauses; i++) {
25040e79f18Snicm 			control_write(c, "%%config-error %s", cfg_causes[i]);
25140e79f18Snicm 			free(cfg_causes[i]);
25240e79f18Snicm 		}
25340e79f18Snicm 		goto out;
25440e79f18Snicm 	}
25540e79f18Snicm 
25640e79f18Snicm 	if (s == NULL) {
25740e79f18Snicm 		if (c != NULL && c->session != NULL)
25840e79f18Snicm 			s = c->session;
25940e79f18Snicm 		else
26040e79f18Snicm 			s = RB_MIN(sessions, &sessions);
26140e79f18Snicm 	}
26240e79f18Snicm 	if (s == NULL || s->attached == 0) /* wait for an attached session */
26338b63d96Snicm 		return;
26438b63d96Snicm 	wp = s->curw->window->active;
26538b63d96Snicm 
2662c8678f7Snicm 	wme = TAILQ_FIRST(&wp->modes);
2672c8678f7Snicm 	if (wme == NULL || wme->mode != &window_view_mode)
268cdfe74adSnicm 		window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
26960395c8fSnicm 	for (i = 0; i < cfg_ncauses; i++) {
27016be08e6Snicm 		window_copy_add(wp, 0, "%s", cfg_causes[i]);
27160395c8fSnicm 		free(cfg_causes[i]);
27238b63d96Snicm 	}
27360395c8fSnicm 
27440e79f18Snicm out:
27560395c8fSnicm 	free(cfg_causes);
27660395c8fSnicm 	cfg_causes = NULL;
27794bd52cdSnicm 	cfg_ncauses = 0;
27838b63d96Snicm }
279