1 /* $OpenBSD$ */ 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 27 #include "tmux.h" 28 29 struct client *cfg_client; 30 static char *cfg_file; 31 int cfg_finished; 32 static char **cfg_causes; 33 static u_int cfg_ncauses; 34 static struct cmdq_item *cfg_item; 35 36 static enum cmd_retval 37 cfg_client_done(__unused struct cmdq_item *item, __unused void *data) 38 { 39 if (!cfg_finished) 40 return (CMD_RETURN_WAIT); 41 return (CMD_RETURN_NORMAL); 42 } 43 44 static enum cmd_retval 45 cfg_done(__unused struct cmdq_item *item, __unused void *data) 46 { 47 if (cfg_finished) 48 return (CMD_RETURN_NORMAL); 49 cfg_finished = 1; 50 51 if (!RB_EMPTY(&sessions)) 52 cfg_show_causes(RB_MIN(sessions, &sessions)); 53 54 if (cfg_item != NULL) 55 cmdq_continue(cfg_item); 56 57 status_prompt_load_history(); 58 59 return (CMD_RETURN_NORMAL); 60 } 61 62 void 63 set_cfg_file(const char *path) 64 { 65 free(cfg_file); 66 cfg_file = xstrdup(path); 67 } 68 69 static char * 70 expand_cfg_file(const char *path, const char *home) 71 { 72 char *expanded, *name; 73 const char *end; 74 struct environ_entry *value; 75 76 if (strncmp(path, "~/", 2) == 0) { 77 if (home == NULL) 78 return (NULL); 79 xasprintf(&expanded, "%s%s", home, path + 1); 80 return (expanded); 81 } 82 83 if (*path == '$') { 84 end = strchr(path, '/'); 85 if (end == NULL) 86 name = xstrdup(path + 1); 87 else 88 name = xstrndup(path + 1, end - path - 1); 89 value = environ_find(global_environ, name); 90 free(name); 91 if (value == NULL) 92 return (NULL); 93 if (end == NULL) 94 end = ""; 95 xasprintf(&expanded, "%s%s", value->value, end); 96 return (expanded); 97 } 98 99 return (xstrdup(path)); 100 } 101 102 void 103 start_cfg(void) 104 { 105 const char *home = find_home(); 106 struct client *c; 107 char *path, *copy, *next, *expanded; 108 109 /* 110 * Configuration files are loaded without a client, so commands are run 111 * in the global queue with item->client NULL. 112 * 113 * However, we must block the initial client (but just the initial 114 * client) so that its command runs after the configuration is loaded. 115 * Because start_cfg() is called so early, we can be sure the client's 116 * command queue is currently empty and our callback will be at the 117 * front - we need to get in before MSG_COMMAND. 118 */ 119 cfg_client = c = TAILQ_FIRST(&clients); 120 if (c != NULL) { 121 cfg_item = cmdq_get_callback(cfg_client_done, NULL); 122 cmdq_append(c, cfg_item); 123 } 124 125 if (cfg_file == NULL) { 126 path = copy = xstrdup(TMUX_CONF); 127 while ((next = strsep(&path, ":")) != NULL) { 128 expanded = expand_cfg_file(next, home); 129 if (expanded == NULL) { 130 log_debug("couldn't expand %s", next); 131 continue; 132 } 133 log_debug("expanded %s to %s", next, expanded); 134 load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL); 135 free(expanded); 136 } 137 free(copy); 138 } else 139 load_cfg(cfg_file, c, NULL, 0, NULL); 140 141 cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); 142 } 143 144 int 145 load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, 146 struct cmdq_item **new_item) 147 { 148 FILE *f; 149 struct cmd_parse_input pi; 150 struct cmd_parse_result *pr; 151 struct cmdq_item *new_item0; 152 153 if (new_item != NULL) 154 *new_item = NULL; 155 156 log_debug("loading %s", path); 157 if ((f = fopen(path, "rb")) == NULL) { 158 if (errno == ENOENT && (flags & CMD_PARSE_QUIET)) 159 return (0); 160 cfg_add_cause("%s: %s", path, strerror(errno)); 161 return (-1); 162 } 163 164 memset(&pi, 0, sizeof pi); 165 pi.flags = flags; 166 pi.file = path; 167 pi.line = 1; 168 pi.item = item; 169 pi.c = c; 170 171 pr = cmd_parse_from_file(f, &pi); 172 fclose(f); 173 if (pr->status == CMD_PARSE_EMPTY) 174 return (0); 175 if (pr->status == CMD_PARSE_ERROR) { 176 cfg_add_cause("%s", pr->error); 177 free(pr->error); 178 return (-1); 179 } 180 if (flags & CMD_PARSE_PARSEONLY) { 181 cmd_list_free(pr->cmdlist); 182 return (0); 183 } 184 185 new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); 186 if (item != NULL) 187 new_item0 = cmdq_insert_after(item, new_item0); 188 else 189 new_item0 = cmdq_append(NULL, new_item0); 190 cmd_list_free(pr->cmdlist); 191 192 if (new_item != NULL) 193 *new_item = new_item0; 194 return (0); 195 } 196 197 int 198 load_cfg_from_buffer(const void *buf, size_t len, const char *path, 199 struct client *c, struct cmdq_item *item, int flags, 200 struct cmdq_item **new_item) 201 { 202 struct cmd_parse_input pi; 203 struct cmd_parse_result *pr; 204 struct cmdq_item *new_item0; 205 206 if (new_item != NULL) 207 *new_item = NULL; 208 209 log_debug("loading %s", path); 210 211 memset(&pi, 0, sizeof pi); 212 pi.flags = flags; 213 pi.file = path; 214 pi.line = 1; 215 pi.item = item; 216 pi.c = c; 217 218 pr = cmd_parse_from_buffer(buf, len, &pi); 219 if (pr->status == CMD_PARSE_EMPTY) 220 return (0); 221 if (pr->status == CMD_PARSE_ERROR) { 222 cfg_add_cause("%s", pr->error); 223 free(pr->error); 224 return (-1); 225 } 226 if (flags & CMD_PARSE_PARSEONLY) { 227 cmd_list_free(pr->cmdlist); 228 return (0); 229 } 230 231 new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); 232 if (item != NULL) 233 new_item0 = cmdq_insert_after(item, new_item0); 234 else 235 new_item0 = cmdq_append(NULL, new_item0); 236 cmd_list_free(pr->cmdlist); 237 238 if (new_item != NULL) 239 *new_item = new_item0; 240 return (0); 241 } 242 243 void 244 cfg_add_cause(const char *fmt, ...) 245 { 246 va_list ap; 247 char *msg; 248 249 va_start(ap, fmt); 250 xvasprintf(&msg, fmt, ap); 251 va_end(ap); 252 253 cfg_ncauses++; 254 cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); 255 cfg_causes[cfg_ncauses - 1] = msg; 256 } 257 258 void 259 cfg_print_causes(struct cmdq_item *item) 260 { 261 u_int i; 262 263 for (i = 0; i < cfg_ncauses; i++) { 264 cmdq_print(item, "%s", cfg_causes[i]); 265 free(cfg_causes[i]); 266 } 267 268 free(cfg_causes); 269 cfg_causes = NULL; 270 cfg_ncauses = 0; 271 } 272 273 void 274 cfg_show_causes(struct session *s) 275 { 276 struct window_pane *wp; 277 struct window_mode_entry *wme; 278 u_int i; 279 280 if (s == NULL || cfg_ncauses == 0) 281 return; 282 wp = s->curw->window->active; 283 284 wme = TAILQ_FIRST(&wp->modes); 285 if (wme == NULL || wme->mode != &window_view_mode) 286 window_pane_set_mode(wp, &window_view_mode, NULL, NULL); 287 for (i = 0; i < cfg_ncauses; i++) { 288 window_copy_add(wp, "%s", cfg_causes[i]); 289 free(cfg_causes[i]); 290 } 291 292 free(cfg_causes); 293 cfg_causes = NULL; 294 cfg_ncauses = 0; 295 } 296