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