1 /* $OpenBSD$ */
2
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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 enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmd_q *);
31
32 enum cmd_retval cmd_set_option_user(struct cmd *, struct cmd_q *,
33 const char *, const char *);
34
35 int cmd_set_option_unset(struct cmd *, struct cmd_q *,
36 const struct options_table_entry *, struct options *,
37 const char *);
38 int cmd_set_option_set(struct cmd *, struct cmd_q *,
39 const struct options_table_entry *, struct options *,
40 const char *);
41
42 struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_q *,
43 const struct options_table_entry *, struct options *,
44 const char *);
45 struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_q *,
46 const struct options_table_entry *, struct options *,
47 const char *);
48 struct options_entry *cmd_set_option_key(struct cmd *, struct cmd_q *,
49 const struct options_table_entry *, struct options *,
50 const char *);
51 struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_q *,
52 const struct options_table_entry *, struct options *,
53 const char *);
54 struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_q *,
55 const struct options_table_entry *, struct options *,
56 const char *);
57 struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *,
58 const struct options_table_entry *, struct options *,
59 const char *);
60 struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *,
61 const struct options_table_entry *, struct options *,
62 const char *);
63 struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *,
64 const struct options_table_entry *, struct options *,
65 const char *);
66
67 const struct cmd_entry cmd_set_option_entry = {
68 .name = "set-option",
69 .alias = "set",
70
71 .args = { "agoqst:uw", 1, 2 },
72 .usage = "[-agosquw] [-t target-window] option [value]",
73
74 .tflag = CMD_WINDOW_CANFAIL,
75
76 .flags = 0,
77 .exec = cmd_set_option_exec
78 };
79
80 const struct cmd_entry cmd_set_window_option_entry = {
81 .name = "set-window-option",
82 .alias = "setw",
83
84 .args = { "agoqt:u", 1, 2 },
85 .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]",
86
87 .tflag = CMD_WINDOW_CANFAIL,
88
89 .flags = 0,
90 .exec = cmd_set_option_exec
91 };
92
93 enum cmd_retval
cmd_set_option_exec(struct cmd * self,struct cmd_q * cmdq)94 cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
95 {
96 struct args *args = self->args;
97 struct session *s = cmdq->state.tflag.s;
98 struct winlink *wl = cmdq->state.tflag.wl;
99 struct window *w;
100 struct client *c;
101 const struct options_table_entry *oe;
102 struct options *oo;
103 const char *optstr, *valstr, *target;
104
105 /* Get the option name and value. */
106 optstr = args->argv[0];
107 if (*optstr == '\0') {
108 cmdq_error(cmdq, "invalid option");
109 return (CMD_RETURN_ERROR);
110 }
111 if (args->argc < 2)
112 valstr = NULL;
113 else
114 valstr = args->argv[1];
115
116 /* Is this a user option? */
117 if (*optstr == '@')
118 return (cmd_set_option_user(self, cmdq, optstr, valstr));
119
120 /* Find the option entry, try each table. */
121 oe = NULL;
122 if (options_table_find(optstr, &oe) != 0) {
123 if (!args_has(args, 'q')) {
124 cmdq_error(cmdq, "ambiguous option: %s", optstr);
125 return (CMD_RETURN_ERROR);
126 }
127 return (CMD_RETURN_NORMAL);
128 }
129 if (oe == NULL) {
130 if (!args_has(args, 'q')) {
131 cmdq_error(cmdq, "unknown option: %s", optstr);
132 return (CMD_RETURN_ERROR);
133 }
134 return (CMD_RETURN_NORMAL);
135 }
136
137 /* Work out the tree from the scope of the option. */
138 if (oe->scope == OPTIONS_TABLE_SERVER)
139 oo = global_options;
140 else if (oe->scope == OPTIONS_TABLE_WINDOW) {
141 if (args_has(self->args, 'g'))
142 oo = global_w_options;
143 else if (wl == NULL) {
144 target = args_get(args, 't');
145 if (target != NULL) {
146 cmdq_error(cmdq, "no such window: %s",
147 target);
148 } else
149 cmdq_error(cmdq, "no current window");
150 return (CMD_RETURN_ERROR);
151 } else
152 oo = wl->window->options;
153 } else if (oe->scope == OPTIONS_TABLE_SESSION) {
154 if (args_has(self->args, 'g'))
155 oo = global_s_options;
156 else if (s == NULL) {
157 target = args_get(args, 't');
158 if (target != NULL) {
159 cmdq_error(cmdq, "no such session: %s",
160 target);
161 } else
162 cmdq_error(cmdq, "no current session");
163 return (CMD_RETURN_ERROR);
164 } else
165 oo = s->options;
166 } else {
167 cmdq_error(cmdq, "unknown table");
168 return (CMD_RETURN_ERROR);
169 }
170
171 /* Unset or set the option. */
172 if (args_has(args, 'u')) {
173 if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0)
174 return (CMD_RETURN_ERROR);
175 } else {
176 if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) {
177 if (!args_has(args, 'q')) {
178 cmdq_error(cmdq, "already set: %s", optstr);
179 return (CMD_RETURN_ERROR);
180 }
181 return (CMD_RETURN_NORMAL);
182 }
183 if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0)
184 return (CMD_RETURN_ERROR);
185 }
186
187 /* Start or stop timers if necessary. */
188 if (strcmp(oe->name, "automatic-rename") == 0) {
189 RB_FOREACH(w, windows, &windows) {
190 if (options_get_number(w->options, "automatic-rename"))
191 w->active->flags |= PANE_CHANGED;
192 }
193 }
194 if (strcmp(oe->name, "key-table") == 0) {
195 TAILQ_FOREACH(c, &clients, entry)
196 server_client_set_key_table(c, NULL);
197 }
198 if (strcmp(oe->name, "status") == 0 ||
199 strcmp(oe->name, "status-interval") == 0)
200 status_timer_start_all();
201 if (strcmp(oe->name, "monitor-silence") == 0)
202 alerts_reset_all();
203 if (strcmp(oe->name, "window-style") == 0 ||
204 strcmp(oe->name, "window-active-style") == 0) {
205 RB_FOREACH(w, windows, &windows)
206 w->flags |= WINDOW_STYLECHANGED;
207 }
208
209 /* When the pane-border-status option has been changed, resize panes. */
210 if (strcmp(oe->name, "pane-border-status") == 0) {
211 RB_FOREACH(w, windows, &windows)
212 layout_fix_panes(w, w->sx, w->sy);
213 }
214
215 /* Update sizes and redraw. May not need it but meh. */
216 recalculate_sizes();
217 TAILQ_FOREACH(c, &clients, entry) {
218 if (c->session != NULL)
219 server_redraw_client(c);
220 }
221
222 return (CMD_RETURN_NORMAL);
223 }
224
225 /* Set user option. */
226 enum cmd_retval
cmd_set_option_user(struct cmd * self,struct cmd_q * cmdq,const char * optstr,const char * valstr)227 cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr,
228 const char *valstr)
229 {
230 struct args *args = self->args;
231 struct session *s = cmdq->state.tflag.s;
232 struct winlink *wl = cmdq->state.tflag.wl;
233 struct options *oo;
234 struct options_entry *o;
235
236 if (args_has(args, 's'))
237 oo = global_options;
238 else if (args_has(self->args, 'w') ||
239 self->entry == &cmd_set_window_option_entry) {
240 if (args_has(self->args, 'g'))
241 oo = global_w_options;
242 else
243 oo = wl->window->options;
244 } else {
245 if (args_has(self->args, 'g'))
246 oo = global_s_options;
247 else
248 oo = s->options;
249 }
250
251 if (args_has(args, 'u')) {
252 if (options_find1(oo, optstr) == NULL) {
253 if (!args_has(args, 'q')) {
254 cmdq_error(cmdq, "unknown option: %s", optstr);
255 return (CMD_RETURN_ERROR);
256 }
257 return (CMD_RETURN_NORMAL);
258 }
259 if (valstr != NULL) {
260 cmdq_error(cmdq, "value passed to unset option: %s",
261 optstr);
262 return (CMD_RETURN_ERROR);
263 }
264 options_remove(oo, optstr);
265 } else {
266 o = options_find1(oo, optstr);
267 if (args_has(args, 'o') && o != NULL) {
268 if (!args_has(args, 'q')) {
269 cmdq_error(cmdq, "already set: %s", optstr);
270 return (CMD_RETURN_ERROR);
271 }
272 return (CMD_RETURN_NORMAL);
273 }
274 if (valstr == NULL) {
275 cmdq_error(cmdq, "empty value");
276 return (CMD_RETURN_ERROR);
277 }
278 if (o != NULL && args_has(args, 'a'))
279 options_set_string(oo, optstr, "%s%s", o->str, valstr);
280 else
281 options_set_string(oo, optstr, "%s", valstr);
282 }
283 return (CMD_RETURN_NORMAL);
284 }
285
286 /* Unset an option. */
287 int
cmd_set_option_unset(struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)288 cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq,
289 const struct options_table_entry *oe, struct options *oo,
290 const char *value)
291 {
292 struct args *args = self->args;
293
294 if (value != NULL) {
295 cmdq_error(cmdq, "value passed to unset option: %s", oe->name);
296 return (-1);
297 }
298
299 if (args_has(args, 'g') || oo == global_options) {
300 switch (oe->type) {
301 case OPTIONS_TABLE_STRING:
302 options_set_string(oo, oe->name, "%s", oe->default_str);
303 break;
304 case OPTIONS_TABLE_STYLE:
305 options_set_style(oo, oe->name, oe->default_str, 0);
306 break;
307 default:
308 options_set_number(oo, oe->name, oe->default_num);
309 break;
310 }
311 } else
312 options_remove(oo, oe->name);
313 return (0);
314 }
315
316 /* Set an option. */
317 int
cmd_set_option_set(struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)318 cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq,
319 const struct options_table_entry *oe, struct options *oo,
320 const char *value)
321 {
322 struct options_entry *o;
323
324 switch (oe->type) {
325 case OPTIONS_TABLE_FLAG:
326 case OPTIONS_TABLE_CHOICE:
327 break;
328 default:
329 if (value == NULL) {
330 cmdq_error(cmdq, "empty value");
331 return (-1);
332 }
333 }
334
335 o = NULL;
336 switch (oe->type) {
337 case OPTIONS_TABLE_STRING:
338 o = cmd_set_option_string(self, cmdq, oe, oo, value);
339 break;
340 case OPTIONS_TABLE_NUMBER:
341 o = cmd_set_option_number(self, cmdq, oe, oo, value);
342 break;
343 case OPTIONS_TABLE_KEY:
344 o = cmd_set_option_key(self, cmdq, oe, oo, value);
345 break;
346 case OPTIONS_TABLE_COLOUR:
347 o = cmd_set_option_colour(self, cmdq, oe, oo, value);
348 if (o != NULL)
349 style_update_new(oo, o->name, oe->style);
350 break;
351 case OPTIONS_TABLE_ATTRIBUTES:
352 o = cmd_set_option_attributes(self, cmdq, oe, oo, value);
353 if (o != NULL)
354 style_update_new(oo, o->name, oe->style);
355 break;
356 case OPTIONS_TABLE_FLAG:
357 o = cmd_set_option_flag(self, cmdq, oe, oo, value);
358 break;
359 case OPTIONS_TABLE_CHOICE:
360 o = cmd_set_option_choice(self, cmdq, oe, oo, value);
361 break;
362 case OPTIONS_TABLE_STYLE:
363 o = cmd_set_option_style(self, cmdq, oe, oo, value);
364 break;
365 }
366 if (o == NULL)
367 return (-1);
368 return (0);
369 }
370
371 /* Set a string option. */
372 struct options_entry *
cmd_set_option_string(struct cmd * self,__unused struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)373 cmd_set_option_string(struct cmd *self, __unused struct cmd_q *cmdq,
374 const struct options_table_entry *oe, struct options *oo,
375 const char *value)
376 {
377 struct args *args = self->args;
378 struct options_entry *o;
379 char *oldval, *newval;
380
381 if (args_has(args, 'a')) {
382 oldval = options_get_string(oo, oe->name);
383 xasprintf(&newval, "%s%s", oldval, value);
384 } else
385 newval = xstrdup(value);
386
387 o = options_set_string(oo, oe->name, "%s", newval);
388
389 free(newval);
390 return (o);
391 }
392
393 /* Set a number option. */
394 struct options_entry *
cmd_set_option_number(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)395 cmd_set_option_number(__unused struct cmd *self, struct cmd_q *cmdq,
396 const struct options_table_entry *oe, struct options *oo,
397 const char *value)
398 {
399 long long ll;
400 const char *errstr;
401
402 ll = strtonum(value, oe->minimum, oe->maximum, &errstr);
403 if (errstr != NULL) {
404 cmdq_error(cmdq, "value is %s: %s", errstr, value);
405 return (NULL);
406 }
407
408 return (options_set_number(oo, oe->name, ll));
409 }
410
411 /* Set a key option. */
412 struct options_entry *
cmd_set_option_key(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)413 cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq,
414 const struct options_table_entry *oe, struct options *oo,
415 const char *value)
416 {
417 key_code key;
418
419 key = key_string_lookup_string(value);
420 if (key == KEYC_UNKNOWN) {
421 cmdq_error(cmdq, "bad key: %s", value);
422 return (NULL);
423 }
424
425 return (options_set_number(oo, oe->name, key));
426 }
427
428 /* Set a colour option. */
429 struct options_entry *
cmd_set_option_colour(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)430 cmd_set_option_colour(__unused struct cmd *self, struct cmd_q *cmdq,
431 const struct options_table_entry *oe, struct options *oo,
432 const char *value)
433 {
434 int colour;
435
436 if ((colour = colour_fromstring(value)) == -1) {
437 cmdq_error(cmdq, "bad colour: %s", value);
438 return (NULL);
439 }
440
441 return (options_set_number(oo, oe->name, colour));
442 }
443
444 /* Set an attributes option. */
445 struct options_entry *
cmd_set_option_attributes(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)446 cmd_set_option_attributes(__unused struct cmd *self, struct cmd_q *cmdq,
447 const struct options_table_entry *oe, struct options *oo,
448 const char *value)
449 {
450 int attr;
451
452 if ((attr = attributes_fromstring(value)) == -1) {
453 cmdq_error(cmdq, "bad attributes: %s", value);
454 return (NULL);
455 }
456
457 return (options_set_number(oo, oe->name, attr));
458 }
459
460 /* Set a flag option. */
461 struct options_entry *
cmd_set_option_flag(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)462 cmd_set_option_flag(__unused struct cmd *self, struct cmd_q *cmdq,
463 const struct options_table_entry *oe, struct options *oo,
464 const char *value)
465 {
466 int flag;
467
468 if (value == NULL || *value == '\0')
469 flag = !options_get_number(oo, oe->name);
470 else {
471 if ((value[0] == '1' && value[1] == '\0') ||
472 strcasecmp(value, "on") == 0 ||
473 strcasecmp(value, "yes") == 0)
474 flag = 1;
475 else if ((value[0] == '0' && value[1] == '\0') ||
476 strcasecmp(value, "off") == 0 ||
477 strcasecmp(value, "no") == 0)
478 flag = 0;
479 else {
480 cmdq_error(cmdq, "bad value: %s", value);
481 return (NULL);
482 }
483 }
484
485 return (options_set_number(oo, oe->name, flag));
486 }
487
488 /* Set a choice option. */
489 struct options_entry *
cmd_set_option_choice(__unused struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)490 cmd_set_option_choice(__unused struct cmd *self, struct cmd_q *cmdq,
491 const struct options_table_entry *oe, struct options *oo,
492 const char *value)
493 {
494 const char **choicep;
495 int n, choice = -1;
496
497 if (value == NULL) {
498 choice = options_get_number(oo, oe->name);
499 if (choice < 2)
500 choice = !choice;
501 } else {
502 n = 0;
503 for (choicep = oe->choices; *choicep != NULL; choicep++) {
504 n++;
505 if (strncmp(*choicep, value, strlen(value)) != 0)
506 continue;
507
508 if (choice != -1) {
509 cmdq_error(cmdq, "ambiguous value: %s", value);
510 return (NULL);
511 }
512 choice = n - 1;
513 }
514 if (choice == -1) {
515 cmdq_error(cmdq, "unknown value: %s", value);
516 return (NULL);
517 }
518 }
519
520 return (options_set_number(oo, oe->name, choice));
521 }
522
523 /* Set a style option. */
524 struct options_entry *
cmd_set_option_style(struct cmd * self,struct cmd_q * cmdq,const struct options_table_entry * oe,struct options * oo,const char * value)525 cmd_set_option_style(struct cmd *self, struct cmd_q *cmdq,
526 const struct options_table_entry *oe, struct options *oo,
527 const char *value)
528 {
529 struct args *args = self->args;
530 struct options_entry *o;
531 int append;
532
533 append = args_has(args, 'a');
534 if ((o = options_set_style(oo, oe->name, value, append)) == NULL) {
535 cmdq_error(cmdq, "bad style: %s", value);
536 return (NULL);
537 }
538
539 style_update_old(oo, oe->name, &o->style);
540 return (o);
541 }
542