Move the jobs output cache into the formats code so that #() work more
authornicm <nicm@openbsd.org>
Wed, 27 May 2015 13:28:04 +0000 (13:28 +0000)
committernicm <nicm@openbsd.org>
Wed, 27 May 2015 13:28:04 +0000 (13:28 +0000)
generally (for example, again working in set-titles-string).

usr.bin/tmux/cmd-refresh-client.c
usr.bin/tmux/format.c
usr.bin/tmux/server-client.c
usr.bin/tmux/server.c
usr.bin/tmux/status.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h

index f4e85ca..f1b9504 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-refresh-client.c,v 1.13 2014/10/20 22:29:25 nicm Exp $ */
+/* $OpenBSD: cmd-refresh-client.c,v 1.14 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -65,10 +65,9 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq)
                }
                if (tty_set_size(&c->tty, w, h))
                        recalculate_sizes();
-       } else if (args_has(args, 'S')) {
-               status_update_jobs(c);
+       } else if (args_has(args, 'S'))
                server_status_client(c);
-       else
+       else
                server_redraw_client(c);
 
        return (CMD_RETURN_NORMAL);
index 9bf9a1c..f7bbd89 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.67 2015/05/20 06:39:02 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.68 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -35,6 +35,9 @@
  * string.
  */
 
+void    format_job_callback(struct job *);
+const char *format_job_get(struct format_tree *, const char *);
+
 int     format_replace(struct format_tree *, const char *, size_t, char **,
             size_t *, size_t *);
 char   *format_time_string(time_t);
@@ -46,6 +49,32 @@ void  format_defaults_client(struct format_tree *, struct client *);
 void    format_defaults_winlink(struct format_tree *, struct session *,
             struct winlink *);
 
+/* Entry in format job tree. */
+struct format_job {
+       const char              *cmd;
+
+       time_t                   last;
+       char                    *out;
+
+       struct job              *job;
+       int                      status;
+
+       RB_ENTRY(format_job)     entry;
+};
+
+/* Format job tree. */
+int    format_job_cmp(struct format_job *, struct format_job *);
+RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
+RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp);
+RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp);
+
+/* Format job tree comparison function. */
+int
+format_job_cmp(struct format_job *fj1, struct format_job *fj2)
+{
+       return (strcmp(fj1->cmd, fj2->cmd));
+}
+
 /* Entry in format tree. */
 struct format_entry {
        char                   *key;
@@ -54,22 +83,22 @@ struct format_entry {
        RB_ENTRY(format_entry)  entry;
 };
 
-/* Tree of format entries. */
+/* Format entry tree. */
 struct format_tree {
        struct window   *w;
        struct session  *s;
 
-       RB_HEAD(format_rb_tree, format_entry) tree;
-};
+       int              status;
 
-/* Format key-value replacement entry. */
-int    format_cmp(struct format_entry *, struct format_entry *);
-RB_PROTOTYPE(format_rb_tree, format_entry, entry, format_cmp);
-RB_GENERATE(format_rb_tree, format_entry, entry, format_cmp);
+       RB_HEAD(format_entry_tree, format_entry) tree;
+};
+int    format_entry_cmp(struct format_entry *, struct format_entry *);
+RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp);
+RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp);
 
-/* Format tree comparison function. */
+/* Format entry tree comparison function. */
 int
-format_cmp(struct format_entry *fe1, struct format_entry *fe2)
+format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
 {
        return (strcmp(fe1->key, fe2->key));
 }
@@ -134,15 +163,118 @@ const char *format_lower[] = {
        NULL            /* z */
 };
 
+/* Format job callback. */
+void
+format_job_callback(struct job *job)
+{
+       struct format_job       *fj = job->data;
+       char                    *line, *buf;
+       size_t                   len;
+       struct client           *c;
+
+       fj->job = NULL;
+       free(fj->out);
+
+       if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) {
+               xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd,
+                   WEXITSTATUS(job->status));
+               return;
+       }
+       if (WIFSIGNALED(job->status)) {
+               xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd,
+                   WTERMSIG(job->status));
+               return;
+       }
+
+       buf = NULL;
+       if ((line = evbuffer_readline(job->event->input)) == NULL) {
+               len = EVBUFFER_LENGTH(job->event->input);
+               buf = xmalloc(len + 1);
+               if (len != 0)
+                       memcpy(buf, EVBUFFER_DATA(job->event->input), len);
+               buf[len] = '\0';
+       } else
+               buf = line;
+       fj->out = buf;
+
+       if (fj->status) {
+               TAILQ_FOREACH(c, &clients, entry)
+                   server_status_client(c);
+               fj->status = 0;
+       }
+}
+
+/* Find a job. */
+const char *
+format_job_get(struct format_tree *ft, const char *cmd)
+{
+       struct format_job       fj0, *fj;
+
+       fj0.cmd = cmd;
+       if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL)
+       {
+               fj = xcalloc(1, sizeof *fj);
+               fj->cmd = xstrdup(cmd);
+               fj->status = ft->status;
+
+               xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
+
+               RB_INSERT(format_job_tree, &format_jobs, fj);
+       }
+
+       if (fj->job == NULL && fj->last != time(NULL)) {
+               fj->job = job_run(fj->cmd, NULL, -1, format_job_callback,
+                   NULL, fj);
+               if (fj->job == NULL) {
+                       free(fj->out);
+                       xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
+               }
+       }
+       fj->last = time(NULL);
+
+       return (fj->out);
+}
+
+/* Remove old jobs. */
+void
+format_clean(void)
+{
+       struct format_job       *fj, *fj1;
+       time_t                   now;
+
+       now = time(NULL);
+       RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) {
+               if (fj->last > now || now - fj->last < 3600)
+                       continue;
+               RB_REMOVE(format_job_tree, &format_jobs, fj);
+
+               if (fj->job != NULL)
+                       job_free(fj->job);
+
+               free((void*)fj->cmd);
+               free(fj->out);
+
+               free(fj);
+       }
+}
+
 /* Create a new tree. */
 struct format_tree *
 format_create(void)
+{
+       return (format_create_status(0));
+}
+
+/* Create a new tree for the status line. */
+struct format_tree *
+format_create_status(int status)
 {
        struct format_tree      *ft;
-       char                     host[HOST_NAME_MAX+1], *ptr;
+       char                     host[HOST_NAME_MAX + 1], *ptr;
 
        ft = xcalloc(1, sizeof *ft);
        RB_INIT(&ft->tree);
+       ft->status = status;
 
        if (gethostname(host, sizeof host) == 0) {
                format_add(ft, "host", "%s", host);
@@ -160,8 +292,8 @@ format_free(struct format_tree *ft)
 {
        struct format_entry     *fe, *fe1;
 
-       RB_FOREACH_SAFE(fe, format_rb_tree, &ft->tree, fe1) {
-               RB_REMOVE(format_rb_tree, &ft->tree, fe);
+       RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
+               RB_REMOVE(format_entry_tree, &ft->tree, fe);
                free(fe->value);
                free(fe->key);
                free(fe);
@@ -185,7 +317,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
        xvasprintf(&fe->value, fmt, ap);
        va_end(ap);
 
-       fe_now = RB_INSERT(format_rb_tree, &ft->tree, fe);
+       fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
        if (fe_now != NULL) {
                free(fe_now->value);
                fe_now->value = fe->value;
@@ -224,7 +356,7 @@ format_find(struct format_tree *ft, const char *key)
        }
 
        fe_find.key = (char *) key;
-       fe = RB_FIND(format_rb_tree, &ft->tree, &fe_find);
+       fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
        if (fe == NULL)
                return (NULL);
        return (fe->value);
@@ -358,9 +490,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t)
 char *
 format_expand(struct format_tree *ft, const char *fmt)
 {
-       char            *buf;
+       char            *buf, *tmp;
        const char      *ptr, *s;
-       size_t           off, len, n;
+       size_t           off, len, n, slen;
        int              ch, brackets;
 
        if (fmt == NULL)
@@ -383,6 +515,34 @@ format_expand(struct format_tree *ft, const char *fmt)
 
                ch = (u_char) *fmt++;
                switch (ch) {
+               case '(':
+                       brackets = 1;
+                       for (ptr = fmt; *ptr != '\0'; ptr++) {
+                               if (*ptr == '(')
+                                       brackets++;
+                               if (*ptr == ')' && --brackets == 0)
+                                       break;
+                       }
+                       if (*ptr != ')' || brackets != 0)
+                               break;
+                       n = ptr - fmt;
+
+                       tmp = xmalloc(n + 1);
+                       memcpy(tmp, fmt, n);
+                       tmp[n] = '\0';
+
+                       s = format_job_get(ft, tmp);
+                       slen = strlen(s);
+
+                       while (len - off < slen + 1) {
+                               buf = xreallocarray(buf, 2, len);
+                               len *= 2;
+                       }
+                       memcpy(buf + off, s, slen);
+                       off += slen;
+
+                       fmt += n + 1;
+                       continue;
                case '{':
                        brackets = 1;
                        for (ptr = fmt; *ptr != '\0'; ptr++) {
index 7e87362..731b9ea 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server-client.c,v 1.138 2015/05/08 15:56:49 nicm Exp $ */
+/* $OpenBSD: server-client.c,v 1.139 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -157,8 +157,6 @@ server_client_lost(struct client *c)
        if (c->stderr_data != c->stdout_data)
                evbuffer_free(c->stderr_data);
 
-       status_free_jobs(&c->status_new);
-       status_free_jobs(&c->status_old);
        screen_free(&c->status);
 
        free(c->title);
@@ -269,10 +267,8 @@ server_client_status_timer(void)
                interval = options_get_number(&s->options, "status-interval");
 
                difference = tv.tv_sec - c->status_timer.tv_sec;
-               if (interval != 0 && difference >= interval) {
-                       status_update_jobs(c);
+               if (interval != 0 && difference >= interval)
                        c->flags |= CLIENT_STATUS;
-               }
        }
 }
 
index 0a652a7..34ec540 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.122 2015/04/24 23:17:11 nicm Exp $ */
+/* $OpenBSD: server.c,v 1.123 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -485,6 +485,8 @@ server_second_callback(unused int fd, unused short events, unused void *arg)
 
        server_client_status_timer();
 
+       format_clean();
+
        evtimer_del(&server_ev_second);
        memset(&tv, 0, sizeof tv);
        tv.tv_sec = 1;
index 7b8885e..ae0ea06 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: status.c,v 1.128 2015/05/06 23:56:46 nicm Exp $ */
+/* $OpenBSD: status.c,v 1.129 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -33,13 +33,10 @@ char   *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *,
            size_t *);
 char   *status_redraw_get_right(struct client *, time_t, int,
            struct grid_cell *, size_t *);
-char   *status_find_job(struct client *, char **);
-void   status_job_free(void *);
-void   status_job_callback(struct job *);
 char   *status_print(struct client *, struct winlink *, time_t,
            struct grid_cell *);
 char   *status_replace(struct client *, struct winlink *, const char *, time_t);
-void   status_replace1(struct client *, char **, char **, char *, size_t);
+void   status_replace1(char **, char **, char *, size_t);
 void   status_message_callback(int, short, void *);
 
 const char *status_prompt_up_history(u_int *);
@@ -368,246 +365,25 @@ out:
        return (1);
 }
 
-/* Replace a single special sequence (prefixed by #). */
-void
-status_replace1(struct client *c, char **iptr, char **optr, char *out,
-    size_t outsize)
-{
-       char    ch, tmp[256], *ptr, *endptr;
-       size_t  ptrlen;
-       long    limit;
-
-       errno = 0;
-       limit = strtol(*iptr, &endptr, 10);
-       if ((limit == 0 && errno != EINVAL) ||
-           (limit == LONG_MIN && errno != ERANGE) ||
-           (limit == LONG_MAX && errno != ERANGE) ||
-           limit != 0)
-               *iptr = endptr;
-       if (limit <= 0)
-               limit = LONG_MAX;
-
-       switch (*(*iptr)++) {
-       case '(':
-               if ((ptr = status_find_job(c, iptr)) == NULL)
-                       return;
-               goto do_replace;
-       case '[':
-               /*
-                * Embedded style, handled at display time. Leave present and
-                * skip input until ].
-                */
-               ch = ']';
-               goto skip_to;
-       case '{':
-               ptr = (char *) "#{";
-               goto do_replace;
-       default:
-               xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1));
-               ptr = tmp;
-               goto do_replace;
-       }
-
-       return;
-
-do_replace:
-       ptrlen = strlen(ptr);
-       if ((size_t) limit < ptrlen)
-               ptrlen = limit;
-
-       if (*optr + ptrlen >= out + outsize - 1)
-               return;
-       while (ptrlen > 0 && *ptr != '\0') {
-               *(*optr)++ = *ptr++;
-               ptrlen--;
-       }
-
-       return;
-
-skip_to:
-       *(*optr)++ = '#';
-
-       (*iptr)--;      /* include ch */
-       while (**iptr != ch && **iptr != '\0') {
-               if (*optr >=  out + outsize - 1)
-                       break;
-               *(*optr)++ = *(*iptr)++;
-       }
-}
-
 /* Replace special sequences in fmt. */
 char *
 status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
 {
-       static char              out[BUFSIZ];
-       char                     in[BUFSIZ], ch, *iptr, *optr, *expanded;
-       size_t                   len;
        struct format_tree      *ft;
+       char                    *expanded;
 
        if (fmt == NULL)
                return (xstrdup(""));
 
-       len = strftime(in, sizeof in, fmt, localtime(&t));
-       in[len] = '\0';
-
-       iptr = in;
-       optr = out;
-
-       while (*iptr != '\0') {
-               if (optr >= out + (sizeof out) - 1)
-                       break;
-               ch = *iptr++;
+       ft = format_create_status(1);
+       format_defaults(ft, c, NULL, wl, NULL);
 
-               if (ch != '#' || *iptr == '\0') {
-                       *optr++ = ch;
-                       continue;
-               }
-               status_replace1(c, &iptr, &optr, out, sizeof out);
-       }
-       *optr = '\0';
+       expanded = format_expand_time(ft, fmt, t);
 
-       ft = format_create();
-       format_defaults(ft, c, NULL, wl, NULL);
-       expanded = format_expand(ft, out);
        format_free(ft);
        return (expanded);
 }
 
-/* Figure out job name and get its result, starting it off if necessary. */
-char *
-status_find_job(struct client *c, char **iptr)
-{
-       struct status_out       *so, so_find;
-       char                    *cmd;
-       int                      lastesc;
-       size_t                   len;
-
-       if (**iptr == '\0')
-               return (NULL);
-       if (**iptr == ')') {            /* no command given */
-               (*iptr)++;
-               return (NULL);
-       }
-
-       cmd = xmalloc(strlen(*iptr) + 1);
-       len = 0;
-
-       lastesc = 0;
-       for (; **iptr != '\0'; (*iptr)++) {
-               if (!lastesc && **iptr == ')')
-                       break;          /* unescaped ) is the end */
-               if (!lastesc && **iptr == '\\') {
-                       lastesc = 1;
-                       continue;       /* skip \ if not escaped */
-               }
-               lastesc = 0;
-               cmd[len++] = **iptr;
-       }
-       if (**iptr == '\0')             /* no terminating ) */ {
-               free(cmd);
-               return (NULL);
-       }
-       (*iptr)++;                      /* skip final ) */
-       cmd[len] = '\0';
-
-       /* First try in the new tree. */
-       so_find.cmd = cmd;
-       so = RB_FIND(status_out_tree, &c->status_new, &so_find);
-       if (so != NULL && so->out != NULL) {
-               free(cmd);
-               return (so->out);
-       }
-
-       /* If not found at all, start the job and add to the tree. */
-       if (so == NULL) {
-               job_run(cmd, NULL, -1, status_job_callback, status_job_free, c);
-               c->references++;
-
-               so = xmalloc(sizeof *so);
-               so->cmd = xstrdup(cmd);
-               so->out = NULL;
-               RB_INSERT(status_out_tree, &c->status_new, so);
-       }
-
-       /* Lookup in the old tree. */
-       so_find.cmd = cmd;
-       so = RB_FIND(status_out_tree, &c->status_old, &so_find);
-       free(cmd);
-       if (so != NULL)
-               return (so->out);
-       return (NULL);
-}
-
-/* Free job tree. */
-void
-status_free_jobs(struct status_out_tree *sotree)
-{
-       struct status_out       *so, *so_next;
-
-       so_next = RB_MIN(status_out_tree, sotree);
-       while (so_next != NULL) {
-               so = so_next;
-               so_next = RB_NEXT(status_out_tree, sotree, so);
-
-               RB_REMOVE(status_out_tree, sotree, so);
-               free(so->out);
-               free(so->cmd);
-               free(so);
-       }
-}
-
-/* Update jobs on status interval. */
-void
-status_update_jobs(struct client *c)
-{
-       /* Free the old tree. */
-       status_free_jobs(&c->status_old);
-
-       /* Move the new to old. */
-       memcpy(&c->status_old, &c->status_new, sizeof c->status_old);
-       RB_INIT(&c->status_new);
-}
-
-/* Free status job. */
-void
-status_job_free(void *data)
-{
-       struct client   *c = data;
-
-       c->references--;
-}
-
-/* Job has finished: save its result. */
-void
-status_job_callback(struct job *job)
-{
-       struct client           *c = job->data;
-       struct status_out       *so, so_find;
-       char                    *line, *buf;
-       size_t                   len;
-
-       if (c->flags & CLIENT_DEAD)
-               return;
-
-       so_find.cmd = job->cmd;
-       so = RB_FIND(status_out_tree, &c->status_new, &so_find);
-       if (so == NULL || so->out != NULL)
-               return;
-
-       buf = NULL;
-       if ((line = evbuffer_readline(job->event->input)) == NULL) {
-               len = EVBUFFER_LENGTH(job->event->input);
-               buf = xmalloc(len + 1);
-               if (len != 0)
-                       memcpy(buf, EVBUFFER_DATA(job->event->input), len);
-               buf[len] = '\0';
-       } else
-               buf = line;
-
-       so->out = buf;
-       server_status_client(c);
-}
-
 /* Return winlink status line entry and adjust gc as necessary. */
 char *
 status_print(struct client *c, struct winlink *wl, time_t t,
index 7ca49f0..6d0fd14 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.430 2015/05/12 15:29:29 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.431 2015/05/27 13:28:04 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
 .\"
@@ -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: May 12 2015 $
+.Dd $Mdocdate: May 27 2015 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -2662,25 +2662,10 @@ will be expanded.
 It may also contain any of the following special character sequences:
 .Bl -column "Character pair" "Replaced with" -offset indent
 .It Sy "Character pair" Ta Sy "Replaced with"
-.It Li "#(shell-command)" Ta "First line of the command's output"
 .It Li "#[attributes]" Ta "Colour or attribute change"
 .It Li "##" Ta "A literal" Ql #
 .El
 .Pp
-The #(shell-command) form executes
-.Ql shell-command
-and inserts the first line of its output.
-Note that shell commands are only executed once at the interval specified by
-the
-.Ic status-interval
-option: if the status line is redrawn in the meantime, the previous result is
-used.
-Shell commands are executed with the
-.Nm
-global environment set (see the
-.Sx ENVIRONMENT
-section).
-.Pp
 For details on how the names and titles can be set see the
 .Sx "NAMES AND TITLES"
 section.
@@ -3245,6 +3230,23 @@ a number and a colon, so
 .Ql #{=10:pane_title}
 will include at most the first 10 characters of the pane title.
 .Pp
+In addition, the first line of a shell command's output may be inserted using
+.Ql #() .
+For example,
+.Ql #(uptime)
+will insert the system's uptime.
+When constructing formats,
+.Nm
+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.
+Commands are executed with the
+.Nm
+global environment set (see the
+.Sx ENVIRONMENT
+section).
+.Pp
 The following variables are available, where appropriate:
 .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
 .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
index c627af1..a79ac4a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.514 2015/05/12 22:40:38 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.515 2015/05/27 13:28:04 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -1475,7 +1475,9 @@ void               cfg_show_causes(struct session *);
 
 /* format.c */
 struct format_tree;
+void            format_clean(void);
 struct format_tree *format_create(void);
+struct format_tree *format_create_status(int);
 void            format_free(struct format_tree *);
 void printflike(3, 4) format_add(struct format_tree *, const char *,
                     const char *, ...);