If a #() command doesn't exit, use its most recent line of output (it
authornicm <nicm@openbsd.org>
Thu, 20 Apr 2017 09:20:22 +0000 (09:20 +0000)
committernicm <nicm@openbsd.org>
Thu, 20 Apr 2017 09:20:22 +0000 (09:20 +0000)
must be a full line). Don't let it redraw the status line more than once
a second.

Requested by someone about 10 years ago...

usr.bin/tmux/cmd-if-shell.c
usr.bin/tmux/cmd-run-shell.c
usr.bin/tmux/cmd-show-messages.c
usr.bin/tmux/format.c
usr.bin/tmux/job.c
usr.bin/tmux/server.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/window-copy.c

index 7ce23e0..b5029fb 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-if-shell.c,v 1.51 2017/03/08 13:36:12 nicm Exp $ */
+/* $OpenBSD: cmd-if-shell.c,v 1.52 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -127,8 +127,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
                cdata->item = NULL;
        memcpy(&cdata->mouse, &item->mouse, sizeof cdata->mouse);
 
-       job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free,
-           cdata);
+       job_run(shellcmd, s, cwd, NULL, cmd_if_shell_callback,
+           cmd_if_shell_free, cdata);
        free(shellcmd);
 
        if (args_has(args, 'b'))
index 0f10f38..665f0cb 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-run-shell.c,v 1.46 2017/03/08 13:36:12 nicm Exp $ */
+/* $OpenBSD: cmd-run-shell.c,v 1.47 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -110,8 +110,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
        if (!args_has(args, 'b'))
                cdata->item = item;
 
-       job_run(cdata->cmd, s, cwd, cmd_run_shell_callback, cmd_run_shell_free,
-           cdata);
+       job_run(cdata->cmd, s, cwd, NULL, cmd_run_shell_callback,
+           cmd_run_shell_free, cdata);
 
        if (args_has(args, 'b'))
                return (CMD_RETURN_NORMAL);
index 9db9905..9c82675 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-show-messages.c,v 1.25 2017/03/17 14:51:41 nicm Exp $ */
+/* $OpenBSD: cmd-show-messages.c,v 1.26 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -76,7 +76,7 @@ cmd_show_messages_jobs(struct cmdq_item *item, int blank)
        u_int            n;
 
        n = 0;
-       LIST_FOREACH(job, &all_jobs, lentry) {
+       LIST_FOREACH(job, &all_jobs, entry) {
                if (blank) {
                        cmdq_print(item, "%s", "");
                        blank = 0;
index fb43f42..0087527 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.126 2017/04/19 06:52:27 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.127 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -39,7 +39,6 @@
 struct format_entry;
 typedef void (*format_cb)(struct format_tree *, struct format_entry *);
 
-static void     format_job_callback(struct job *);
 static char    *format_job_get(struct format_tree *, const char *);
 static void     format_job_timer(int, short, void *);
 
@@ -83,6 +82,7 @@ struct format_job {
 
        time_t                   last;
        char                    *out;
+       int                      updated;
 
        struct job              *job;
        int                      status;
@@ -203,9 +203,35 @@ static const char *format_lower[] = {
        NULL            /* z */
 };
 
-/* Format job callback. */
+/* Format job update callback. */
 static void
-format_job_callback(struct job *job)
+format_job_update(struct job *job)
+{
+       struct format_job       *fj = job->data;
+       char                    *line;
+       time_t                   t;
+       struct client           *c;
+
+       if ((line = evbuffer_readline(job->event->input)) == NULL)
+               return;
+       fj->updated = 1;
+
+       free(fj->out);
+       fj->out = line;
+
+       log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
+
+       t = time (NULL);
+       if (fj->status && fj->last != t) {
+               TAILQ_FOREACH(c, &clients, entry)
+                   server_status_client(c);
+               fj->last = t;
+       }
+}
+
+/* Format job complete callback. */
+static void
+format_job_complete(struct job *job)
 {
        struct format_job       *fj = job->data;
        char                    *line, *buf;
@@ -213,7 +239,6 @@ format_job_callback(struct job *job)
        struct client           *c;
 
        fj->job = NULL;
-       free(fj->out);
 
        buf = NULL;
        if ((line = evbuffer_readline(job->event->input)) == NULL) {
@@ -224,15 +249,19 @@ format_job_callback(struct job *job)
                buf[len] = '\0';
        } else
                buf = line;
-       fj->out = buf;
+
+       if (*buf != '\0' || !fj->updated) {
+               free(fj->out);
+               fj->out = buf;
+               log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
+       } else
+               free(buf);
 
        if (fj->status) {
                TAILQ_FOREACH(c, &clients, entry)
                    server_status_client(c);
                fj->status = 0;
        }
-
-       log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
 }
 
 /* Find a job. */
@@ -267,8 +296,8 @@ format_job_get(struct format_tree *ft, const char *cmd)
 
        t = time(NULL);
        if (fj->job == NULL && (force || fj->last != t)) {
-               fj->job = job_run(expanded, NULL, NULL, format_job_callback,
-                   NULL, fj);
+               fj->job = job_run(expanded, NULL, NULL, format_job_update,
+                   format_job_complete, NULL, fj);
                if (fj->job == NULL) {
                        free(fj->out);
                        xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
index 9f50311..0345fdd 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: job.c,v 1.42 2017/03/09 17:02:38 nicm Exp $ */
+/* $OpenBSD: job.c,v 1.43 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -33,8 +33,9 @@
  * output.
  */
 
-static void    job_callback(struct bufferevent *, short, void *);
+static void    job_read_callback(struct bufferevent *, void *);
 static void    job_write_callback(struct bufferevent *, void *);
+static void    job_error_callback(struct bufferevent *, short, void *);
 
 /* All jobs list. */
 struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
@@ -42,7 +43,8 @@ struct joblist        all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
 /* Start a job running, if it isn't already. */
 struct job *
 job_run(const char *cmd, struct session *s, const char *cwd,
-    void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
+    job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
+    void *data)
 {
        struct job      *job;
        struct environ  *env;
@@ -104,17 +106,18 @@ job_run(const char *cmd, struct session *s, const char *cwd,
        job->pid = pid;
        job->status = 0;
 
-       LIST_INSERT_HEAD(&all_jobs, job, lentry);
+       LIST_INSERT_HEAD(&all_jobs, job, entry);
 
-       job->callbackfn = callbackfn;
-       job->freefn = freefn;
+       job->updatecb = updatecb;
+       job->completecb = completecb;
+       job->freecb = freecb;
        job->data = data;
 
        job->fd = out[0];
        setblocking(job->fd, 0);
 
-       job->event = bufferevent_new(job->fd, NULL, job_write_callback,
-           job_callback, job);
+       job->event = bufferevent_new(job->fd, job_read_callback,
+           job_write_callback, job_error_callback, job);
        bufferevent_enable(job->event, EV_READ|EV_WRITE);
 
        log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
@@ -127,11 +130,11 @@ job_free(struct job *job)
 {
        log_debug("free job %p: %s", job, job->cmd);
 
-       LIST_REMOVE(job, lentry);
+       LIST_REMOVE(job, entry);
        free(job->cmd);
 
-       if (job->freefn != NULL && job->data != NULL)
-               job->freefn(job->data);
+       if (job->freecb != NULL && job->data != NULL)
+               job->freecb(job->data);
 
        if (job->pid != -1)
                kill(job->pid, SIGTERM);
@@ -143,7 +146,21 @@ job_free(struct job *job)
        free(job);
 }
 
-/* Called when output buffer falls below low watermark (default is 0). */
+/* Job buffer read callback. */
+static void
+job_read_callback(__unused struct bufferevent *bufev, void *data)
+{
+       struct job      *job = data;
+
+       if (job->updatecb != NULL)
+               job->updatecb (job);
+}
+
+/*
+ * Job buffer write callback. Fired when the buffer falls below watermark
+ * (default is empty). If all the data has been written, disable the write
+ * event.
+ */
 static void
 job_write_callback(__unused struct bufferevent *bufev, void *data)
 {
@@ -161,7 +178,7 @@ job_write_callback(__unused struct bufferevent *bufev, void *data)
 
 /* Job buffer error callback. */
 static void
-job_callback(__unused struct bufferevent *bufev, __unused short events,
+job_error_callback(__unused struct bufferevent *bufev, __unused short events,
     void *data)
 {
        struct job      *job = data;
@@ -169,8 +186,8 @@ job_callback(__unused struct bufferevent *bufev, __unused short events,
        log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
 
        if (job->state == JOB_DEAD) {
-               if (job->callbackfn != NULL)
-                       job->callbackfn(job);
+               if (job->completecb != NULL)
+                       job->completecb(job);
                job_free(job);
        } else {
                bufferevent_disable(job->event, EV_READ);
@@ -187,8 +204,8 @@ job_died(struct job *job, int status)
        job->status = status;
 
        if (job->state == JOB_CLOSED) {
-               if (job->callbackfn != NULL)
-                       job->callbackfn(job);
+               if (job->completecb != NULL)
+                       job->completecb(job);
                job_free(job);
        } else {
                job->pid = -1;
index b01c4a0..4421979 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.166 2017/02/09 15:04:53 nicm Exp $ */
+/* $OpenBSD: server.c,v 1.167 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -134,7 +134,8 @@ server_create_socket(void)
 int
 server_start(struct event_base *base, int lockfd, char *lockfile)
 {
-       int     pair[2];
+       int              pair[2];
+       struct job      *job;
 
        if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
                fatal("socketpair failed");
@@ -180,6 +181,12 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
        server_add_accept(0);
 
        proc_loop(server_proc, server_loop);
+
+       LIST_FOREACH(job, &all_jobs, entry) {
+               if (job->pid != -1)
+                       kill(job->pid, SIGTERM);
+       }
+
        status_prompt_save_history();
        exit(0);
 }
@@ -400,7 +407,7 @@ server_child_exited(pid_t pid, int status)
                }
        }
 
-       LIST_FOREACH(job, &all_jobs, lentry) {
+       LIST_FOREACH(job, &all_jobs, entry) {
                if (pid == job->pid) {
                        job_died(job, status);  /* might free job */
                        break;
index 8f18660..b1c828d 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.542 2017/04/19 06:52:27 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.543 2017/04/20 09:20:22 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -14,7 +14,7 @@
 .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 19 2017 $
+.Dd $Mdocdate: April 20 2017 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -3490,6 +3490,8 @@ does not wait for
 .Ql #()
 commands to finish; instead, the previous result from running the same command is used,
 or a placeholder if the command has not been run before.
+If the command hasn't exited, the most recent line of output will be used, but the status
+line will not be updated more than once a second.
 Commands are executed with the
 .Nm
 global environment set (see the
index 627e956..e28e52c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.740 2017/04/19 14:00:28 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.741 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -588,6 +588,10 @@ struct hook {
 };
 
 /* Scheduled job. */
+struct job;
+typedef void (*job_update_cb) (struct job *);
+typedef void (*job_complete_cb) (struct job *);
+typedef void (*job_free_cb) (void *);
 struct job {
        enum {
                JOB_RUNNING,
@@ -595,18 +599,19 @@ struct job {
                JOB_CLOSED
        } state;
 
-       char            *cmd;
-       pid_t            pid;
-       int              status;
+       char                    *cmd;
+       pid_t                    pid;
+       int                      status;
 
-       int              fd;
-       struct bufferevent *event;
+       int                      fd;
+       struct bufferevent      *event;
 
-       void            (*callbackfn)(struct job *);
-       void            (*freefn)(void *);
-       void            *data;
+       job_update_cb            updatecb;
+       job_complete_cb          completecb;
+       job_free_cb              freecb;
+       void                    *data;
 
-       LIST_ENTRY(job)  lentry;
+       LIST_ENTRY(job)          entry;
 };
 LIST_HEAD(joblist, job);
 
@@ -1601,10 +1606,10 @@ extern const struct options_table_entry options_table[];
 
 /* job.c */
 extern struct joblist all_jobs;
-struct job *job_run(const char *, struct session *, const char *,
-           void (*)(struct job *), void (*)(void *), void *);
-void   job_free(struct job *);
-void   job_died(struct job *, int);
+struct job     *job_run(const char *, struct session *, const char *,
+                    job_update_cb, job_complete_cb, job_free_cb, void *);
+void            job_free(struct job *);
+void            job_died(struct job *, int);
 
 /* environ.c */
 struct environ *environ_create(void);
index eaaaa75..9cdee9a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-copy.c,v 1.170 2017/03/24 07:14:27 nicm Exp $ */
+/* $OpenBSD: window-copy.c,v 1.171 2017/04/20 09:20:22 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1641,7 +1641,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *s,
                return;
        expanded = format_single(NULL, arg, NULL, s, NULL, wp);
 
-       job = job_run(expanded, s, NULL, NULL, NULL, NULL);
+       job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL);
        bufferevent_write(job->event, buf, len);
 
        free(expanded);