MSG_EXIT can now have a return code in the message, so check for that
[tmux-openbsd.git] / cmd-set-option.c
blob8caf42e26a24584536bde4477786817d090461d6
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
27 * Set an option.
30 int cmd_set_option_exec(struct cmd *, struct cmd_ctx *);
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 *);
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
60 const char *set_option_mode_keys_list[] = {
61 "emacs", "vi", NULL
63 const char *set_option_clock_mode_style_list[] = {
64 "12", "24", NULL
66 const char *set_option_status_keys_list[] = {
67 "emacs", "vi", NULL
69 const char *set_option_status_justify_list[] = {
70 "left", "centre", "right", NULL
72 const char *set_option_bell_action_list[] = {
73 "none", "any", "current", NULL
76 const struct set_option_entry set_option_table[] = {
77 { "escape-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
78 { "quiet", SET_OPTION_FLAG, 0, 0, NULL },
79 { NULL, 0, 0, 0, NULL }
82 const struct set_option_entry set_session_option_table[] = {
83 { "base-index", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
84 { "bell-action", SET_OPTION_CHOICE, 0, 0, set_option_bell_action_list },
85 { "buffer-limit", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
86 { "default-command", SET_OPTION_STRING, 0, 0, NULL },
87 { "default-path", SET_OPTION_STRING, 0, 0, NULL },
88 { "default-shell", SET_OPTION_STRING, 0, 0, NULL },
89 { "default-terminal", SET_OPTION_STRING, 0, 0, NULL },
90 { "detach-on-destroy", SET_OPTION_FLAG, 0, 0, NULL },
91 { "display-panes-colour", SET_OPTION_COLOUR, 0, 0, NULL },
92 { "display-panes-active-colour", SET_OPTION_COLOUR, 0, 0, NULL },
93 { "display-panes-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
94 { "display-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
95 { "history-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
96 { "lock-after-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
97 { "lock-command", SET_OPTION_STRING, 0, 0, NULL },
98 { "lock-server", SET_OPTION_FLAG, 0, 0, NULL },
99 { "message-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
100 { "message-bg", SET_OPTION_COLOUR, 0, 0, NULL },
101 { "message-fg", SET_OPTION_COLOUR, 0, 0, NULL },
102 { "message-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
103 { "mouse-select-pane", SET_OPTION_FLAG, 0, 0, NULL },
104 { "pane-active-border-bg", SET_OPTION_COLOUR, 0, 0, NULL },
105 { "pane-active-border-fg", SET_OPTION_COLOUR, 0, 0, NULL },
106 { "pane-border-bg", SET_OPTION_COLOUR, 0, 0, NULL },
107 { "pane-border-fg", SET_OPTION_COLOUR, 0, 0, NULL },
108 { "prefix", SET_OPTION_KEYS, 0, 0, NULL },
109 { "repeat-time", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
110 { "set-remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL },
111 { "set-titles", SET_OPTION_FLAG, 0, 0, NULL },
112 { "set-titles-string", SET_OPTION_STRING, 0, 0, NULL },
113 { "status", SET_OPTION_FLAG, 0, 0, NULL },
114 { "status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
115 { "status-bg", SET_OPTION_COLOUR, 0, 0, NULL },
116 { "status-fg", SET_OPTION_COLOUR, 0, 0, NULL },
117 { "status-interval", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
118 { "status-justify",
119 SET_OPTION_CHOICE, 0, 0, set_option_status_justify_list },
120 { "status-keys", SET_OPTION_CHOICE, 0, 0, set_option_status_keys_list },
121 { "status-left", SET_OPTION_STRING, 0, 0, NULL },
122 { "status-left-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
123 { "status-left-bg", SET_OPTION_COLOUR, 0, 0, NULL },
124 { "status-left-fg", SET_OPTION_COLOUR, 0, 0, NULL },
125 { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
126 { "status-right", SET_OPTION_STRING, 0, 0, NULL },
127 { "status-right-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
128 { "status-right-bg", SET_OPTION_COLOUR, 0, 0, NULL },
129 { "status-right-fg", SET_OPTION_COLOUR, 0, 0, NULL },
130 { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
131 { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL },
132 { "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL },
133 { "update-environment", SET_OPTION_STRING, 0, 0, NULL },
134 { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL },
135 { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL },
136 { "visual-content", SET_OPTION_FLAG, 0, 0, NULL },
137 { NULL, 0, 0, 0, NULL }
140 const struct set_option_entry set_window_option_table[] = {
141 { "aggressive-resize", SET_OPTION_FLAG, 0, 0, NULL },
142 { "alternate-screen", SET_OPTION_FLAG, 0, 0, NULL },
143 { "automatic-rename", SET_OPTION_FLAG, 0, 0, NULL },
144 { "clock-mode-colour", SET_OPTION_COLOUR, 0, 0, NULL },
145 { "clock-mode-style",
146 SET_OPTION_CHOICE, 0, 0, set_option_clock_mode_style_list },
147 { "force-height", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
148 { "force-width", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
149 { "main-pane-height", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
150 { "main-pane-width", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
151 { "mode-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
152 { "mode-bg", SET_OPTION_COLOUR, 0, 0, NULL },
153 { "mode-fg", SET_OPTION_COLOUR, 0, 0, NULL },
154 { "mode-keys", SET_OPTION_CHOICE, 0, 0, set_option_mode_keys_list },
155 { "mode-mouse", SET_OPTION_FLAG, 0, 0, NULL },
156 { "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL },
157 { "monitor-content", SET_OPTION_STRING, 0, 0, NULL },
158 { "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL },
159 { "synchronize-panes", SET_OPTION_FLAG, 0, 0, NULL },
160 { "utf8", SET_OPTION_FLAG, 0, 0, NULL },
161 { "window-status-alert-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
162 { "window-status-alert-bg", SET_OPTION_COLOUR, 0, 0, NULL },
163 { "window-status-alert-fg", SET_OPTION_COLOUR, 0, 0, NULL },
164 { "window-status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
165 { "window-status-bg", SET_OPTION_COLOUR, 0, 0, NULL },
166 { "window-status-current-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
167 { "window-status-current-bg", SET_OPTION_COLOUR, 0, 0, NULL },
168 { "window-status-current-fg", SET_OPTION_COLOUR, 0, 0, NULL },
169 { "window-status-current-format", SET_OPTION_STRING, 0, 0, NULL },
170 { "window-status-fg", SET_OPTION_COLOUR, 0, 0, NULL },
171 { "window-status-format", SET_OPTION_STRING, 0, 0, NULL },
172 { "word-separators", SET_OPTION_STRING, 0, 0, NULL },
173 { "xterm-keys", SET_OPTION_FLAG, 0, 0, NULL },
174 { NULL, 0, 0, 0, NULL }
178 cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx)
180 struct cmd_target_data *data = self->data;
181 const struct set_option_entry *table;
182 struct session *s;
183 struct winlink *wl;
184 struct client *c;
185 struct options *oo;
186 const struct set_option_entry *entry, *opt;
187 struct jobs *jobs;
188 struct job *job, *nextjob;
189 u_int i;
190 int try_again;
192 if (cmd_check_flag(data->chflags, 's')) {
193 oo = &global_options;
194 table = set_option_table;
195 } else if (cmd_check_flag(data->chflags, 'w')) {
196 table = set_window_option_table;
197 if (cmd_check_flag(data->chflags, 'g'))
198 oo = &global_w_options;
199 else {
200 wl = cmd_find_window(ctx, data->target, NULL);
201 if (wl == NULL)
202 return (-1);
203 oo = &wl->window->options;
205 } else {
206 table = set_session_option_table;
207 if (cmd_check_flag(data->chflags, 'g'))
208 oo = &global_s_options;
209 else {
210 s = cmd_find_session(ctx, data->target);
211 if (s == NULL)
212 return (-1);
213 oo = &s->options;
217 if (*data->arg == '\0') {
218 ctx->error(ctx, "invalid option");
219 return (-1);
222 entry = NULL;
223 for (opt = table; opt->name != NULL; opt++) {
224 if (strncmp(opt->name, data->arg, strlen(data->arg)) != 0)
225 continue;
226 if (entry != NULL) {
227 ctx->error(ctx, "ambiguous option: %s", data->arg);
228 return (-1);
230 entry = opt;
232 /* Bail now if an exact match. */
233 if (strcmp(entry->name, data->arg) == 0)
234 break;
236 if (entry == NULL) {
237 ctx->error(ctx, "unknown option: %s", data->arg);
238 return (-1);
241 if (cmd_check_flag(data->chflags, 'u')) {
242 if (cmd_check_flag(data->chflags, 'g')) {
243 ctx->error(ctx,
244 "can't unset global option: %s", entry->name);
245 return (-1);
247 if (data->arg2 != NULL) {
248 ctx->error(ctx,
249 "value passed to unset option: %s", entry->name);
250 return (-1);
253 options_remove(oo, entry->name);
254 ctx->info(ctx, "unset option: %s", entry->name);
255 } else {
256 switch (entry->type) {
257 case SET_OPTION_STRING:
258 cmd_set_option_string(ctx, oo, entry,
259 data->arg2, cmd_check_flag(data->chflags, 'a'));
260 break;
261 case SET_OPTION_NUMBER:
262 cmd_set_option_number(ctx, oo, entry, data->arg2);
263 break;
264 case SET_OPTION_KEYS:
265 cmd_set_option_keys(ctx, oo, entry, data->arg2);
266 break;
267 case SET_OPTION_COLOUR:
268 cmd_set_option_colour(ctx, oo, entry, data->arg2);
269 break;
270 case SET_OPTION_ATTRIBUTES:
271 cmd_set_option_attributes(ctx, oo, entry, data->arg2);
272 break;
273 case SET_OPTION_FLAG:
274 cmd_set_option_flag(ctx, oo, entry, data->arg2);
275 break;
276 case SET_OPTION_CHOICE:
277 cmd_set_option_choice(ctx, oo, entry, data->arg2);
278 break;
282 recalculate_sizes();
283 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
284 c = ARRAY_ITEM(&clients, i);
285 if (c != NULL && c->session != NULL)
286 server_redraw_client(c);
290 * Special-case: kill all persistent jobs if status-left, status-right
291 * or set-titles-string have changed. Persistent jobs are only used by
292 * the status line at the moment so this works XXX.
294 if (strcmp(entry->name, "status-left") == 0 ||
295 strcmp(entry->name, "status-right") == 0 ||
296 strcmp(entry->name, "set-titles-string") == 0 ||
297 strcmp(entry->name, "window-status-format") == 0) {
298 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
299 c = ARRAY_ITEM(&clients, i);
300 if (c == NULL || c->session == NULL)
301 continue;
303 jobs = &c->status_jobs;
304 do {
305 try_again = 0;
306 job = RB_ROOT(jobs);
307 while (job != NULL) {
308 nextjob = RB_NEXT(jobs, jobs, job);
309 if (job->flags & JOB_PERSIST) {
310 job_remove(jobs, job);
311 try_again = 1;
312 break;
314 job = nextjob;
316 } while (try_again);
317 server_redraw_client(c);
321 return (0);
324 const char *
325 cmd_set_option_print(
326 const struct set_option_entry *entry, struct options_entry *o)
328 static char out[BUFSIZ];
329 const char *s;
330 struct keylist *keylist;
331 u_int i;
333 *out = '\0';
334 switch (entry->type) {
335 case SET_OPTION_STRING:
336 xsnprintf(out, sizeof out, "\"%s\"", o->str);
337 break;
338 case SET_OPTION_NUMBER:
339 xsnprintf(out, sizeof out, "%lld", o->num);
340 break;
341 case SET_OPTION_KEYS:
342 keylist = o->data;
343 for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
344 strlcat(out, key_string_lookup_key(
345 ARRAY_ITEM(keylist, i)), sizeof out);
346 if (i != ARRAY_LENGTH(keylist) - 1)
347 strlcat(out, ",", sizeof out);
349 break;
350 case SET_OPTION_COLOUR:
351 s = colour_tostring(o->num);
352 xsnprintf(out, sizeof out, "%s", s);
353 break;
354 case SET_OPTION_ATTRIBUTES:
355 s = attributes_tostring(o->num);
356 xsnprintf(out, sizeof out, "%s", s);
357 break;
358 case SET_OPTION_FLAG:
359 if (o->num)
360 strlcpy(out, "on", sizeof out);
361 else
362 strlcpy(out, "off", sizeof out);
363 break;
364 case SET_OPTION_CHOICE:
365 s = entry->choices[o->num];
366 xsnprintf(out, sizeof out, "%s", s);
367 break;
369 return (out);
372 void
373 cmd_set_option_string(struct cmd_ctx *ctx, struct options *oo,
374 const struct set_option_entry *entry, char *value, int append)
376 struct options_entry *o;
377 char *oldvalue, *newvalue;
379 if (value == NULL) {
380 ctx->error(ctx, "empty value");
381 return;
384 if (append) {
385 oldvalue = options_get_string(oo, entry->name);
386 xasprintf(&newvalue, "%s%s", oldvalue, value);
387 } else
388 newvalue = value;
390 o = options_set_string(oo, entry->name, "%s", newvalue);
391 ctx->info(ctx,
392 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
394 if (newvalue != value)
395 xfree(newvalue);
398 void
399 cmd_set_option_number(struct cmd_ctx *ctx, struct options *oo,
400 const struct set_option_entry *entry, char *value)
402 struct options_entry *o;
403 long long number;
404 const char *errstr;
406 if (value == NULL) {
407 ctx->error(ctx, "empty value");
408 return;
411 number = strtonum(value, entry->minimum, entry->maximum, &errstr);
412 if (errstr != NULL) {
413 ctx->error(ctx, "value is %s: %s", errstr, value);
414 return;
417 o = options_set_number(oo, entry->name, number);
418 ctx->info(ctx,
419 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
422 void
423 cmd_set_option_keys(struct cmd_ctx *ctx, struct options *oo,
424 const struct set_option_entry *entry, char *value)
426 struct options_entry *o;
427 struct keylist *keylist;
428 char *copyvalue, *ptr, *str;
429 int key;
431 if (value == NULL) {
432 ctx->error(ctx, "empty value");
433 return;
436 keylist = xmalloc(sizeof *keylist);
437 ARRAY_INIT(keylist);
439 ptr = copyvalue = xstrdup(value);
440 while ((str = strsep(&ptr, ",")) != NULL) {
441 if ((key = key_string_lookup_string(str)) == KEYC_NONE) {
442 xfree(keylist);
443 ctx->error(ctx, "unknown key: %s", str);
444 xfree(copyvalue);
445 return;
447 ARRAY_ADD(keylist, key);
449 xfree(copyvalue);
451 o = options_set_data(oo, entry->name, keylist, xfree);
452 ctx->info(ctx,
453 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
456 void
457 cmd_set_option_colour(struct cmd_ctx *ctx, struct options *oo,
458 const struct set_option_entry *entry, char *value)
460 struct options_entry *o;
461 int colour;
463 if (value == NULL) {
464 ctx->error(ctx, "empty value");
465 return;
468 if ((colour = colour_fromstring(value)) == -1) {
469 ctx->error(ctx, "bad colour: %s", value);
470 return;
473 o = options_set_number(oo, entry->name, colour);
474 ctx->info(ctx,
475 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
478 void
479 cmd_set_option_attributes(struct cmd_ctx *ctx, struct options *oo,
480 const struct set_option_entry *entry, char *value)
482 struct options_entry *o;
483 int attr;
485 if (value == NULL) {
486 ctx->error(ctx, "empty value");
487 return;
490 if ((attr = attributes_fromstring(value)) == -1) {
491 ctx->error(ctx, "bad attributes: %s", value);
492 return;
495 o = options_set_number(oo, entry->name, attr);
496 ctx->info(ctx,
497 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
500 void
501 cmd_set_option_flag(struct cmd_ctx *ctx, struct options *oo,
502 const struct set_option_entry *entry, char *value)
504 struct options_entry *o;
505 int flag;
507 if (value == NULL || *value == '\0')
508 flag = !options_get_number(oo, entry->name);
509 else {
510 if ((value[0] == '1' && value[1] == '\0') ||
511 strcasecmp(value, "on") == 0 ||
512 strcasecmp(value, "yes") == 0)
513 flag = 1;
514 else if ((value[0] == '0' && value[1] == '\0') ||
515 strcasecmp(value, "off") == 0 ||
516 strcasecmp(value, "no") == 0)
517 flag = 0;
518 else {
519 ctx->error(ctx, "bad value: %s", value);
520 return;
524 o = options_set_number(oo, entry->name, flag);
525 ctx->info(ctx,
526 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));
529 void
530 cmd_set_option_choice(struct cmd_ctx *ctx, struct options *oo,
531 const struct set_option_entry *entry, char *value)
533 struct options_entry *o;
534 const char **choicep;
535 int n, choice = -1;
537 if (value == NULL) {
538 ctx->error(ctx, "empty value");
539 return;
542 n = 0;
543 for (choicep = entry->choices; *choicep != NULL; choicep++) {
544 n++;
545 if (strncmp(*choicep, value, strlen(value)) != 0)
546 continue;
548 if (choice != -1) {
549 ctx->error(ctx, "ambiguous option value: %s", value);
550 return;
552 choice = n - 1;
554 if (choice == -1) {
555 ctx->error(ctx, "unknown option value: %s", value);
556 return;
559 o = options_set_number(oo, entry->name, choice);
560 ctx->info(ctx,
561 "set option: %s -> %s", o->name, cmd_set_option_print(entry, o));