1 /* $OpenBSD: cmd-set-option.c,v 1.27 2009/12/03 17:44:02 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 /* 27 * Set an option. 28 */ 29 30 int cmd_set_option_exec(struct cmd *, struct cmd_ctx *); 31 32 const char *cmd_set_option_print( 33 const struct set_option_entry *, struct options_entry *); 34 void cmd_set_option_string(struct cmd_ctx *, 35 struct options *, const struct set_option_entry *, char *, int); 36 void cmd_set_option_number(struct cmd_ctx *, 37 struct options *, const struct set_option_entry *, char *); 38 void cmd_set_option_keys(struct cmd_ctx *, 39 struct options *, const struct set_option_entry *, char *); 40 void cmd_set_option_colour(struct cmd_ctx *, 41 struct options *, const struct set_option_entry *, char *); 42 void cmd_set_option_attributes(struct cmd_ctx *, 43 struct options *, const struct set_option_entry *, char *); 44 void cmd_set_option_flag(struct cmd_ctx *, 45 struct options *, const struct set_option_entry *, char *); 46 void cmd_set_option_choice(struct cmd_ctx *, 47 struct options *, const struct set_option_entry *, char *); 48 49 const struct cmd_entry cmd_set_option_entry = { 50 "set-option", "set", 51 "[-aguw] [-t target-session|target-window] option [value]", 52 CMD_ARG12, "aguw", 53 NULL, 54 cmd_target_parse, 55 cmd_set_option_exec, 56 cmd_target_free, 57 cmd_target_print 58 }; 59 60 const char *set_option_mode_keys_list[] = { 61 "emacs", "vi", NULL 62 }; 63 const char *set_option_clock_mode_style_list[] = { 64 "12", "24", NULL 65 }; 66 const char *set_option_status_keys_list[] = { 67 "emacs", "vi", NULL 68 }; 69 const char *set_option_status_justify_list[] = { 70 "left", "centre", "right", NULL 71 }; 72 const char *set_option_bell_action_list[] = { 73 "none", "any", "current", NULL 74 }; 75 76 const struct set_option_entry set_session_option_table[] = { 77 { "base-index", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 78 { "bell-action", SET_OPTION_CHOICE, 0, 0, set_option_bell_action_list }, 79 { "buffer-limit", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, 80 { "default-command", SET_OPTION_STRING, 0, 0, NULL }, 81 { "default-path", SET_OPTION_STRING, 0, 0, NULL }, 82 { "default-shell", SET_OPTION_STRING, 0, 0, NULL }, 83 { "default-terminal", SET_OPTION_STRING, 0, 0, NULL }, 84 { "display-panes-colour", SET_OPTION_COLOUR, 0, 0, NULL }, 85 { "display-panes-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, 86 { "display-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, 87 { "history-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 88 { "lock-after-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 89 { "lock-command", SET_OPTION_STRING, 0, 0, NULL }, 90 { "lock-server", SET_OPTION_FLAG, 0, 0, NULL }, 91 { "message-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 92 { "message-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 93 { "message-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 94 { "message-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 95 { "mouse-select-pane", SET_OPTION_FLAG, 0, 0, NULL }, 96 { "prefix", SET_OPTION_KEYS, 0, 0, NULL }, 97 { "repeat-time", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, 98 { "set-remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, 99 { "set-titles", SET_OPTION_FLAG, 0, 0, NULL }, 100 { "set-titles-string", SET_OPTION_STRING, 0, 0, NULL }, 101 { "status", SET_OPTION_FLAG, 0, 0, NULL }, 102 { "status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 103 { "status-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 104 { "status-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 105 { "status-interval", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 106 { "status-justify", 107 SET_OPTION_CHOICE, 0, 0, set_option_status_justify_list }, 108 { "status-keys", SET_OPTION_CHOICE, 0, 0, set_option_status_keys_list }, 109 { "status-left", SET_OPTION_STRING, 0, 0, NULL }, 110 { "status-left-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 111 { "status-left-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 112 { "status-left-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 113 { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, 114 { "status-right", SET_OPTION_STRING, 0, 0, NULL }, 115 { "status-right-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 116 { "status-right-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 117 { "status-right-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 118 { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, 119 { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL }, 120 { "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL }, 121 { "update-environment", SET_OPTION_STRING, 0, 0, NULL }, 122 { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL }, 123 { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL }, 124 { "visual-content", SET_OPTION_FLAG, 0, 0, NULL }, 125 { NULL, 0, 0, 0, NULL } 126 }; 127 128 const struct set_option_entry set_window_option_table[] = { 129 { "aggressive-resize", SET_OPTION_FLAG, 0, 0, NULL }, 130 { "automatic-rename", SET_OPTION_FLAG, 0, 0, NULL }, 131 { "clock-mode-colour", SET_OPTION_COLOUR, 0, 0, NULL }, 132 { "clock-mode-style", 133 SET_OPTION_CHOICE, 0, 0, set_option_clock_mode_style_list }, 134 { "force-height", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 135 { "force-width", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, 136 { "main-pane-height", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, 137 { "main-pane-width", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, 138 { "mode-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 139 { "mode-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 140 { "mode-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 141 { "mode-keys", SET_OPTION_CHOICE, 0, 0, set_option_mode_keys_list }, 142 { "mode-mouse", SET_OPTION_FLAG, 0, 0, NULL }, 143 { "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL }, 144 { "monitor-content", SET_OPTION_STRING, 0, 0, NULL }, 145 { "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, 146 { "synchronize-panes", SET_OPTION_FLAG, 0, 0, NULL }, 147 { "utf8", SET_OPTION_FLAG, 0, 0, NULL }, 148 { "window-status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 149 { "window-status-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 150 { "window-status-current-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, 151 { "window-status-current-bg", SET_OPTION_COLOUR, 0, 0, NULL }, 152 { "window-status-current-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 153 { "window-status-current-format", SET_OPTION_STRING, 0, 0, NULL }, 154 { "window-status-fg", SET_OPTION_COLOUR, 0, 0, NULL }, 155 { "window-status-format", SET_OPTION_STRING, 0, 0, NULL }, 156 { "xterm-keys", SET_OPTION_FLAG, 0, 0, NULL }, 157 { NULL, 0, 0, 0, NULL } 158 }; 159 160 int 161 cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) 162 { 163 struct cmd_target_data *data = self->data; 164 const struct set_option_entry *table; 165 struct session *s; 166 struct winlink *wl; 167 struct client *c; 168 struct options *oo; 169 const struct set_option_entry *entry, *opt; 170 struct jobs *jobs; 171 struct job *job, *nextjob; 172 u_int i; 173 int try_again; 174 175 if (cmd_check_flag(data->chflags, 'w')) { 176 table = set_window_option_table; 177 if (cmd_check_flag(data->chflags, 'g')) 178 oo = &global_w_options; 179 else { 180 wl = cmd_find_window(ctx, data->target, NULL); 181 if (wl == NULL) 182 return (-1); 183 oo = &wl->window->options; 184 } 185 } else { 186 table = set_session_option_table; 187 if (cmd_check_flag(data->chflags, 'g')) 188 oo = &global_s_options; 189 else { 190 s = cmd_find_session(ctx, data->target); 191 if (s == NULL) 192 return (-1); 193 oo = &s->options; 194 } 195 } 196 197 if (*data->arg == '\0') { 198 ctx->error(ctx, "invalid option"); 199 return (-1); 200 } 201 202 entry = NULL; 203 for (opt = table; opt->name != NULL; opt++) { 204 if (strncmp(opt->name, data->arg, strlen(data->arg)) != 0) 205 continue; 206 if (entry != NULL) { 207 ctx->error(ctx, "ambiguous option: %s", data->arg); 208 return (-1); 209 } 210 entry = opt; 211 212 /* Bail now if an exact match. */ 213 if (strcmp(entry->name, data->arg) == 0) 214 break; 215 } 216 if (entry == NULL) { 217 ctx->error(ctx, "unknown option: %s", data->arg); 218 return (-1); 219 } 220 221 if (cmd_check_flag(data->chflags, 'u')) { 222 if (cmd_check_flag(data->chflags, 'g')) { 223 ctx->error(ctx, 224 "can't unset global option: %s", entry->name); 225 return (-1); 226 } 227 if (data->arg2 != NULL) { 228 ctx->error(ctx, 229 "value passed to unset option: %s", entry->name); 230 return (-1); 231 } 232 233 options_remove(oo, entry->name); 234 ctx->info(ctx, "unset option: %s", entry->name); 235 } else { 236 switch (entry->type) { 237 case SET_OPTION_STRING: 238 cmd_set_option_string(ctx, oo, entry, 239 data->arg2, cmd_check_flag(data->chflags, 'a')); 240 break; 241 case SET_OPTION_NUMBER: 242 cmd_set_option_number(ctx, oo, entry, data->arg2); 243 break; 244 case SET_OPTION_KEYS: 245 cmd_set_option_keys(ctx, oo, entry, data->arg2); 246 break; 247 case SET_OPTION_COLOUR: 248 cmd_set_option_colour(ctx, oo, entry, data->arg2); 249 break; 250 case SET_OPTION_ATTRIBUTES: 251 cmd_set_option_attributes(ctx, oo, entry, data->arg2); 252 break; 253 case SET_OPTION_FLAG: 254 cmd_set_option_flag(ctx, oo, entry, data->arg2); 255 break; 256 case SET_OPTION_CHOICE: 257 cmd_set_option_choice(ctx, oo, entry, data->arg2); 258 break; 259 } 260 } 261 262 recalculate_sizes(); 263 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 264 c = ARRAY_ITEM(&clients, i); 265 if (c != NULL && c->session != NULL) 266 server_redraw_client(c); 267 } 268 269 /* 270 * Special-case: kill all persistent jobs if status-left, status-right 271 * or set-titles-string have changed. Persistent jobs are only used by 272 * the status line at the moment so this works XXX. 273 */ 274 if (strcmp(entry->name, "status-left") == 0 || 275 strcmp(entry->name, "status-right") == 0 || 276 strcmp(entry->name, "set-titles-string") == 0 || 277 strcmp(entry->name, "window-status-format") == 0) { 278 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 279 c = ARRAY_ITEM(&clients, i); 280 if (c == NULL || c->session == NULL) 281 continue; 282 283 jobs = &c->status_jobs; 284 do { 285 try_again = 0; 286 job = RB_ROOT(jobs); 287 while (job != NULL) { 288 nextjob = RB_NEXT(jobs, jobs, job); 289 if (job->flags & JOB_PERSIST) { 290 job_remove(jobs, job); 291 try_again = 1; 292 break; 293 } 294 job = nextjob; 295 } 296 } while (try_again); 297 server_redraw_client(c); 298 } 299 } 300 301 return (0); 302 } 303 304 const char * 305 cmd_set_option_print( 306 const struct set_option_entry *entry, struct options_entry *o) 307 { 308 static char out[BUFSIZ]; 309 const char *s; 310 struct keylist *keylist; 311 u_int i; 312 313 *out = '\0'; 314 switch (entry->type) { 315 case SET_OPTION_STRING: 316 xsnprintf(out, sizeof out, "\"%s\"", o->str); 317 break; 318 case SET_OPTION_NUMBER: 319 xsnprintf(out, sizeof out, "%lld", o->num); 320 break; 321 case SET_OPTION_KEYS: 322 keylist = o->data; 323 for (i = 0; i < ARRAY_LENGTH(keylist); i++) { 324 strlcat(out, key_string_lookup_key( 325 ARRAY_ITEM(keylist, i)), sizeof out); 326 if (i != ARRAY_LENGTH(keylist) - 1) 327 strlcat(out, ",", sizeof out); 328 } 329 break; 330 case SET_OPTION_COLOUR: 331 s = colour_tostring(o->num); 332 xsnprintf(out, sizeof out, "%s", s); 333 break; 334 case SET_OPTION_ATTRIBUTES: 335 s = attributes_tostring(o->num); 336 xsnprintf(out, sizeof out, "%s", s); 337 break; 338 case SET_OPTION_FLAG: 339 if (o->num) 340 strlcpy(out, "on", sizeof out); 341 else 342 strlcpy(out, "off", sizeof out); 343 break; 344 case SET_OPTION_CHOICE: 345 s = entry->choices[o->num]; 346 xsnprintf(out, sizeof out, "%s", s); 347 break; 348 } 349 return (out); 350 } 351 352 void 353 cmd_set_option_string(struct cmd_ctx *ctx, struct options *oo, 354 const struct set_option_entry *entry, char *value, int append) 355 { 356 struct options_entry *o; 357 char *oldvalue, *newvalue; 358 359 if (value == NULL) { 360 ctx->error(ctx, "empty value"); 361 return; 362 } 363 364 if (append) { 365 oldvalue = options_get_string(oo, entry->name); 366 xasprintf(&newvalue, "%s%s", oldvalue, value); 367 } else 368 newvalue = value; 369 370 o = options_set_string(oo, entry->name, "%s", newvalue); 371 ctx->info(ctx, 372 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 373 374 if (newvalue != value) 375 xfree(newvalue); 376 } 377 378 void 379 cmd_set_option_number(struct cmd_ctx *ctx, struct options *oo, 380 const struct set_option_entry *entry, char *value) 381 { 382 struct options_entry *o; 383 long long number; 384 const char *errstr; 385 386 if (value == NULL) { 387 ctx->error(ctx, "empty value"); 388 return; 389 } 390 391 number = strtonum(value, entry->minimum, entry->maximum, &errstr); 392 if (errstr != NULL) { 393 ctx->error(ctx, "value is %s: %s", errstr, value); 394 return; 395 } 396 397 o = options_set_number(oo, entry->name, number); 398 ctx->info(ctx, 399 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 400 } 401 402 void 403 cmd_set_option_keys(struct cmd_ctx *ctx, struct options *oo, 404 const struct set_option_entry *entry, char *value) 405 { 406 struct options_entry *o; 407 struct keylist *keylist; 408 char *copyvalue, *ptr, *str; 409 int key; 410 411 if (value == NULL) { 412 ctx->error(ctx, "empty value"); 413 return; 414 } 415 416 keylist = xmalloc(sizeof *keylist); 417 ARRAY_INIT(keylist); 418 419 ptr = copyvalue = xstrdup(value); 420 while ((str = strsep(&ptr, ",")) != NULL) { 421 if ((key = key_string_lookup_string(str)) == KEYC_NONE) { 422 xfree(keylist); 423 ctx->error(ctx, "unknown key: %s", str); 424 xfree(copyvalue); 425 return; 426 } 427 ARRAY_ADD(keylist, key); 428 } 429 xfree(copyvalue); 430 431 o = options_set_data(oo, entry->name, keylist, xfree); 432 ctx->info(ctx, 433 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 434 } 435 436 void 437 cmd_set_option_colour(struct cmd_ctx *ctx, struct options *oo, 438 const struct set_option_entry *entry, char *value) 439 { 440 struct options_entry *o; 441 int colour; 442 443 if (value == NULL) { 444 ctx->error(ctx, "empty value"); 445 return; 446 } 447 448 if ((colour = colour_fromstring(value)) == -1) { 449 ctx->error(ctx, "bad colour: %s", value); 450 return; 451 } 452 453 o = options_set_number(oo, entry->name, colour); 454 ctx->info(ctx, 455 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 456 } 457 458 void 459 cmd_set_option_attributes(struct cmd_ctx *ctx, struct options *oo, 460 const struct set_option_entry *entry, char *value) 461 { 462 struct options_entry *o; 463 int attr; 464 465 if (value == NULL) { 466 ctx->error(ctx, "empty value"); 467 return; 468 } 469 470 if ((attr = attributes_fromstring(value)) == -1) { 471 ctx->error(ctx, "bad attributes: %s", value); 472 return; 473 } 474 475 o = options_set_number(oo, entry->name, attr); 476 ctx->info(ctx, 477 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 478 } 479 480 void 481 cmd_set_option_flag(struct cmd_ctx *ctx, struct options *oo, 482 const struct set_option_entry *entry, char *value) 483 { 484 struct options_entry *o; 485 int flag; 486 487 if (value == NULL || *value == '\0') 488 flag = !options_get_number(oo, entry->name); 489 else { 490 if ((value[0] == '1' && value[1] == '\0') || 491 strcasecmp(value, "on") == 0 || 492 strcasecmp(value, "yes") == 0) 493 flag = 1; 494 else if ((value[0] == '0' && value[1] == '\0') || 495 strcasecmp(value, "off") == 0 || 496 strcasecmp(value, "no") == 0) 497 flag = 0; 498 else { 499 ctx->error(ctx, "bad value: %s", value); 500 return; 501 } 502 } 503 504 o = options_set_number(oo, entry->name, flag); 505 ctx->info(ctx, 506 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 507 } 508 509 void 510 cmd_set_option_choice(struct cmd_ctx *ctx, struct options *oo, 511 const struct set_option_entry *entry, char *value) 512 { 513 struct options_entry *o; 514 const char **choicep; 515 int n, choice = -1; 516 517 if (value == NULL) { 518 ctx->error(ctx, "empty value"); 519 return; 520 } 521 522 n = 0; 523 for (choicep = entry->choices; *choicep != NULL; choicep++) { 524 n++; 525 if (strncmp(*choicep, value, strlen(value)) != 0) 526 continue; 527 528 if (choice != -1) { 529 ctx->error(ctx, "ambiguous option value: %s", value); 530 return; 531 } 532 choice = n - 1; 533 } 534 if (choice == -1) { 535 ctx->error(ctx, "unknown option value: %s", value); 536 return; 537 } 538 539 o = options_set_number(oo, entry->name, choice); 540 ctx->info(ctx, 541 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o)); 542 } 543