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