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 void 70 start_cfg(void) 71 { 72 const char *home; 73 int flags = 0; 74 struct client *c; 75 76 /* 77 * Configuration files are loaded without a client, so commands are run 78 * in the global queue with item->client NULL. 79 * 80 * However, we must block the initial client (but just the initial 81 * client) so that its command runs after the configuration is loaded. 82 * Because start_cfg() is called so early, we can be sure the client's 83 * command queue is currently empty and our callback will be at the 84 * front - we need to get in before MSG_COMMAND. 85 */ 86 cfg_client = c = TAILQ_FIRST(&clients); 87 if (c != NULL) { 88 cfg_item = cmdq_get_callback(cfg_client_done, NULL); 89 cmdq_append(c, cfg_item); 90 } 91 92 if (cfg_file == NULL) 93 load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL); 94 95 if (cfg_file == NULL && (home = find_home()) != NULL) { 96 xasprintf(&cfg_file, "%s/.tmux.conf", home); 97 flags = CMD_PARSE_QUIET; 98 } 99 if (cfg_file != NULL) 100 load_cfg(cfg_file, c, NULL, flags, NULL); 101 102 cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); 103 } 104 105 int 106 load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, 107 struct cmdq_item **new_item) 108 { 109 FILE *f; 110 struct cmd_parse_input pi; 111 struct cmd_parse_result *pr; 112 struct cmdq_item *new_item0; 113 114 if (new_item != NULL) 115 *new_item = NULL; 116 117 log_debug("loading %s", path); 118 if ((f = fopen(path, "rb")) == NULL) { 119 if (errno == ENOENT && (flags & CMD_PARSE_QUIET)) 120 return (0); 121 cfg_add_cause("%s: %s", path, strerror(errno)); 122 return (-1); 123 } 124 125 memset(&pi, 0, sizeof pi); 126 pi.flags = flags; 127 pi.file = path; 128 pi.line = 1; 129 pi.item = item; 130 pi.c = c; 131 132 pr = cmd_parse_from_file(f, &pi); 133 fclose(f); 134 if (pr->status == CMD_PARSE_EMPTY) 135 return (0); 136 if (pr->status == CMD_PARSE_ERROR) { 137 cfg_add_cause("%s", pr->error); 138 free(pr->error); 139 return (-1); 140 } 141 if (flags & CMD_PARSE_PARSEONLY) { 142 cmd_list_free(pr->cmdlist); 143 return (0); 144 } 145 146 new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); 147 if (item != NULL) 148 cmdq_insert_after(item, new_item0); 149 else 150 cmdq_append(NULL, new_item0); 151 cmd_list_free(pr->cmdlist); 152 153 if (new_item != NULL) 154 *new_item = new_item0; 155 return (0); 156 } 157 158 void 159 cfg_add_cause(const char *fmt, ...) 160 { 161 va_list ap; 162 char *msg; 163 164 va_start(ap, fmt); 165 xvasprintf(&msg, fmt, ap); 166 va_end(ap); 167 168 cfg_ncauses++; 169 cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); 170 cfg_causes[cfg_ncauses - 1] = msg; 171 } 172 173 void 174 cfg_print_causes(struct cmdq_item *item) 175 { 176 u_int i; 177 178 for (i = 0; i < cfg_ncauses; i++) { 179 cmdq_print(item, "%s", cfg_causes[i]); 180 free(cfg_causes[i]); 181 } 182 183 free(cfg_causes); 184 cfg_causes = NULL; 185 cfg_ncauses = 0; 186 } 187 188 void 189 cfg_show_causes(struct session *s) 190 { 191 struct window_pane *wp; 192 struct window_mode_entry *wme; 193 u_int i; 194 195 if (s == NULL || cfg_ncauses == 0) 196 return; 197 wp = s->curw->window->active; 198 199 wme = TAILQ_FIRST(&wp->modes); 200 if (wme == NULL || wme->mode != &window_view_mode) 201 window_pane_set_mode(wp, &window_view_mode, NULL, NULL); 202 for (i = 0; i < cfg_ncauses; i++) { 203 window_copy_add(wp, "%s", cfg_causes[i]); 204 free(cfg_causes[i]); 205 } 206 207 free(cfg_causes); 208 cfg_causes = NULL; 209 cfg_ncauses = 0; 210 } 211