4 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
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>
28 /* Command queue flags. */
29 #define CMDQ_FIRED 0x1
30 #define CMDQ_WAITING 0x2
32 /* Command queue item type. */
38 /* Command queue item. */
41 struct cmdq_list
*queue
;
42 struct cmdq_item
*next
;
44 struct client
*client
;
45 struct client
*target_client
;
55 struct cmdq_state
*state
;
56 struct cmd_find_state source
;
57 struct cmd_find_state target
;
59 struct cmd_list
*cmdlist
;
65 TAILQ_ENTRY(cmdq_item
) entry
;
67 TAILQ_HEAD(cmdq_item_list
, cmdq_item
);
70 * Command queue state. This is the context for commands on the command queue.
71 * It holds information about how the commands were fired (the key and flags),
72 * any additional formats for the commands, and the current default target.
73 * Multiple commands can share the same state and a command may update the
80 struct format_tree
*formats
;
82 struct key_event event
;
83 struct cmd_find_state current
;
88 struct cmdq_item
*item
;
89 struct cmdq_item_list list
;
92 /* Get command queue name. */
94 cmdq_name(struct client
*c
)
101 xsnprintf(s
, sizeof s
, "<%s>", c
->name
);
103 xsnprintf(s
, sizeof s
, "<%p>", c
);
107 /* Get command queue from client. */
108 static struct cmdq_list
*
109 cmdq_get(struct client
*c
)
111 static struct cmdq_list
*global_queue
;
114 if (global_queue
== NULL
)
115 global_queue
= cmdq_new();
116 return (global_queue
);
121 /* Create a queue. */
125 struct cmdq_list
*queue
;
127 queue
= xcalloc (1, sizeof *queue
);
128 TAILQ_INIT (&queue
->list
);
134 cmdq_free(struct cmdq_list
*queue
)
136 if (!TAILQ_EMPTY(&queue
->list
))
137 fatalx("queue not empty");
143 cmdq_get_name(struct cmdq_item
*item
)
148 /* Get item client. */
150 cmdq_get_client(struct cmdq_item
*item
)
152 return (item
->client
);
155 /* Get item target client. */
157 cmdq_get_target_client(struct cmdq_item
*item
)
159 return (item
->target_client
);
162 /* Get item state. */
164 cmdq_get_state(struct cmdq_item
*item
)
166 return (item
->state
);
169 /* Get item target. */
170 struct cmd_find_state
*
171 cmdq_get_target(struct cmdq_item
*item
)
173 return (&item
->target
);
176 /* Get item source. */
177 struct cmd_find_state
*
178 cmdq_get_source(struct cmdq_item
*item
)
180 return (&item
->source
);
183 /* Get state event. */
185 cmdq_get_event(struct cmdq_item
*item
)
187 return (&item
->state
->event
);
190 /* Get state current target. */
191 struct cmd_find_state
*
192 cmdq_get_current(struct cmdq_item
*item
)
194 return (&item
->state
->current
);
197 /* Get state flags. */
199 cmdq_get_flags(struct cmdq_item
*item
)
201 return (item
->state
->flags
);
204 /* Create a new state. */
206 cmdq_new_state(struct cmd_find_state
*current
, struct key_event
*event
,
209 struct cmdq_state
*state
;
211 state
= xcalloc(1, sizeof *state
);
212 state
->references
= 1;
213 state
->flags
= flags
;
216 memcpy(&state
->event
, event
, sizeof state
->event
);
218 state
->event
.key
= KEYC_NONE
;
219 if (current
!= NULL
&& cmd_find_valid_state(current
))
220 cmd_find_copy_state(&state
->current
, current
);
222 cmd_find_clear_state(&state
->current
, 0);
227 /* Add a reference to a state. */
229 cmdq_link_state(struct cmdq_state
*state
)
235 /* Make a copy of a state. */
237 cmdq_copy_state(struct cmdq_state
*state
)
239 return (cmdq_new_state(&state
->current
, &state
->event
, state
->flags
));
244 cmdq_free_state(struct cmdq_state
*state
)
246 if (--state
->references
!= 0)
249 if (state
->formats
!= NULL
)
250 format_free(state
->formats
);
254 /* Add a format to command queue. */
256 cmdq_add_format(struct cmdq_state
*state
, const char *key
, const char *fmt
, ...)
262 xvasprintf(&value
, fmt
, ap
);
265 if (state
->formats
== NULL
)
266 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
267 format_add(state
->formats
, key
, "%s", value
);
272 /* Add formats to command queue. */
274 cmdq_add_formats(struct cmdq_state
*state
, struct format_tree
*ft
)
276 if (state
->formats
== NULL
)
277 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
278 format_merge(state
->formats
, ft
);
281 /* Merge formats from item. */
283 cmdq_merge_formats(struct cmdq_item
*item
, struct format_tree
*ft
)
285 const struct cmd_entry
*entry
;
287 if (item
->cmd
!= NULL
) {
288 entry
= cmd_get_entry(item
->cmd
);
289 format_add(ft
, "command", "%s", entry
->name
);
291 if (item
->state
->formats
!= NULL
)
292 format_merge(ft
, item
->state
->formats
);
295 /* Append an item. */
297 cmdq_append(struct client
*c
, struct cmdq_item
*item
)
299 struct cmdq_list
*queue
= cmdq_get(c
);
300 struct cmdq_item
*next
;
311 TAILQ_INSERT_TAIL(&queue
->list
, item
, entry
);
312 log_debug("%s %s: %s", __func__
, cmdq_name(c
), item
->name
);
315 } while (item
!= NULL
);
316 return (TAILQ_LAST(&queue
->list
, cmdq_item_list
));
319 /* Insert an item. */
321 cmdq_insert_after(struct cmdq_item
*after
, struct cmdq_item
*item
)
323 struct client
*c
= after
->client
;
324 struct cmdq_list
*queue
= after
->queue
;
325 struct cmdq_item
*next
;
329 item
->next
= after
->next
;
337 TAILQ_INSERT_AFTER(&queue
->list
, after
, item
, entry
);
338 log_debug("%s %s: %s after %s", __func__
, cmdq_name(c
),
339 item
->name
, after
->name
);
343 } while (item
!= NULL
);
349 cmdq_insert_hook(struct session
*s
, struct cmdq_item
*item
,
350 struct cmd_find_state
*current
, const char *fmt
, ...)
352 struct cmdq_state
*state
= item
->state
;
353 struct cmd
*cmd
= item
->cmd
;
354 struct args
*args
= cmd_get_args(cmd
);
355 struct args_entry
*ae
;
356 struct args_value
*av
;
359 char *name
, tmp
[32], flag
, *arguments
;
362 struct cmdq_item
*new_item
;
363 struct cmdq_state
*new_state
;
364 struct options_entry
*o
;
365 struct options_array_item
*a
;
366 struct cmd_list
*cmdlist
;
368 if (item
->state
->flags
& CMDQ_STATE_NOHOOKS
)
371 oo
= global_s_options
;
376 xvasprintf(&name
, fmt
, ap
);
379 o
= options_get(oo
, name
);
384 log_debug("running hook %s (parent %p)", name
, item
);
387 * The hooks get a new state because they should not update the current
388 * target or formats for any subsequent commands.
390 new_state
= cmdq_new_state(current
, &state
->event
, CMDQ_STATE_NOHOOKS
);
391 cmdq_add_format(new_state
, "hook", "%s", name
);
393 arguments
= args_print(args
);
394 cmdq_add_format(new_state
, "hook_arguments", "%s", arguments
);
397 for (i
= 0; i
< args_count(args
); i
++) {
398 xsnprintf(tmp
, sizeof tmp
, "hook_argument_%d", i
);
399 cmdq_add_format(new_state
, tmp
, "%s", args_string(args
, i
));
401 flag
= args_first(args
, &ae
);
403 value
= args_get(args
, flag
);
405 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
406 cmdq_add_format(new_state
, tmp
, "1");
408 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
409 cmdq_add_format(new_state
, tmp
, "%s", value
);
413 av
= args_first_value(args
, flag
);
415 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c_%d", flag
, i
);
416 cmdq_add_format(new_state
, tmp
, "%s", av
->string
);
418 av
= args_next_value(av
);
421 flag
= args_next(&ae
);
424 a
= options_array_first(o
);
426 cmdlist
= options_array_item_value(a
)->cmdlist
;
427 if (cmdlist
!= NULL
) {
428 new_item
= cmdq_get_command(cmdlist
, new_state
);
430 item
= cmdq_insert_after(item
, new_item
);
432 item
= cmdq_append(NULL
, new_item
);
434 a
= options_array_next(a
);
437 cmdq_free_state(new_state
);
441 /* Continue processing command queue. */
443 cmdq_continue(struct cmdq_item
*item
)
445 item
->flags
&= ~CMDQ_WAITING
;
448 /* Remove an item. */
450 cmdq_remove(struct cmdq_item
*item
)
452 if (item
->client
!= NULL
)
453 server_client_unref(item
->client
);
454 if (item
->cmdlist
!= NULL
)
455 cmd_list_free(item
->cmdlist
);
456 cmdq_free_state(item
->state
);
458 TAILQ_REMOVE(&item
->queue
->list
, item
, entry
);
464 /* Remove all subsequent items that match this item's group. */
466 cmdq_remove_group(struct cmdq_item
*item
)
468 struct cmdq_item
*this, *next
;
470 if (item
->group
== 0)
472 this = TAILQ_NEXT(item
, entry
);
473 while (this != NULL
) {
474 next
= TAILQ_NEXT(this, entry
);
475 if (this->group
== item
->group
)
481 /* Empty command callback. */
482 static enum cmd_retval
483 cmdq_empty_command(__unused
struct cmdq_item
*item
, __unused
void *data
)
485 return (CMD_RETURN_NORMAL
);
488 /* Get a command for the command queue. */
490 cmdq_get_command(struct cmd_list
*cmdlist
, struct cmdq_state
*state
)
492 struct cmdq_item
*item
, *first
= NULL
, *last
= NULL
;
494 const struct cmd_entry
*entry
;
497 if ((cmd
= cmd_list_first(cmdlist
)) == NULL
)
498 return (cmdq_get_callback(cmdq_empty_command
, NULL
));
501 state
= cmdq_new_state(NULL
, NULL
, 0);
505 while (cmd
!= NULL
) {
506 entry
= cmd_get_entry(cmd
);
508 item
= xcalloc(1, sizeof *item
);
509 xasprintf(&item
->name
, "[%s/%p]", entry
->name
, item
);
510 item
->type
= CMDQ_COMMAND
;
512 item
->group
= cmd_get_group(cmd
);
513 item
->state
= cmdq_link_state(state
);
515 item
->cmdlist
= cmdlist
;
518 cmdlist
->references
++;
519 log_debug("%s: %s group %u", __func__
, item
->name
, item
->group
);
527 cmd
= cmd_list_next(cmd
);
531 cmdq_free_state(state
);
535 /* Fill in flag for a command. */
536 static enum cmd_retval
537 cmdq_find_flag(struct cmdq_item
*item
, struct cmd_find_state
*fs
,
538 const struct cmd_entry_flag
*flag
)
542 if (flag
->flag
== 0) {
543 cmd_find_from_client(fs
, item
->target_client
, 0);
544 return (CMD_RETURN_NORMAL
);
547 value
= args_get(cmd_get_args(item
->cmd
), flag
->flag
);
548 if (cmd_find_target(fs
, item
, value
, flag
->type
, flag
->flags
) != 0) {
549 cmd_find_clear_state(fs
, 0);
550 return (CMD_RETURN_ERROR
);
552 return (CMD_RETURN_NORMAL
);
555 /* Add message with command. */
557 cmdq_add_message(struct cmdq_item
*item
)
559 struct client
*c
= item
->client
;
560 struct cmdq_state
*state
= item
->state
;
561 const char *name
, *key
;
564 tmp
= cmd_print(item
->cmd
);
567 if (c
->session
!= NULL
&& state
->event
.key
!= KEYC_NONE
) {
568 key
= key_string_lookup_key(state
->event
.key
, 0);
569 server_add_message("%s key %s: %s", name
, key
, tmp
);
571 server_add_message("%s command: %s", name
, tmp
);
573 server_add_message("command: %s", tmp
);
577 /* Fire command on command queue. */
578 static enum cmd_retval
579 cmdq_fire_command(struct cmdq_item
*item
)
581 const char *name
= cmdq_name(item
->client
);
582 struct cmdq_state
*state
= item
->state
;
583 struct cmd
*cmd
= item
->cmd
;
584 struct args
*args
= cmd_get_args(cmd
);
585 const struct cmd_entry
*entry
= cmd_get_entry(cmd
);
586 struct client
*tc
, *saved
= item
->client
;
587 enum cmd_retval retval
;
588 struct cmd_find_state
*fsp
, fs
;
589 int flags
, quiet
= 0;
593 cmdq_add_message(item
);
594 if (log_get_level() > 1) {
595 tmp
= cmd_print(cmd
);
596 log_debug("%s %s: (%u) %s", __func__
, name
, item
->group
, tmp
);
600 flags
= !!(state
->flags
& CMDQ_STATE_CONTROL
);
601 cmdq_guard(item
, "begin", flags
);
603 if (item
->client
== NULL
)
604 item
->client
= cmd_find_client(item
, NULL
, 1);
606 if (entry
->flags
& CMD_CLIENT_CANFAIL
)
608 if (entry
->flags
& CMD_CLIENT_CFLAG
) {
609 tc
= cmd_find_client(item
, args_get(args
, 'c'), quiet
);
610 if (tc
== NULL
&& !quiet
) {
611 retval
= CMD_RETURN_ERROR
;
614 } else if (entry
->flags
& CMD_CLIENT_TFLAG
) {
615 tc
= cmd_find_client(item
, args_get(args
, 't'), quiet
);
616 if (tc
== NULL
&& !quiet
) {
617 retval
= CMD_RETURN_ERROR
;
621 tc
= cmd_find_client(item
, NULL
, 1);
622 item
->target_client
= tc
;
624 retval
= cmdq_find_flag(item
, &item
->source
, &entry
->source
);
625 if (retval
== CMD_RETURN_ERROR
)
627 retval
= cmdq_find_flag(item
, &item
->target
, &entry
->target
);
628 if (retval
== CMD_RETURN_ERROR
)
631 retval
= entry
->exec(cmd
, item
);
632 if (retval
== CMD_RETURN_ERROR
)
635 if (entry
->flags
& CMD_AFTERHOOK
) {
636 if (cmd_find_valid_state(&item
->target
))
638 else if (cmd_find_valid_state(&item
->state
->current
))
639 fsp
= &item
->state
->current
;
640 else if (cmd_find_from_client(&fs
, item
->client
, 0) == 0)
644 cmdq_insert_hook(fsp
->s
, item
, fsp
, "after-%s", entry
->name
);
648 item
->client
= saved
;
649 if (retval
== CMD_RETURN_ERROR
)
650 cmdq_guard(item
, "error", flags
);
652 cmdq_guard(item
, "end", flags
);
656 /* Get a callback for the command queue. */
658 cmdq_get_callback1(const char *name
, cmdq_cb cb
, void *data
)
660 struct cmdq_item
*item
;
662 item
= xcalloc(1, sizeof *item
);
663 xasprintf(&item
->name
, "[%s/%p]", name
, item
);
664 item
->type
= CMDQ_CALLBACK
;
667 item
->state
= cmdq_new_state(NULL
, NULL
, 0);
675 /* Generic error callback. */
676 static enum cmd_retval
677 cmdq_error_callback(struct cmdq_item
*item
, void *data
)
681 cmdq_error(item
, "%s", error
);
684 return (CMD_RETURN_NORMAL
);
687 /* Get an error callback for the command queue. */
689 cmdq_get_error(const char *error
)
691 return (cmdq_get_callback(cmdq_error_callback
, xstrdup(error
)));
694 /* Fire callback on callback queue. */
695 static enum cmd_retval
696 cmdq_fire_callback(struct cmdq_item
*item
)
698 return (item
->cb(item
, item
->data
));
701 /* Process next item on command queue. */
703 cmdq_next(struct client
*c
)
705 struct cmdq_list
*queue
= cmdq_get(c
);
706 const char *name
= cmdq_name(c
);
707 struct cmdq_item
*item
;
708 enum cmd_retval retval
;
712 if (TAILQ_EMPTY(&queue
->list
)) {
713 log_debug("%s %s: empty", __func__
, name
);
716 if (TAILQ_FIRST(&queue
->list
)->flags
& CMDQ_WAITING
) {
717 log_debug("%s %s: waiting", __func__
, name
);
721 log_debug("%s %s: enter", __func__
, name
);
723 item
= queue
->item
= TAILQ_FIRST(&queue
->list
);
726 log_debug("%s %s: %s (%d), flags %x", __func__
, name
,
727 item
->name
, item
->type
, item
->flags
);
730 * Any item with the waiting flag set waits until an external
731 * event clears the flag (for example, a job - look at
734 if (item
->flags
& CMDQ_WAITING
)
738 * Items are only fired once, once the fired flag is set, a
739 * waiting flag can only be cleared by an external event.
741 if (~item
->flags
& CMDQ_FIRED
) {
742 item
->time
= time(NULL
);
743 item
->number
= ++number
;
745 switch (item
->type
) {
747 retval
= cmdq_fire_command(item
);
750 * If a command returns an error, remove any
751 * subsequent commands in the same group.
753 if (retval
== CMD_RETURN_ERROR
)
754 cmdq_remove_group(item
);
757 retval
= cmdq_fire_callback(item
);
760 retval
= CMD_RETURN_ERROR
;
763 item
->flags
|= CMDQ_FIRED
;
765 if (retval
== CMD_RETURN_WAIT
) {
766 item
->flags
|= CMDQ_WAITING
;
775 log_debug("%s %s: exit (empty)", __func__
, name
);
779 log_debug("%s %s: exit (wait)", __func__
, name
);
783 /* Get running item if any. */
785 cmdq_running(struct client
*c
)
787 struct cmdq_list
*queue
= cmdq_get(c
);
789 if (queue
->item
== NULL
)
791 if (queue
->item
->flags
& CMDQ_WAITING
)
793 return (queue
->item
);
796 /* Print a guard line. */
798 cmdq_guard(struct cmdq_item
*item
, const char *guard
, int flags
)
800 struct client
*c
= item
->client
;
802 u_int number
= item
->number
;
804 if (c
!= NULL
&& (c
->flags
& CLIENT_CONTROL
))
805 control_write(c
, "%%%s %ld %u %d", guard
, t
, number
, flags
);
808 /* Show message from command. */
810 cmdq_print(struct cmdq_item
*item
, const char *fmt
, ...)
812 struct client
*c
= item
->client
;
813 struct window_pane
*wp
;
814 struct window_mode_entry
*wme
;
819 xvasprintf(&msg
, fmt
, ap
);
822 log_debug("%s: %s", __func__
, msg
);
826 else if (c
->session
== NULL
|| (c
->flags
& CLIENT_CONTROL
)) {
827 if (~c
->flags
& CLIENT_UTF8
) {
829 msg
= utf8_sanitize(tmp
);
832 if (c
->flags
& CLIENT_CONTROL
)
833 control_write(c
, "%s", msg
);
835 file_print(c
, "%s\n", msg
);
837 wp
= server_client_get_pane(c
);
838 wme
= TAILQ_FIRST(&wp
->modes
);
839 if (wme
== NULL
|| wme
->mode
!= &window_view_mode
) {
840 window_pane_set_mode(wp
, NULL
, &window_view_mode
, NULL
,
843 window_copy_add(wp
, "%s", msg
);
849 /* Show error from command. */
851 cmdq_error(struct cmdq_item
*item
, const char *fmt
, ...)
853 struct client
*c
= item
->client
;
854 struct cmd
*cmd
= item
->cmd
;
861 xvasprintf(&msg
, fmt
, ap
);
864 log_debug("%s: %s", __func__
, msg
);
867 cmd_get_source(cmd
, &file
, &line
);
868 cfg_add_cause("%s:%u: %s", file
, line
, msg
);
869 } else if (c
->session
== NULL
|| (c
->flags
& CLIENT_CONTROL
)) {
870 server_add_message("%s message: %s", c
->name
, msg
);
871 if (~c
->flags
& CLIENT_UTF8
) {
873 msg
= utf8_sanitize(tmp
);
876 if (c
->flags
& CLIENT_CONTROL
)
877 control_write(c
, "%s", msg
);
879 file_error(c
, "%s\n", msg
);
882 *msg
= toupper((u_char
) *msg
);
883 status_message_set(c
, -1, 1, 0, "%s", msg
);