Annotation of src/usr.bin/tmux/cmd-set-option.c, Revision 1.28
1.28 ! nicm 1: /* $OpenBSD: cmd-set-option.c,v 1.27 2009/12/03 17:44:02 nicm Exp $ */
1.1 nicm 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:
1.27 nicm 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 *,
1.28 ! nicm 37: struct options *, const struct set_option_entry *, char *);
1.27 nicm 38: void cmd_set_option_keys(struct cmd_ctx *,
1.28 ! nicm 39: struct options *, const struct set_option_entry *, char *);
1.27 nicm 40: void cmd_set_option_colour(struct cmd_ctx *,
1.28 ! nicm 41: struct options *, const struct set_option_entry *, char *);
1.27 nicm 42: void cmd_set_option_attributes(struct cmd_ctx *,
1.28 ! nicm 43: struct options *, const struct set_option_entry *, char *);
1.27 nicm 44: void cmd_set_option_flag(struct cmd_ctx *,
1.28 ! nicm 45: struct options *, const struct set_option_entry *, char *);
1.27 nicm 46: void cmd_set_option_choice(struct cmd_ctx *,
1.28 ! nicm 47: struct options *, const struct set_option_entry *, char *);
1.27 nicm 48:
1.1 nicm 49: const struct cmd_entry cmd_set_option_entry = {
50: "set-option", "set",
1.27 nicm 51: "[-aguw] [-t target-session|target-window] option [value]",
52: CMD_ARG12, "aguw",
1.1 nicm 53: NULL,
1.14 nicm 54: cmd_target_parse,
1.1 nicm 55: cmd_set_option_exec,
1.14 nicm 56: cmd_target_free,
57: cmd_target_print
1.1 nicm 58: };
59:
1.27 nicm 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: };
1.1 nicm 66: const char *set_option_status_keys_list[] = {
67: "emacs", "vi", NULL
68: };
1.8 nicm 69: const char *set_option_status_justify_list[] = {
70: "left", "centre", "right", NULL
71: };
1.1 nicm 72: const char *set_option_bell_action_list[] = {
73: "none", "any", "current", NULL
74: };
1.27 nicm 75:
76: const struct set_option_entry set_session_option_table[] = {
1.15 nicm 77: { "base-index", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
1.1 nicm 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 },
1.17 nicm 82: { "default-shell", SET_OPTION_STRING, 0, 0, NULL },
1.4 nicm 83: { "default-terminal", SET_OPTION_STRING, 0, 0, NULL },
1.16 nicm 84: { "display-panes-colour", SET_OPTION_COLOUR, 0, 0, NULL },
85: { "display-panes-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
1.1 nicm 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 },
1.20 nicm 89: { "lock-command", SET_OPTION_STRING, 0, 0, NULL },
1.21 nicm 90: { "lock-server", SET_OPTION_FLAG, 0, 0, NULL },
1.1 nicm 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 },
1.26 nicm 94: { "message-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
1.22 nicm 95: { "mouse-select-pane", SET_OPTION_FLAG, 0, 0, NULL },
1.19 nicm 96: { "prefix", SET_OPTION_KEYS, 0, 0, NULL },
1.1 nicm 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 },
1.18 nicm 100: { "set-titles-string", SET_OPTION_STRING, 0, 0, NULL },
1.1 nicm 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 },
1.28 ! nicm 106: { "status-justify",
1.8 nicm 107: SET_OPTION_CHOICE, 0, 0, set_option_status_justify_list },
1.1 nicm 108: { "status-keys", SET_OPTION_CHOICE, 0, 0, set_option_status_keys_list },
109: { "status-left", SET_OPTION_STRING, 0, 0, NULL },
1.12 nicm 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 },
1.1 nicm 113: { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
114: { "status-right", SET_OPTION_STRING, 0, 0, NULL },
1.12 nicm 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 },
1.1 nicm 118: { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
1.28 ! nicm 119: { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL },
1.10 nicm 120: { "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL },
1.13 nicm 121: { "update-environment", SET_OPTION_STRING, 0, 0, NULL },
1.7 nicm 122: { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL },
1.28 ! nicm 123: { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL },
1.7 nicm 124: { "visual-content", SET_OPTION_FLAG, 0, 0, NULL },
1.6 nicm 125: { NULL, 0, 0, 0, NULL }
1.1 nicm 126: };
127:
1.27 nicm 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:
1.1 nicm 160: int
161: cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx)
162: {
1.14 nicm 163: struct cmd_target_data *data = self->data;
1.27 nicm 164: const struct set_option_entry *table;
1.1 nicm 165: struct session *s;
1.27 nicm 166: struct winlink *wl;
1.1 nicm 167: struct client *c;
168: struct options *oo;
1.6 nicm 169: const struct set_option_entry *entry, *opt;
1.24 nicm 170: struct jobs *jobs;
171: struct job *job, *nextjob;
1.1 nicm 172: u_int i;
1.24 nicm 173: int try_again;
1.1 nicm 174:
1.27 nicm 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: }
1.1 nicm 195: }
196:
1.14 nicm 197: if (*data->arg == '\0') {
1.1 nicm 198: ctx->error(ctx, "invalid option");
199: return (-1);
200: }
201:
202: entry = NULL;
1.27 nicm 203: for (opt = table; opt->name != NULL; opt++) {
1.14 nicm 204: if (strncmp(opt->name, data->arg, strlen(data->arg)) != 0)
1.1 nicm 205: continue;
206: if (entry != NULL) {
1.14 nicm 207: ctx->error(ctx, "ambiguous option: %s", data->arg);
1.1 nicm 208: return (-1);
209: }
1.6 nicm 210: entry = opt;
1.1 nicm 211:
212: /* Bail now if an exact match. */
1.14 nicm 213: if (strcmp(entry->name, data->arg) == 0)
1.1 nicm 214: break;
215: }
216: if (entry == NULL) {
1.14 nicm 217: ctx->error(ctx, "unknown option: %s", data->arg);
1.1 nicm 218: return (-1);
219: }
220:
1.25 nicm 221: if (cmd_check_flag(data->chflags, 'u')) {
222: if (cmd_check_flag(data->chflags, 'g')) {
1.1 nicm 223: ctx->error(ctx,
224: "can't unset global option: %s", entry->name);
225: return (-1);
226: }
1.14 nicm 227: if (data->arg2 != NULL) {
1.1 nicm 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:
1.27 nicm 238: cmd_set_option_string(ctx, oo, entry,
1.25 nicm 239: data->arg2, cmd_check_flag(data->chflags, 'a'));
1.1 nicm 240: break;
241: case SET_OPTION_NUMBER:
1.27 nicm 242: cmd_set_option_number(ctx, oo, entry, data->arg2);
1.1 nicm 243: break;
1.19 nicm 244: case SET_OPTION_KEYS:
1.27 nicm 245: cmd_set_option_keys(ctx, oo, entry, data->arg2);
1.1 nicm 246: break;
247: case SET_OPTION_COLOUR:
1.27 nicm 248: cmd_set_option_colour(ctx, oo, entry, data->arg2);
1.1 nicm 249: break;
250: case SET_OPTION_ATTRIBUTES:
1.27 nicm 251: cmd_set_option_attributes(ctx, oo, entry, data->arg2);
1.1 nicm 252: break;
253: case SET_OPTION_FLAG:
1.27 nicm 254: cmd_set_option_flag(ctx, oo, entry, data->arg2);
1.1 nicm 255: break;
256: case SET_OPTION_CHOICE:
1.27 nicm 257: cmd_set_option_choice(ctx, oo, entry, data->arg2);
1.1 nicm 258: break;
259: }
260: }
261:
262: recalculate_sizes();
1.27 nicm 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: }
1.24 nicm 268:
1.28 ! nicm 269: /*
1.24 nicm 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 ||
1.27 nicm 276: strcmp(entry->name, "set-titles-string") == 0 ||
277: strcmp(entry->name, "window-status-format") == 0) {
1.24 nicm 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 {
1.28 ! nicm 285: try_again = 0;
1.24 nicm 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);
1.1 nicm 297: server_redraw_client(c);
1.23 nicm 298: }
1.1 nicm 299: }
300:
301: return (0);
1.27 nicm 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;
1.28 ! nicm 369:
1.27 nicm 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));
1.1 nicm 542: }