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 static char *cfg_file; 30 int cfg_finished; 31 static char **cfg_causes; 32 static u_int cfg_ncauses; 33 static struct cmdq_item *cfg_item; 34 35 static enum cmd_retval 36 cfg_client_done(__unused struct cmdq_item *item, __unused void *data) 37 { 38 if (!cfg_finished) 39 return (CMD_RETURN_WAIT); 40 return (CMD_RETURN_NORMAL); 41 } 42 43 static enum cmd_retval 44 cfg_done(__unused struct cmdq_item *item, __unused void *data) 45 { 46 if (cfg_finished) 47 return (CMD_RETURN_NORMAL); 48 cfg_finished = 1; 49 50 if (!RB_EMPTY(&sessions)) 51 cfg_show_causes(RB_MIN(sessions, &sessions)); 52 53 if (cfg_item != NULL) 54 cfg_item->flags &= ~CMDQ_WAITING; 55 56 status_prompt_load_history(); 57 58 return (CMD_RETURN_NORMAL); 59 } 60 61 void 62 set_cfg_file(const char *path) 63 { 64 free(cfg_file); 65 cfg_file = xstrdup(path); 66 } 67 68 void 69 start_cfg(void) 70 { 71 const char *home; 72 int quiet = 0; 73 struct client *c; 74 75 /* 76 * Configuration files are loaded without a client, so NULL is passed 77 * into load_cfg() and commands run in the global queue with 78 * 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 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 load_cfg(TMUX_CONF, NULL, NULL, 1); 93 94 if (cfg_file == NULL && (home = find_home()) != NULL) { 95 xasprintf(&cfg_file, "%s/.tmux.conf", home); 96 quiet = 1; 97 } 98 if (cfg_file != NULL) 99 load_cfg(cfg_file, NULL, NULL, quiet); 100 101 cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); 102 } 103 104 int 105 load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) 106 { 107 FILE *f; 108 const char delim[3] = { '\\', '\\', '\0' }; 109 u_int found = 0; 110 size_t line = 0; 111 char *buf, *cause1, *p, *q, *s; 112 struct cmd_list *cmdlist; 113 struct cmdq_item *new_item; 114 int condition = 0; 115 struct format_tree *ft; 116 117 log_debug("loading %s", path); 118 if ((f = fopen(path, "rb")) == NULL) { 119 if (errno == ENOENT && quiet) 120 return (0); 121 cfg_add_cause("%s: %s", path, strerror(errno)); 122 return (-1); 123 } 124 125 while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { 126 log_debug("%s: %s", path, buf); 127 128 p = buf; 129 while (isspace((u_char)*p)) 130 p++; 131 if (*p == '\0') { 132 free(buf); 133 continue; 134 } 135 q = p + strlen(p) - 1; 136 while (q != p && isspace((u_char)*q)) 137 *q-- = '\0'; 138 139 if (condition != 0 && strcmp(p, "%endif") == 0) { 140 condition = 0; 141 continue; 142 } 143 if (strncmp(p, "%if ", 4) == 0) { 144 if (condition != 0) { 145 cfg_add_cause("%s:%zu: nested %%if", path, 146 line); 147 continue; 148 } 149 ft = format_create(NULL, NULL, FORMAT_NONE, 150 FORMAT_NOJOBS); 151 152 s = p + 3; 153 while (isspace((u_char)*s)) 154 s++; 155 s = format_expand(ft, s); 156 if (*s != '\0' && (s[0] != '0' || s[1] != '\0')) 157 condition = 1; 158 else 159 condition = -1; 160 free(s); 161 162 format_free(ft); 163 continue; 164 } 165 if (condition == -1) 166 continue; 167 168 cmdlist = cmd_string_parse(p, path, line, &cause1); 169 if (cmdlist == NULL) { 170 free(buf); 171 if (cause1 == NULL) 172 continue; 173 cfg_add_cause("%s:%zu: %s", path, line, cause1); 174 free(cause1); 175 continue; 176 } 177 free(buf); 178 179 if (cmdlist == NULL) 180 continue; 181 new_item = cmdq_get_command(cmdlist, NULL, NULL, 0); 182 if (item != NULL) 183 cmdq_insert_after(item, new_item); 184 else 185 cmdq_append(c, new_item); 186 cmd_list_free(cmdlist); 187 188 found++; 189 } 190 fclose(f); 191 192 return (found); 193 } 194 195 void 196 cfg_add_cause(const char *fmt, ...) 197 { 198 va_list ap; 199 char *msg; 200 201 va_start(ap, fmt); 202 xvasprintf(&msg, fmt, ap); 203 va_end(ap); 204 205 cfg_ncauses++; 206 cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); 207 cfg_causes[cfg_ncauses - 1] = msg; 208 } 209 210 void 211 cfg_print_causes(struct cmdq_item *item) 212 { 213 u_int i; 214 215 for (i = 0; i < cfg_ncauses; i++) { 216 cmdq_print(item, "%s", cfg_causes[i]); 217 free(cfg_causes[i]); 218 } 219 220 free(cfg_causes); 221 cfg_causes = NULL; 222 cfg_ncauses = 0; 223 } 224 225 void 226 cfg_show_causes(struct session *s) 227 { 228 struct window_pane *wp; 229 u_int i; 230 231 if (s == NULL || cfg_ncauses == 0) 232 return; 233 wp = s->curw->window->active; 234 235 window_pane_set_mode(wp, &window_copy_mode, NULL, NULL); 236 window_copy_init_for_output(wp); 237 for (i = 0; i < cfg_ncauses; i++) { 238 window_copy_add(wp, "%s", cfg_causes[i]); 239 free(cfg_causes[i]); 240 } 241 242 free(cfg_causes); 243 cfg_causes = NULL; 244 cfg_ncauses = 0; 245 } 246