On Thu, May 21, 2015 at 11:55:42AM +0100, Nicholas Marriott wrote: > I think that all the status jobs stuff needs to be moved into formats, > but it needs a bit of thinking.
Something like this: Index: cmd-refresh-client.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-refresh-client.c,v retrieving revision 1.13 diff -u -p -r1.13 cmd-refresh-client.c --- cmd-refresh-client.c 20 Oct 2014 22:29:25 -0000 1.13 +++ cmd-refresh-client.c 21 May 2015 11:30:04 -0000 @@ -65,10 +65,9 @@ cmd_refresh_client_exec(struct cmd *self } 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: format.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/format.c,v retrieving revision 1.67 diff -u -p -r1.67 format.c --- format.c 20 May 2015 06:39:02 -0000 1.67 +++ format.c 21 May 2015 11:30:04 -0000 @@ -35,6 +35,9 @@ * string. */ +void format_job_callback(struct job *); +const char *format_job_get(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,31 @@ void format_defaults_client(struct form 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; + + 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 +82,20 @@ 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; + 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 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); - -/* 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,6 +160,94 @@ 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; + + fj->job = NULL; + free(fj->out); + + if (!WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) { + xasprintf(&fj->out, "<job '%s' exited with %d>", fj->cmd, + WEXITSTATUS(job->status)); + return; + } + if (WIFSIGNALED(job->status)) { + xasprintf(&fj->out, "<job '%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; +} + +/* Find a job. */ +const char * +format_job_get(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->last = time(NULL); + xasprintf(&fj->out, "<job '%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, "<job '%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) @@ -160,8 +274,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 +299,7 @@ format_add(struct format_tree *ft, const 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 +338,7 @@ format_find(struct format_tree *ft, cons } 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 +472,9 @@ format_expand_time(struct format_tree *f 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 +497,34 @@ format_expand(struct format_tree *ft, co 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(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: server-client.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/server-client.c,v retrieving revision 1.138 diff -u -p -r1.138 server-client.c --- server-client.c 8 May 2015 15:56:49 -0000 1.138 +++ server-client.c 21 May 2015 11:30:05 -0000 @@ -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: server.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/server.c,v retrieving revision 1.122 diff -u -p -r1.122 server.c --- server.c 24 Apr 2015 23:17:11 -0000 1.122 +++ server.c 21 May 2015 11:30:05 -0000 @@ -485,6 +485,8 @@ server_second_callback(unused int fd, un server_client_status_timer(); + format_clean(); + evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; Index: status.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/status.c,v retrieving revision 1.128 diff -u -p -r1.128 status.c --- status.c 6 May 2015 23:56:46 -0000 1.128 +++ status.c 21 May 2015 11:30:05 -0000 @@ -33,13 +33,10 @@ char *status_redraw_get_left(struct cl 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 *); @@ -370,8 +367,7 @@ out: /* Replace a single special sequence (prefixed by #). */ void -status_replace1(struct client *c, char **iptr, char **optr, char *out, - size_t outsize) +status_replace1(char **iptr, char **optr, char *out, size_t outsize) { char ch, tmp[256], *ptr, *endptr; size_t ptrlen; @@ -388,10 +384,6 @@ status_replace1(struct client *c, char * 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 @@ -462,7 +454,7 @@ status_replace(struct client *c, struct *optr++ = ch; continue; } - status_replace1(c, &iptr, &optr, out, sizeof out); + status_replace1(&iptr, &optr, out, sizeof out); } *optr = '\0'; @@ -471,141 +463,6 @@ status_replace(struct client *c, struct 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. */ Index: tmux.h =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.h,v retrieving revision 1.514 diff -u -p -r1.514 tmux.h --- tmux.h 12 May 2015 22:40:38 -0000 1.514 +++ tmux.h 21 May 2015 11:30:06 -0000 @@ -1475,6 +1475,7 @@ void cfg_show_causes(struct session *) /* format.c */ struct format_tree; +void format_clean(void); struct format_tree *format_create(void); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users