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