From 748acdc77ca11a09e637324946a6a4f189defc8e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 25 Mar 2013 10:09:35 +0000 Subject: [PATCH] Add wait-for -L and -U for lock and unlock, from Thiago Padilha. --- cmd-wait-for.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 35 ++++++++- 2 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 cmd-wait-for.c diff --git a/cmd-wait-for.c b/cmd-wait-for.c new file mode 100644 index 00000000..3a8d8ea4 --- /dev/null +++ b/cmd-wait-for.c @@ -0,0 +1,197 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Thiago de Arruda + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Block or wake a client on a named wait channel. + */ + +enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_wait_for_entry = { + "wait-for", "wait", + "LSU", 1, 1, + "[-LSU] channel", + 0, + NULL, + NULL, + cmd_wait_for_exec +}; + +struct wait_channel { + const char *name; + int locked; + + TAILQ_HEAD(, cmd_q) waiters; + TAILQ_HEAD(, cmd_q) lockers; + + RB_ENTRY(wait_channel) entry; +}; +RB_HEAD(wait_channels, wait_channel); +struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); + +int wait_channel_cmp(struct wait_channel *, struct wait_channel *); +RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); +RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); + +int +wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) +{ + return (strcmp(wc1->name, wc2->name)); +} + +enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, + struct wait_channel *); + +enum cmd_retval +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + const char *name = args->argv[0]; + struct wait_channel *wc, wc0; + + wc0.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &wc0); + + if (args_has(args, 'S')) + return (cmd_wait_for_signal(cmdq, name, wc)); + if (args_has(args, 'L')) + return (cmd_wait_for_lock(cmdq, name, wc)); + if (args_has(args, 'U')) + return (cmd_wait_for_unlock(cmdq, name, wc)); + return (cmd_wait_for_wait(cmdq, name, wc)); +} + +enum cmd_retval +cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq, *wq1; + + if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { + cmdq_error(cmdq, "no waiting clients on %s", name); + return (CMD_RETURN_ERROR); + } + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to wait"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + } + + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); + cmdq->references++; + + return (CMD_RETURN_WAIT); +} + +enum cmd_retval +cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to lock"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + } + + if (wc->locked) { + TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); + cmdq->references++; + return (CMD_RETURN_WAIT); + } + wc->locked = 1; + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq; + + if (wc == NULL || !wc->locked) { + cmdq_error(cmdq, "channel %s not locked", name); + return (CMD_RETURN_ERROR); + } + + if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + TAILQ_REMOVE(&wc->lockers, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } else { + wc->locked = 0; + if (TAILQ_EMPTY(&wc->waiters)) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } + } + + return (CMD_RETURN_NORMAL); +} + diff --git a/tmux.1 b/tmux.1 index b0d6b4e7..f6055f11 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1062,8 +1062,7 @@ By default, it uses the format but a different format may be specified with .Fl F . .It Xo Ic capture-pane -.Op Fl e -.Op Fl p +.Op Fl aepq .Op Fl b Ar buffer-index .Op Fl E Ar end-line .Op Fl S Ar start-line @@ -1077,13 +1076,19 @@ is given, the output goes to stdout, otherwise to the buffer specified with .Fl b or a new buffer if omitted. If +.Fl a +is given, the alternate screen is used, and the history is not accessible. +If no alternate screen exists, an error will be returned unless +.Fl q +is given. +If .Fl e is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. .Fl J -joins wrapped lines. +joins wrapped lines and preserves trailing spaces at each line's end. .Pp .Fl S and @@ -2946,7 +2951,7 @@ If this option is set, searches will wrap around the end of the pane contents. The default is on. .El .It Xo Ic show-options -.Op Fl gsvw +.Op Fl gqsvw .Op Fl t Ar target-session | Ar target-window .Op Ar option .Xc @@ -2964,6 +2969,11 @@ Global session or window options are listed if is used. .Fl v shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. .It Xo Ic show-window-options .Op Fl gv .Op Fl t Ar target-window @@ -3539,6 +3549,23 @@ If the command doesn't return success, the exit status is also displayed. .It Ic server-info .D1 (alias: Ic info ) Show server information and terminal details. +.It Xo Ic wait-for +.Fl LSU +.Ar channel +.Xc +.D1 (alias: Ic wait ) +When used without options, prevents the client from exiting until woken using +.Ic wait-for +.Fl S +with the same channel. +When +.Fl L +is used, the channel is locked and any clients that try to lock the same +channel are made to wait until the channel is unlocked with +.Ic wait-for +.Fl U . +This command only works from outside +.Nm . .El .Sh TERMINFO EXTENSIONS .Nm