diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 4b4399c8..045f551e 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -44,8 +44,8 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bd:Ct:c:", 0, 1, cmd_run_shell_args_parse }, - .usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE + .args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse }, + .usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -158,6 +158,9 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) else cdata->cwd = xstrdup(server_client_get_cwd(c, s)); + if (args_has(args, 'E')) + cdata->flags |= JOB_SHOWSTDERR; + cdata->s = s; if (s != NULL) session_add_ref(s, __func__); diff --git a/format.c b/format.c index 05d65b68..bc648c14 100644 --- a/format.c +++ b/format.c @@ -106,6 +106,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_CLIENTS 0x40000 #define FORMAT_NOT 0x80000 #define FORMAT_NOT_NOT 0x100000 +#define FORMAT_REPEAT 0x200000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 100 @@ -205,7 +206,7 @@ static const char *format_upper[] = { "window_name", /* W */ NULL, /* X */ NULL, /* Y */ - NULL /* Z */ + NULL /* Z */ }; /* Single-character lowercase aliases. */ @@ -865,7 +866,7 @@ format_cb_history_bytes(struct format_tree *ft) struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; - size_t size = 0; + size_t size = 0; u_int i; char *value; @@ -1623,11 +1624,11 @@ format_cb_cursor_shape(struct format_tree *ft) switch (ft->wp->screen->cstyle) { case SCREEN_CURSOR_BLOCK: return (xstrdup("block")); - case SCREEN_CURSOR_UNDERLINE: + case SCREEN_CURSOR_UNDERLINE: return (xstrdup("underline")); - case SCREEN_CURSOR_BAR: + case SCREEN_CURSOR_BAR: return (xstrdup("bar")); - default: + default: return (xstrdup("default")); } } @@ -3672,7 +3673,7 @@ format_quote_style(const char *s) char * format_pretty_time(time_t t, int seconds) { - struct tm now_tm, tm; + struct tm now_tm, tm; time_t now, age; char s[9]; @@ -3850,7 +3851,7 @@ format_unescape(const char *s) *s == '#' && strchr(",#{}:", s[1]) != NULL) { *cp++ = *++s; - continue; + continue; } if (*s == '}') brackets--; @@ -3992,10 +3993,10 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* * Modifiers are a ; separated list of the forms: - * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,> + * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,R,<,> * =a * =/a - * =/a/ + * =/a/ * s/a/b/ * s/a/b * ||,&&,!=,==,<=,>= @@ -4031,7 +4032,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, } /* Now try single character with arguments. */ - if (strchr("mCNst=peq", cp[0]) == NULL) + if (strchr("mCNst=pReq", cp[0]) == NULL) break; c = cp[0]; @@ -4586,7 +4587,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, struct format_modifier *list, *cmp = NULL, *search = NULL; struct format_modifier **sub = NULL, *mexp = NULL, *fm; struct format_modifier *bool_op_n = NULL; - u_int i, count, nsub = 0; + u_int i, count, nsub = 0, nrep; struct format_expand_state next; /* Make a copy of the key. */ @@ -4708,6 +4709,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'L': modifiers |= FORMAT_CLIENTS; break; + case 'R': + modifiers |= FORMAT_REPEAT; + break; } } else if (fm->size == 2) { if (strcmp(fm->modifier, "||") == 0 || @@ -4790,7 +4794,26 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = format_search(search, wp, new); } free(new); - } else if (modifiers & FORMAT_NOT) { + } else if (modifiers & FORMAT_REPEAT) { + /* Repeat multiple times. */ + if (format_choose(es, copy, &left, &right, 1) != 0) { + format_log(es, "repeat syntax error: %s", copy); + goto fail; + } + nrep = strtonum(right, 1, 10000, &errstr); + if (errstr != NULL) + value = xstrdup(""); + else { + value = xstrdup(""); + for (i = 0; i < nrep; i++) { + xasprintf(&new, "%s%s", value, left); + free(value); + value = new; + } + } + free(right); + free(left); + } else if (modifiers & FORMAT_NOT) { value = format_bool_op_1(es, copy, 1); } else if (modifiers & FORMAT_NOT_NOT) { value = format_bool_op_1(es, copy, 0); @@ -5050,7 +5073,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) char *buf, *out, *name; const char *ptr, *s, *style_end = NULL; size_t off, len, n, outlen; - int ch, brackets; + int ch, brackets; char expanded[8192]; if (fmt == NULL || *fmt == '\0') diff --git a/job.c b/job.c index a5fc79c8..6a874121 100644 --- a/job.c +++ b/job.c @@ -77,7 +77,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct job *job; struct environ *env; pid_t pid; - int nullfd, out[2], master; + int nullfd, out[2], master, do_close = 1; const char *home, *shell; sigset_t set, oldset; struct winsize ws; @@ -150,19 +150,26 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, if (~flags & JOB_PTY) { if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); + do_close = do_close && out[1] != STDIN_FILENO; if (dup2(out[1], STDOUT_FILENO) == -1) fatal("dup2 failed"); - if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) + do_close = do_close && out[1] != STDOUT_FILENO; + if (flags & JOB_SHOWSTDERR) { + if (dup2(out[1], STDERR_FILENO) == -1) + fatal("dup2 failed"); + do_close = do_close && out[1] != STDERR_FILENO; + } else { + nullfd = open(_PATH_DEVNULL, O_RDWR); + if (nullfd == -1) + fatal("open failed"); + if (dup2(nullfd, STDERR_FILENO) == -1) + fatal("dup2 failed"); + if (nullfd != STDERR_FILENO) + close(nullfd); + } + if (do_close) close(out[1]); close(out[0]); - - nullfd = open(_PATH_DEVNULL, O_RDWR); - if (nullfd == -1) - fatal("open failed"); - if (dup2(nullfd, STDERR_FILENO) == -1) - fatal("dup2 failed"); - if (nullfd != STDERR_FILENO) - close(nullfd); } closefrom(STDERR_FILENO + 1); diff --git a/status.c b/status.c index 745ee3ae..2786db7e 100644 --- a/status.c +++ b/status.c @@ -631,17 +631,18 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, if (input == NULL) input = ""; - if (flags & PROMPT_NOFORMAT) - tmp = xstrdup(input); - else - tmp = format_expand_time(ft, input); status_message_clear(c); status_prompt_clear(c); status_push_screen(c); - c->prompt_string = format_expand_time(ft, msg); + c->prompt_formats = ft; + c->prompt_string = xstrdup (msg); + if (flags & PROMPT_NOFORMAT) + tmp = xstrdup(input); + else + tmp = format_expand_time(ft, input); if (flags & PROMPT_INCREMENTAL) { c->prompt_last = xstrdup(tmp); c->prompt_buffer = utf8_fromcstr(""); @@ -650,6 +651,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->prompt_buffer = utf8_fromcstr(tmp); } c->prompt_index = utf8_strlen(c->prompt_buffer); + free(tmp); c->prompt_inputcb = inputcb; c->prompt_freecb = freecb; @@ -668,9 +670,6 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, if (flags & PROMPT_INCREMENTAL) c->prompt_inputcb(c, c->prompt_data, "=", 0); - free(tmp); - format_free(ft); - if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT)) cmdq_append(c, cmdq_get_callback(status_prompt_accept, c)); } @@ -688,6 +687,9 @@ status_prompt_clear(struct client *c) free(c->prompt_last); c->prompt_last = NULL; + format_free(c->prompt_formats); + c->prompt_formats = NULL; + free(c->prompt_string); c->prompt_string = NULL; @@ -707,27 +709,20 @@ status_prompt_clear(struct client *c) void status_prompt_update(struct client *c, const char *msg, const char *input) { - struct format_tree *ft; - char *tmp; - - ft = format_create(c, NULL, FORMAT_NONE, 0); - format_defaults(ft, c, NULL, NULL, NULL); - - tmp = format_expand_time(ft, input); + char *tmp; free(c->prompt_string); - c->prompt_string = format_expand_time(ft, msg); + c->prompt_string = xstrdup(msg); free(c->prompt_buffer); + tmp = format_expand_time(c->prompt_formats, input); c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); + free(tmp); memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->flags |= CLIENT_REDRAWSTATUS; - - free(tmp); - format_free(ft); } /* Redraw character. Return 1 if can continue redrawing, 0 otherwise. */ @@ -790,7 +785,8 @@ status_prompt_redraw(struct client *c) u_int i, lines, offset, left, start, width, n; u_int pcursor, pwidth, promptline; struct grid_cell gc; - struct format_tree *ft; + struct format_tree *ft = c->prompt_formats; + char *prompt, *tmp; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -811,14 +807,17 @@ status_prompt_redraw(struct client *c) if (promptline > lines - 1) promptline = lines - 1; - ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (c->prompt_mode == PROMPT_COMMAND) style_apply(&gc, s->options, "message-command-style", ft); else style_apply(&gc, s->options, "message-style", ft); - format_free(ft); - start = format_width(c->prompt_string); + tmp = utf8_tocstr(c->prompt_buffer); + format_add(c->prompt_formats, "prompt-input", "%s", tmp); + prompt = format_expand_time(c->prompt_formats, c->prompt_string); + free (tmp); + + start = format_width(prompt); if (start > c->tty.sx) start = c->tty.sx; @@ -828,7 +827,7 @@ status_prompt_redraw(struct client *c) for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, promptline, 0); - format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0); + format_draw(&ctx, &gc, start, prompt, NULL, 0); screen_write_cursormove(&ctx, start, promptline, 0); left = c->tty.sx - start; diff --git a/tmux.1 b/tmux.1 index 58686732..da3225bc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5835,6 +5835,12 @@ expands to the length of the variable and .Ql w to its width when displayed, for example .Ql #{n:window_name} . +.Ql R +repeats the first argument by a number of times given by the second argument, +so +.Ql #{R:a,3} +will result in +.Ql aaa . .Pp Prefixing a time variable with .Ql t:\& @@ -7283,7 +7289,7 @@ Lock each client individually by running the command specified by the option. .Tg run .It Xo Ic run-shell -.Op Fl bC +.Op Fl bCE .Op Fl c Ar start-directory .Op Fl d Ar delay .Op Fl t Ar target-pane @@ -7311,6 +7317,8 @@ the command is run in the background. waits for .Ar delay seconds before starting the command. +.Fl E +redirects the command's stderr to stdout instead of ignoring it. If .Fl c is given, the current working directory is set to diff --git a/tmux.h b/tmux.h index cd71634a..55334e93 100644 --- a/tmux.h +++ b/tmux.h @@ -2024,6 +2024,7 @@ struct client { struct event message_timer; char *prompt_string; + struct format_tree *prompt_formats; struct utf8_data *prompt_buffer; char *prompt_last; size_t prompt_index; @@ -2428,6 +2429,7 @@ typedef void (*job_free_cb) (void *); #define JOB_KEEPWRITE 0x2 #define JOB_PTY 0x4 #define JOB_DEFAULTSHELL 0x8 +#define JOB_SHOWSTDERR 0x10 struct job *job_run(const char *, int, char **, struct environ *, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int, int, int); diff --git a/tty.c b/tty.c index bfedb8ee..19e55757 100644 --- a/tty.c +++ b/tty.c @@ -2756,8 +2756,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, if (changed & GRID_ATTR_ITALICS) tty_set_italics(tty); if (changed & GRID_ATTR_ALL_UNDERSCORE) { - if ((changed & GRID_ATTR_UNDERSCORE) || - !tty_term_has(tty->term, TTYC_SMULX)) + if (changed & GRID_ATTR_UNDERSCORE) tty_putcode(tty, TTYC_SMUL); else if (changed & GRID_ATTR_UNDERSCORE_2) tty_putcode_i(tty, TTYC_SMULX, 2);