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