xref: /openbsd-src/usr.bin/tmux/cfg.c (revision fb8aa7497fded39583f40e800732f9c046411717)
1 /* $OpenBSD: cfg.c,v 1.45 2016/05/12 16:05:33 tim Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@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 #include <unistd.h>
27 #include <util.h>
28 
29 #include "tmux.h"
30 
31 char		 *cfg_file;
32 struct cmd_q	 *cfg_cmd_q;
33 int		  cfg_finished;
34 int		  cfg_references;
35 char		**cfg_causes;
36 u_int		  cfg_ncauses;
37 struct client	 *cfg_client;
38 
39 void	cfg_default_done(struct cmd_q *);
40 
41 void
42 set_cfg_file(const char *path)
43 {
44 	free(cfg_file);
45 	cfg_file = xstrdup(path);
46 }
47 
48 void
49 start_cfg(void)
50 {
51 	const char	*home;
52 	int		 quiet = 0;
53 
54 	cfg_cmd_q = cmdq_new(NULL);
55 	cfg_cmd_q->emptyfn = cfg_default_done;
56 
57 	cfg_finished = 0;
58 	cfg_references = 1;
59 
60 	cfg_client = TAILQ_FIRST(&clients);
61 	if (cfg_client != NULL)
62 		cfg_client->references++;
63 
64 	load_cfg(TMUX_CONF, cfg_cmd_q, 1);
65 
66 	if (cfg_file == NULL && (home = find_home()) != NULL) {
67 		xasprintf(&cfg_file, "%s/.tmux.conf", home);
68 		quiet = 1;
69 	}
70 	if (cfg_file != NULL)
71 		load_cfg(cfg_file, cfg_cmd_q, quiet);
72 
73 	cmdq_continue(cfg_cmd_q);
74 }
75 
76 int
77 load_cfg(const char *path, struct cmd_q *cmdq, int quiet)
78 {
79 	FILE		*f;
80 	char		 delim[3] = { '\\', '\\', '\0' };
81 	u_int		 found;
82 	size_t		 line = 0;
83 	char		*buf, *cause1, *p;
84 	struct cmd_list	*cmdlist;
85 
86 	log_debug("loading %s", path);
87 	if ((f = fopen(path, "rb")) == NULL) {
88 		if (errno == ENOENT && quiet)
89 			return (0);
90 		cfg_add_cause("%s: %s", path, strerror(errno));
91 		return (-1);
92 	}
93 
94 	found = 0;
95 	while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
96 		log_debug("%s: %s", path, buf);
97 
98 		/* Skip empty lines. */
99 		p = buf;
100 		while (isspace((u_char) *p))
101 			p++;
102 		if (*p == '\0') {
103 			free(buf);
104 			continue;
105 		}
106 
107 		/* Parse and run the command. */
108 		if (cmd_string_parse(p, &cmdlist, path, line, &cause1) != 0) {
109 			free(buf);
110 			if (cause1 == NULL)
111 				continue;
112 			cfg_add_cause("%s:%zu: %s", path, line, cause1);
113 			free(cause1);
114 			continue;
115 		}
116 		free(buf);
117 
118 		if (cmdlist == NULL)
119 			continue;
120 		cmdq_append(cmdq, cmdlist, NULL);
121 		cmd_list_free(cmdlist);
122 		found++;
123 	}
124 	fclose(f);
125 
126 	return (found);
127 }
128 
129 void
130 cfg_default_done(__unused struct cmd_q *cmdq)
131 {
132 	if (--cfg_references != 0)
133 		return;
134 	cfg_finished = 1;
135 
136 	if (!RB_EMPTY(&sessions))
137 		cfg_show_causes(RB_MIN(sessions, &sessions));
138 
139 	cmdq_free(cfg_cmd_q);
140 	cfg_cmd_q = NULL;
141 
142 	if (cfg_client != NULL) {
143 		/*
144 		 * The client command queue starts with client_exit set to 1 so
145 		 * only continue if not empty (that is, we have been delayed
146 		 * during configuration parsing for long enough that the
147 		 * MSG_COMMAND has arrived), else the client will exit before
148 		 * the MSG_COMMAND which might tell it not to.
149 		 */
150 		if (!TAILQ_EMPTY(&cfg_client->cmdq->queue))
151 			cmdq_continue(cfg_client->cmdq);
152 		server_client_unref(cfg_client);
153 		cfg_client = NULL;
154 	}
155 }
156 
157 void
158 cfg_add_cause(const char *fmt, ...)
159 {
160 	va_list	 ap;
161 	char	*msg;
162 
163 	va_start(ap, fmt);
164 	xvasprintf(&msg, fmt, ap);
165 	va_end(ap);
166 
167 	cfg_ncauses++;
168 	cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes);
169 	cfg_causes[cfg_ncauses - 1] = msg;
170 }
171 
172 void
173 cfg_print_causes(struct cmd_q *cmdq)
174 {
175 	u_int	 i;
176 
177 	for (i = 0; i < cfg_ncauses; i++) {
178 		cmdq_print(cmdq, "%s", cfg_causes[i]);
179 		free(cfg_causes[i]);
180 	}
181 
182 	free(cfg_causes);
183 	cfg_causes = NULL;
184 	cfg_ncauses = 0;
185 }
186 
187 void
188 cfg_show_causes(struct session *s)
189 {
190 	struct window_pane	*wp;
191 	u_int			 i;
192 
193 	if (s == NULL || cfg_ncauses == 0)
194 		return;
195 	wp = s->curw->window->active;
196 
197 	window_pane_set_mode(wp, &window_copy_mode);
198 	window_copy_init_for_output(wp);
199 	for (i = 0; i < cfg_ncauses; i++) {
200 		window_copy_add(wp, "%s", cfg_causes[i]);
201 		free(cfg_causes[i]);
202 	}
203 
204 	free(cfg_causes);
205 	cfg_causes = NULL;
206 	cfg_ncauses = 0;
207 }
208