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