1 /* $OpenBSD: cfg.c,v 1.45 2016/05/12 16:05:33 tim 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 <unistd.h> 27 #include <util.h> 28 29 #include "tmux.h" 30 31 char *cfg_file; 32 struct cmd_q *cfg_cmd_q; 33 int cfg_finished; 34 int cfg_references; 35 char **cfg_causes; 36 u_int cfg_ncauses; 37 struct client *cfg_client; 38 39 void cfg_default_done(struct cmd_q *); 40 41 void 42 set_cfg_file(const char *path) 43 { 44 free(cfg_file); 45 cfg_file = xstrdup(path); 46 } 47 48 void 49 start_cfg(void) 50 { 51 const char *home; 52 int quiet = 0; 53 54 cfg_cmd_q = cmdq_new(NULL); 55 cfg_cmd_q->emptyfn = cfg_default_done; 56 57 cfg_finished = 0; 58 cfg_references = 1; 59 60 cfg_client = TAILQ_FIRST(&clients); 61 if (cfg_client != NULL) 62 cfg_client->references++; 63 64 load_cfg(TMUX_CONF, cfg_cmd_q, 1); 65 66 if (cfg_file == NULL && (home = find_home()) != NULL) { 67 xasprintf(&cfg_file, "%s/.tmux.conf", home); 68 quiet = 1; 69 } 70 if (cfg_file != NULL) 71 load_cfg(cfg_file, cfg_cmd_q, quiet); 72 73 cmdq_continue(cfg_cmd_q); 74 } 75 76 int 77 load_cfg(const char *path, struct cmd_q *cmdq, int quiet) 78 { 79 FILE *f; 80 char delim[3] = { '\\', '\\', '\0' }; 81 u_int found; 82 size_t line = 0; 83 char *buf, *cause1, *p; 84 struct cmd_list *cmdlist; 85 86 log_debug("loading %s", path); 87 if ((f = fopen(path, "rb")) == NULL) { 88 if (errno == ENOENT && quiet) 89 return (0); 90 cfg_add_cause("%s: %s", path, strerror(errno)); 91 return (-1); 92 } 93 94 found = 0; 95 while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { 96 log_debug("%s: %s", path, buf); 97 98 /* Skip empty lines. */ 99 p = buf; 100 while (isspace((u_char) *p)) 101 p++; 102 if (*p == '\0') { 103 free(buf); 104 continue; 105 } 106 107 /* Parse and run the command. */ 108 if (cmd_string_parse(p, &cmdlist, path, line, &cause1) != 0) { 109 free(buf); 110 if (cause1 == NULL) 111 continue; 112 cfg_add_cause("%s:%zu: %s", path, line, cause1); 113 free(cause1); 114 continue; 115 } 116 free(buf); 117 118 if (cmdlist == NULL) 119 continue; 120 cmdq_append(cmdq, cmdlist, NULL); 121 cmd_list_free(cmdlist); 122 found++; 123 } 124 fclose(f); 125 126 return (found); 127 } 128 129 void 130 cfg_default_done(__unused struct cmd_q *cmdq) 131 { 132 if (--cfg_references != 0) 133 return; 134 cfg_finished = 1; 135 136 if (!RB_EMPTY(&sessions)) 137 cfg_show_causes(RB_MIN(sessions, &sessions)); 138 139 cmdq_free(cfg_cmd_q); 140 cfg_cmd_q = NULL; 141 142 if (cfg_client != NULL) { 143 /* 144 * The client command queue starts with client_exit set to 1 so 145 * only continue if not empty (that is, we have been delayed 146 * during configuration parsing for long enough that the 147 * MSG_COMMAND has arrived), else the client will exit before 148 * the MSG_COMMAND which might tell it not to. 149 */ 150 if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) 151 cmdq_continue(cfg_client->cmdq); 152 server_client_unref(cfg_client); 153 cfg_client = NULL; 154 } 155 } 156 157 void 158 cfg_add_cause(const char *fmt, ...) 159 { 160 va_list ap; 161 char *msg; 162 163 va_start(ap, fmt); 164 xvasprintf(&msg, fmt, ap); 165 va_end(ap); 166 167 cfg_ncauses++; 168 cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); 169 cfg_causes[cfg_ncauses - 1] = msg; 170 } 171 172 void 173 cfg_print_causes(struct cmd_q *cmdq) 174 { 175 u_int i; 176 177 for (i = 0; i < cfg_ncauses; i++) { 178 cmdq_print(cmdq, "%s", cfg_causes[i]); 179 free(cfg_causes[i]); 180 } 181 182 free(cfg_causes); 183 cfg_causes = NULL; 184 cfg_ncauses = 0; 185 } 186 187 void 188 cfg_show_causes(struct session *s) 189 { 190 struct window_pane *wp; 191 u_int i; 192 193 if (s == NULL || cfg_ncauses == 0) 194 return; 195 wp = s->curw->window->active; 196 197 window_pane_set_mode(wp, &window_copy_mode); 198 window_copy_init_for_output(wp); 199 for (i = 0; i < cfg_ncauses; i++) { 200 window_copy_add(wp, "%s", cfg_causes[i]); 201 free(cfg_causes[i]); 202 } 203 204 free(cfg_causes); 205 cfg_causes = NULL; 206 cfg_ncauses = 0; 207 } 208