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