xref: /openbsd-src/usr.bin/tmux/cmd-source-file.c (revision 926caf46e6b066913ae51997515902941d742b8f)
1*926caf46Snicm /* $OpenBSD: cmd-source-file.c,v 1.56 2024/12/16 09:13:09 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
4311827fbSnicm  * Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
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 
21079748ecSnicm #include <errno.h>
22079748ecSnicm #include <glob.h>
237d053cf9Snicm #include <stdlib.h>
24079748ecSnicm #include <string.h>
25527df7ddSnicm #include <vis.h>
267d053cf9Snicm 
27311827fbSnicm #include "tmux.h"
28311827fbSnicm 
29311827fbSnicm /*
30311827fbSnicm  * Sources a configuration file.
31311827fbSnicm  */
32311827fbSnicm 
33*926caf46Snicm #define CMD_SOURCE_FILE_DEPTH_LIMIT 50
34*926caf46Snicm static u_int cmd_source_file_depth;
35*926caf46Snicm 
3668e0a7f2Snicm static enum cmd_retval	cmd_source_file_exec(struct cmd *, struct cmdq_item *);
37175d36ccSnicm 
38311827fbSnicm const struct cmd_entry cmd_source_file_entry = {
39c057646bSnicm 	.name = "source-file",
40c057646bSnicm 	.alias = "source",
41c057646bSnicm 
4204d313c5Snicm 	.args = { "t:Fnqv", 1, -1, NULL },
4304d313c5Snicm 	.usage = "[-Fnqv] " CMD_TARGET_PANE_USAGE " path ...",
4404d313c5Snicm 
4504d313c5Snicm 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
46c057646bSnicm 
47c057646bSnicm 	.flags = 0,
48c057646bSnicm 	.exec = cmd_source_file_exec
49311827fbSnicm };
50311827fbSnicm 
512ea347ccSnicm struct cmd_source_file_data {
522ea347ccSnicm 	struct cmdq_item	 *item;
532ea347ccSnicm 	int			  flags;
542ea347ccSnicm 
552ea347ccSnicm 	struct cmdq_item	 *after;
562ea347ccSnicm 	enum cmd_retval		  retval;
572ea347ccSnicm 
582ea347ccSnicm 	u_int			  current;
592ea347ccSnicm 	char			**files;
602ea347ccSnicm 	u_int			  nfiles;
612ea347ccSnicm };
622ea347ccSnicm 
632ea347ccSnicm static enum cmd_retval
642ea347ccSnicm cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
652ea347ccSnicm {
66*926caf46Snicm 	struct client	*c = cmdq_get_client(item);
67*926caf46Snicm 
68*926caf46Snicm 	if (c == NULL) {
69*926caf46Snicm 		cmd_source_file_depth--;
70*926caf46Snicm 		log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
71*926caf46Snicm 	} else {
72*926caf46Snicm 		c->source_file_depth--;
73*926caf46Snicm 		log_debug("%s: depth now %u", __func__, c->source_file_depth);
74*926caf46Snicm 	}
75*926caf46Snicm 
762ea347ccSnicm 	cfg_print_causes(item);
772ea347ccSnicm 	return (CMD_RETURN_NORMAL);
782ea347ccSnicm }
792ea347ccSnicm 
802ea347ccSnicm static void
812ea347ccSnicm cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
822ea347ccSnicm {
832ea347ccSnicm 	struct cmdq_item	*new_item;
841d297f78Snicm 	u_int			 i;
852ea347ccSnicm 
862ea347ccSnicm 	if (cfg_finished) {
87f7302605Snicm 		if (cdata->retval == CMD_RETURN_ERROR &&
88f7302605Snicm 		    c != NULL &&
89f7302605Snicm 		    c->session == NULL)
902ea347ccSnicm 			c->retval = 1;
912ea347ccSnicm 		new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
922ea347ccSnicm 		cmdq_insert_after(cdata->after, new_item);
932ea347ccSnicm 	}
942ea347ccSnicm 
951d297f78Snicm 	for (i = 0; i < cdata->nfiles; i++)
961d297f78Snicm 		free(cdata->files[i]);
972ea347ccSnicm 	free(cdata->files);
982ea347ccSnicm 	free(cdata);
992ea347ccSnicm }
1002ea347ccSnicm 
1012ea347ccSnicm static void
1022ea347ccSnicm cmd_source_file_done(struct client *c, const char *path, int error,
1032ea347ccSnicm     int closed, struct evbuffer *buffer, void *data)
1042ea347ccSnicm {
1052ea347ccSnicm 	struct cmd_source_file_data	*cdata = data;
1062ea347ccSnicm 	struct cmdq_item		*item = cdata->item;
1072ea347ccSnicm 	void				*bdata = EVBUFFER_DATA(buffer);
1082ea347ccSnicm 	size_t				 bsize = EVBUFFER_LENGTH(buffer);
1092ea347ccSnicm 	u_int				 n;
1102ea347ccSnicm 	struct cmdq_item		*new_item;
11104d313c5Snicm 	struct cmd_find_state		*target = cmdq_get_target(item);
1122ea347ccSnicm 
1132ea347ccSnicm 	if (!closed)
1142ea347ccSnicm 		return;
1152ea347ccSnicm 
1162ea347ccSnicm 	if (error != 0)
1172ea347ccSnicm 		cmdq_error(item, "%s: %s", path, strerror(error));
1182ea347ccSnicm 	else if (bsize != 0) {
1192ea347ccSnicm 		if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
12004d313c5Snicm 		    target, cdata->flags, &new_item) < 0)
1212ea347ccSnicm 			cdata->retval = CMD_RETURN_ERROR;
1222ea347ccSnicm 		else if (new_item != NULL)
1232ea347ccSnicm 			cdata->after = new_item;
1242ea347ccSnicm 	}
1252ea347ccSnicm 
1262ea347ccSnicm 	n = ++cdata->current;
1272ea347ccSnicm 	if (n < cdata->nfiles)
1282ea347ccSnicm 		file_read(c, cdata->files[n], cmd_source_file_done, cdata);
1292ea347ccSnicm 	else {
1302ea347ccSnicm 		cmd_source_file_complete(c, cdata);
1312ea347ccSnicm 		cmdq_continue(item);
1322ea347ccSnicm 	}
1332ea347ccSnicm }
1342ea347ccSnicm 
1352ea347ccSnicm static void
1362ea347ccSnicm cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
1372ea347ccSnicm {
13838cf620fSnicm 	char	resolved[PATH_MAX];
13938cf620fSnicm 
14038cf620fSnicm 	if (realpath(path, resolved) == NULL) {
14138cf620fSnicm 		log_debug("%s: realpath(\"%s\") failed: %s", __func__,
14238cf620fSnicm 			path, strerror(errno));
14338cf620fSnicm 	} else
14438cf620fSnicm 		path = resolved;
14538cf620fSnicm 
14695cd24f8Snicm 	log_debug("%s: %s", __func__, path);
147*926caf46Snicm 
1482ea347ccSnicm 	cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
1492ea347ccSnicm 	    sizeof *cdata->files);
1502ea347ccSnicm 	cdata->files[cdata->nfiles++] = xstrdup(path);
1512ea347ccSnicm }
1522ea347ccSnicm 
153dc1f0f5fSnicm static enum cmd_retval
15468e0a7f2Snicm cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
155311827fbSnicm {
15690d7ba38Snicm 	struct args			*args = cmd_get_args(self);
1572ea347ccSnicm 	struct cmd_source_file_data	*cdata;
158040343aeSnicm 	struct client			*c = cmdq_get_client(item);
1592ea347ccSnicm 	enum cmd_retval			 retval = CMD_RETURN_NORMAL;
1601693b10bSnicm 	char				*pattern, *cwd, *expanded = NULL;
161e88cca1eSnicm 	const char			*path, *error;
162079748ecSnicm 	glob_t				 g;
1631693b10bSnicm 	int				 result;
1641693b10bSnicm 	u_int				 i, j;
165175d36ccSnicm 
166*926caf46Snicm 	if (c == NULL) {
167*926caf46Snicm 		if (cmd_source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
168*926caf46Snicm 			cmdq_error(item, "too many nested files");
169*926caf46Snicm 			return (CMD_RETURN_ERROR);
170*926caf46Snicm 		}
171*926caf46Snicm 		cmd_source_file_depth++;
172*926caf46Snicm 		log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
173*926caf46Snicm 	} else {
174*926caf46Snicm 		if (c->source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
175*926caf46Snicm 			cmdq_error(item, "too many nested files");
176*926caf46Snicm 			return (CMD_RETURN_ERROR);
177*926caf46Snicm 		}
178*926caf46Snicm 		c->source_file_depth++;
179*926caf46Snicm 		log_debug("%s: depth now %u", __func__, c->source_file_depth);
180*926caf46Snicm 	}
181*926caf46Snicm 
1822ea347ccSnicm 	cdata = xcalloc(1, sizeof *cdata);
1832ea347ccSnicm 	cdata->item = item;
1842ea347ccSnicm 
18544c91ad4Snicm 	if (args_has(args, 'q'))
1862ea347ccSnicm 		cdata->flags |= CMD_PARSE_QUIET;
187df6ab229Snicm 	if (args_has(args, 'n'))
1882ea347ccSnicm 		cdata->flags |= CMD_PARSE_PARSEONLY;
1895304b409Snicm 	if (args_has(args, 'v'))
1902ea347ccSnicm 		cdata->flags |= CMD_PARSE_VERBOSE;
1912ea347ccSnicm 
192e88cca1eSnicm 	utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
193079748ecSnicm 
1941693b10bSnicm 	for (i = 0; i < args_count(args); i++) {
1951693b10bSnicm 		path = args_string(args, i);
1969f04068aSnicm 		if (args_has(args, 'F')) {
1971693b10bSnicm 			free(expanded);
1981693b10bSnicm 			expanded = format_single_from_target(item, path);
1991693b10bSnicm 			path = expanded;
2001693b10bSnicm 		}
2012ea347ccSnicm 		if (strcmp(path, "-") == 0) {
2022ea347ccSnicm 			cmd_source_file_add(cdata, "-");
2032ea347ccSnicm 			continue;
2042ea347ccSnicm 		}
2052ea347ccSnicm 
206e88cca1eSnicm 		if (*path == '/')
207e88cca1eSnicm 			pattern = xstrdup(path);
208e88cca1eSnicm 		else
209e88cca1eSnicm 			xasprintf(&pattern, "%s/%s", cwd, path);
210e88cca1eSnicm 		log_debug("%s: %s", __func__, pattern);
211e88cca1eSnicm 
212b58687efSnicm 		if ((result = glob(pattern, 0, NULL, &g)) != 0) {
213b58687efSnicm 			if (result != GLOB_NOMATCH ||
21420066256Stim 			    (~cdata->flags & CMD_PARSE_QUIET)) {
215b58687efSnicm 				if (result == GLOB_NOMATCH)
216b58687efSnicm 					error = strerror(ENOENT);
217b58687efSnicm 				else if (result == GLOB_NOSPACE)
218b58687efSnicm 					error = strerror(ENOMEM);
219b58687efSnicm 				else
220b58687efSnicm 					error = strerror(EINVAL);
221e88cca1eSnicm 				cmdq_error(item, "%s: %s", path, error);
222527df7ddSnicm 				retval = CMD_RETURN_ERROR;
223527df7ddSnicm 			}
2241d297f78Snicm 			globfree(&g);
225527df7ddSnicm 			free(pattern);
226e88cca1eSnicm 			continue;
227527df7ddSnicm 		}
228527df7ddSnicm 		free(pattern);
229527df7ddSnicm 
2302ea347ccSnicm 		for (j = 0; j < g.gl_pathc; j++)
2312ea347ccSnicm 			cmd_source_file_add(cdata, g.gl_pathv[j]);
2321d297f78Snicm 		globfree(&g);
233175d36ccSnicm 	}
23480b5a9d1Snicm 	free(expanded);
2352ea347ccSnicm 
2362ea347ccSnicm 	cdata->after = item;
2372ea347ccSnicm 	cdata->retval = retval;
2382ea347ccSnicm 
2392ea347ccSnicm 	if (cdata->nfiles != 0) {
2402ea347ccSnicm 		file_read(c, cdata->files[0], cmd_source_file_done, cdata);
2412ea347ccSnicm 		retval = CMD_RETURN_WAIT;
2422ea347ccSnicm 	} else
2432ea347ccSnicm 		cmd_source_file_complete(c, cdata);
244079748ecSnicm 
245e88cca1eSnicm 	free(cwd);
246079748ecSnicm 	return (retval);
247175d36ccSnicm }
248