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