cmd-source-file.c revision 1.1.1.12.2.1 1 /* $OpenBSD$ */
2
3 /*
4 * Copyright (c) 2008 Tiago Cunha <me (at) tiagocunha.org>
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 <glob.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "tmux.h"
28
29 /*
30 * Sources a configuration file.
31 */
32
33 #define CMD_SOURCE_FILE_DEPTH_LIMIT 50
34 static u_int cmd_source_file_depth;
35
36 static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
37
38 const struct cmd_entry cmd_source_file_entry = {
39 .name = "source-file",
40 .alias = "source",
41
42 .args = { "t:Fnqv", 1, -1, NULL },
43 .usage = "[-Fnqv] " CMD_TARGET_PANE_USAGE " path ...",
44
45 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
46
47 .flags = 0,
48 .exec = cmd_source_file_exec
49 };
50
51 struct cmd_source_file_data {
52 struct cmdq_item *item;
53 int flags;
54
55 struct cmdq_item *after;
56 enum cmd_retval retval;
57
58 u_int current;
59 char **files;
60 u_int nfiles;
61 };
62
63 static enum cmd_retval
64 cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
65 {
66 struct client *c = cmdq_get_client(item);
67
68 if (c == NULL) {
69 cmd_source_file_depth--;
70 log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
71 } else {
72 c->source_file_depth--;
73 log_debug("%s: depth now %u", __func__, c->source_file_depth);
74 }
75
76 cfg_print_causes(item);
77 return (CMD_RETURN_NORMAL);
78 }
79
80 static void
81 cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
82 {
83 struct cmdq_item *new_item;
84 u_int i;
85
86 if (cfg_finished) {
87 if (cdata->retval == CMD_RETURN_ERROR &&
88 c != NULL &&
89 c->session == NULL)
90 c->retval = 1;
91 new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
92 cmdq_insert_after(cdata->after, new_item);
93 }
94
95 for (i = 0; i < cdata->nfiles; i++)
96 free(cdata->files[i]);
97 free(cdata->files);
98 free(cdata);
99 }
100
101 static void
102 cmd_source_file_done(struct client *c, const char *path, int error,
103 int closed, struct evbuffer *buffer, void *data)
104 {
105 struct cmd_source_file_data *cdata = data;
106 struct cmdq_item *item = cdata->item;
107 void *bdata = EVBUFFER_DATA(buffer);
108 size_t bsize = EVBUFFER_LENGTH(buffer);
109 u_int n;
110 struct cmdq_item *new_item;
111 struct cmd_find_state *target = cmdq_get_target(item);
112
113 if (!closed)
114 return;
115
116 if (error != 0)
117 cmdq_error(item, "%s: %s", strerror(error), path);
118 else if (bsize != 0) {
119 if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
120 target, cdata->flags, &new_item) < 0)
121 cdata->retval = CMD_RETURN_ERROR;
122 else if (new_item != NULL)
123 cdata->after = new_item;
124 }
125
126 n = ++cdata->current;
127 if (n < cdata->nfiles)
128 file_read(c, cdata->files[n], cmd_source_file_done, cdata);
129 else {
130 cmd_source_file_complete(c, cdata);
131 cmdq_continue(item);
132 }
133 }
134
135 static void
136 cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
137 {
138 log_debug("%s: %s", __func__, path);
139 cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
140 sizeof *cdata->files);
141 cdata->files[cdata->nfiles++] = xstrdup(path);
142 }
143
144 static char *
145 cmd_source_file_quote_for_glob(const char *path)
146 {
147 char *quoted = xmalloc(2 * strlen(path) + 1), *q = quoted;
148 const char *p = path;
149
150 while (*p != '\0') {
151 if ((u_char)*p < 128 && !isalnum((u_char)*p) && *p != '/')
152 *q++ = '\\';
153 *q++ = *p++;
154 }
155 *q = '\0';
156 return (quoted);
157 }
158
159 static enum cmd_retval
160 cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
161 {
162 struct args *args = cmd_get_args(self);
163 struct cmd_source_file_data *cdata;
164 struct client *c = cmdq_get_client(item);
165 enum cmd_retval retval = CMD_RETURN_NORMAL;
166 char *pattern, *cwd, *expanded = NULL;
167 const char *path, *error;
168 glob_t g;
169 int result, parse_flags;
170 u_int i, j;
171
172 if (c == NULL) {
173 if (cmd_source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
174 cmdq_error(item, "too many nested files");
175 return (CMD_RETURN_ERROR);
176 }
177 cmd_source_file_depth++;
178 log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
179 } else {
180 if (c->source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
181 cmdq_error(item, "too many nested files");
182 return (CMD_RETURN_ERROR);
183 }
184 c->source_file_depth++;
185 log_debug("%s: depth now %u", __func__, c->source_file_depth);
186 }
187
188 cdata = xcalloc(1, sizeof *cdata);
189 cdata->item = item;
190
191 if (args_has(args, 'q'))
192 cdata->flags |= CMD_PARSE_QUIET;
193 if (args_has(args, 'n'))
194 cdata->flags |= CMD_PARSE_PARSEONLY;
195 if (c == NULL || ~c->flags & CLIENT_CONTROL) {
196 parse_flags = cmd_get_parse_flags(self);
197 if (args_has(args, 'v') || (parse_flags & CMD_PARSE_VERBOSE))
198 cdata->flags |= CMD_PARSE_VERBOSE;
199 }
200
201 cwd = cmd_source_file_quote_for_glob(server_client_get_cwd(c, NULL));
202
203 for (i = 0; i < args_count(args); i++) {
204 path = args_string(args, i);
205 if (args_has(args, 'F')) {
206 free(expanded);
207 expanded = format_single_from_target(item, path);
208 path = expanded;
209 }
210 if (strcmp(path, "-") == 0) {
211 cmd_source_file_add(cdata, "-");
212 continue;
213 }
214
215 if (*path == '/')
216 pattern = xstrdup(path);
217 else
218 xasprintf(&pattern, "%s/%s", cwd, path);
219 log_debug("%s: %s", __func__, pattern);
220
221 if ((result = glob(pattern, 0, NULL, &g)) != 0) {
222 if (result != GLOB_NOMATCH ||
223 (~cdata->flags & CMD_PARSE_QUIET)) {
224 if (result == GLOB_NOMATCH)
225 error = strerror(ENOENT);
226 else if (result == GLOB_NOSPACE)
227 error = strerror(ENOMEM);
228 else
229 error = strerror(EINVAL);
230 cmdq_error(item, "%s: %s", error, path);
231 retval = CMD_RETURN_ERROR;
232 }
233 globfree(&g);
234 free(pattern);
235 continue;
236 }
237 free(pattern);
238
239 for (j = 0; j < g.gl_pathc; j++)
240 cmd_source_file_add(cdata, g.gl_pathv[j]);
241 globfree(&g);
242 }
243 free(expanded);
244
245 cdata->after = item;
246 cdata->retval = retval;
247
248 if (cdata->nfiles != 0) {
249 file_read(c, cdata->files[0], cmd_source_file_done, cdata);
250 retval = CMD_RETURN_WAIT;
251 } else
252 cmd_source_file_complete(c, cdata);
253
254 free(cwd);
255 return (retval);
256 }
257