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