diff --git a/external/bsd/tmux/dist/client.c b/external/bsd/tmux/dist/client.c index 204a431b8d8e..f247d0590f81 100644 --- a/external/bsd/tmux/dist/client.c +++ b/external/bsd/tmux/dist/client.c @@ -35,7 +35,6 @@ static struct tmuxproc *client_proc; static struct tmuxpeer *client_peer; static int client_flags; -static struct event client_stdin; static enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -46,19 +45,19 @@ static enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; +static int client_exitflag; static int client_exitval; static enum msgtype client_exittype; static const char *client_exitsession; static const char *client_execshell; static const char *client_execcmd; static int client_attached; +static struct client_files client_files = RB_INITIALIZER(&client_files); static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); static int client_connect(struct event_base *, const char *, int); static void client_send_identify(const char *, const char *); -static void client_stdin_callback(int, short, void *); -static void client_write(int, const char *, size_t); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); @@ -211,13 +210,34 @@ client_exit_message(void) return ("unknown reason"); } +/* Exit if all streams flushed. */ +static void +client_exit(void) +{ + struct client_file *cf; + size_t left; + int waiting = 0; + + RB_FOREACH (cf, client_files, &client_files) { + if (cf->event == NULL) + continue; + left = EVBUFFER_LENGTH(cf->event->output); + if (left != 0) { + waiting++; + log_debug("file %u %zu bytes left", cf->stream, left); + } + } + if (waiting == 0) + proc_exit(client_proc); +} + /* Client main loop. */ int client_main(struct event_base *base, int argc, char **argv, int flags) { struct cmd_parse_result *pr; struct cmd *cmd; - struct msg_command_data *data; + struct msg_command *data; int cmdflags, fd, i; const char *ttynam, *cwd; pid_t ppid; @@ -291,7 +311,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) * * "sendfd" is dropped later in client_dispatch_wait(). */ - if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) + if (pledge( + "stdio rpath wpath cpath unix sendfd proc exec tty", + NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ @@ -302,10 +324,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(global_w_options); environ_free(global_environ); - /* Create stdin handler. */ - setblocking(STDIN_FILENO, 0); - event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, - client_stdin_callback, NULL); + /* Set up control mode. */ if (client_flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", @@ -428,39 +447,255 @@ client_send_identify(const char *ttynam, const char *cwd) proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } -/* Callback for client stdin read events. */ +/* File write error callback. */ static void -client_stdin_callback(__unused int fd, __unused short events, - __unused void *arg) +client_write_error_callback(__unused struct bufferevent *bev, + __unused short what, void *arg) { - struct msg_stdin_data data; + struct client_file *cf = arg; - data.size = read(STDIN_FILENO, data.data, sizeof data.data); - if (data.size == -1 && (errno == EINTR || errno == EAGAIN)) - return; + log_debug("write error file %d", cf->stream); - proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); - if (data.size <= 0) - event_del(&client_stdin); + bufferevent_free(cf->event); + cf->event = NULL; + + close(cf->fd); + cf->fd = -1; + + if (client_exitflag) + client_exit(); } -/* Force write to file descriptor. */ +/* File write callback. */ static void -client_write(int fd, const char *data, size_t size) +client_write_callback(__unused struct bufferevent *bev, void *arg) { - ssize_t used; + struct client_file *cf = arg; - log_debug("%s: %.*s", __func__, (int)size, data); - while (size != 0) { - used = write(fd, data, size); - if (used == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - break; - } - data += used; - size -= used; + if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { + bufferevent_free(cf->event); + close(cf->fd); + RB_REMOVE(client_files, &client_files, cf); + file_free(cf); } + + if (client_exitflag) + client_exit(); +} + +/* Open write file. */ +static void +client_write_open(void *data, size_t datalen) +{ + struct msg_write_open *msg = data; + const char *path; + struct msg_write_ready reply; + struct client_file find, *cf; + const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; + int error = 0; + + if (datalen < sizeof *msg) + fatalx("bad MSG_WRITE_OPEN size"); + if (datalen == sizeof *msg) + path = "-"; + else + path = (const char *)(msg + 1); + log_debug("open write file %d %s", msg->stream, path); + + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { + cf = file_create(NULL, msg->stream, NULL, NULL); + RB_INSERT(client_files, &client_files, cf); + } else { + error = EBADF; + goto reply; + } + if (cf->closed) { + error = EBADF; + goto reply; + } + + cf->fd = -1; + if (msg->fd == -1) + cf->fd = open(path, msg->flags|flags, 0644); + else { + if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) + errno = EBADF; + else { + cf->fd = dup(msg->fd); + if (~client_flags & CLIENT_CONTROL) + close(msg->fd); /* can only be used once */ + } + } + if (cf->fd == -1) { + error = errno; + goto reply; + } + + cf->event = bufferevent_new(cf->fd, NULL, client_write_callback, + client_write_error_callback, cf); + bufferevent_enable(cf->event, EV_WRITE); + goto reply; + +reply: + reply.stream = msg->stream; + reply.error = error; + proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply); +} + +/* Write to client file. */ +static void +client_write_data(void *data, size_t datalen) +{ + struct msg_write_data *msg = data; + struct client_file find, *cf; + size_t size = datalen - sizeof *msg; + + if (datalen < sizeof *msg) + fatalx("bad MSG_WRITE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) + fatalx("unknown stream number"); + log_debug("write %zu to file %d", size, cf->stream); + + if (cf->event != NULL) + bufferevent_write(cf->event, msg + 1, size); +} + +/* Close client file. */ +static void +client_write_close(void *data, size_t datalen) +{ + struct msg_write_close *msg = data; + struct client_file find, *cf; + + if (datalen != sizeof *msg) + fatalx("bad MSG_WRITE_CLOSE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) + fatalx("unknown stream number"); + log_debug("close file %d", cf->stream); + + if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { + if (cf->event != NULL) + bufferevent_free(cf->event); + if (cf->fd != -1) + close(cf->fd); + RB_REMOVE(client_files, &client_files, cf); + file_free(cf); + } +} + +/* File read callback. */ +static void +client_read_callback(__unused struct bufferevent *bev, void *arg) +{ + struct client_file *cf = arg; + void *bdata; + size_t bsize; + struct msg_read_data *msg; + size_t msglen; + + msg = xmalloc(sizeof *msg); + for (;;) { + bdata = EVBUFFER_DATA(cf->event->input); + bsize = EVBUFFER_LENGTH(cf->event->input); + + if (bsize == 0) + break; + if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) + bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; + log_debug("read %zu from file %d", bsize, cf->stream); + + msglen = (sizeof *msg) + bsize; + msg = xrealloc(msg, msglen); + msg->stream = cf->stream; + memcpy(msg + 1, bdata, bsize); + proc_send(client_peer, MSG_READ, -1, msg, msglen); + + evbuffer_drain(cf->event->input, bsize); + } + free(msg); +} + +/* File read error callback. */ +static void +client_read_error_callback(__unused struct bufferevent *bev, + __unused short what, void *arg) +{ + struct client_file *cf = arg; + struct msg_read_done msg; + + log_debug("read error file %d", cf->stream); + + msg.stream = cf->stream; + msg.error = 0; + proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg); + + bufferevent_free(cf->event); + close(cf->fd); + RB_REMOVE(client_files, &client_files, cf); + file_free(cf); +} + +/* Open read file. */ +static void +client_read_open(void *data, size_t datalen) +{ + struct msg_read_open *msg = data; + const char *path; + struct msg_read_done reply; + struct client_file find, *cf; + const int flags = O_NONBLOCK|O_RDONLY; + int error = 0; + + if (datalen < sizeof *msg) + fatalx("bad MSG_READ_OPEN size"); + if (datalen == sizeof *msg) + path = "-"; + else + path = (const char *)(msg + 1); + log_debug("open read file %d %s", msg->stream, path); + + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { + cf = file_create(NULL, msg->stream, NULL, NULL); + RB_INSERT(client_files, &client_files, cf); + } else { + error = EBADF; + goto reply; + } + if (cf->closed) { + error = EBADF; + goto reply; + } + + cf->fd = -1; + if (msg->fd == -1) + cf->fd = open(path, flags); + else { + if (msg->fd != STDIN_FILENO) + errno = EBADF; + else { + cf->fd = dup(msg->fd); + if (~client_flags & CLIENT_CONTROL) + close(msg->fd); /* can only be used once */ + } + } + if (cf->fd == -1) { + error = errno; + goto reply; + } + + cf->event = bufferevent_new(cf->fd, client_read_callback, NULL, + client_read_error_callback, cf); + bufferevent_enable(cf->event, EV_READ); + return; + +reply: + reply.stream = msg->stream; + reply.error = error; + proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply); } /* Run command in shell; used for -c. */ @@ -555,12 +790,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg) static void client_dispatch_wait(struct imsg *imsg) { - char *data; - ssize_t datalen; - struct msg_stdout_data stdoutdata; - struct msg_stderr_data stderrdata; - int retval; - static int pledge_applied; + char *data; + ssize_t datalen; + int retval; + static int pledge_applied; /* * "sendfd" is no longer required once all of the identify messages @@ -569,10 +802,12 @@ client_dispatch_wait(struct imsg *imsg) * get the first message from the server. */ if (!pledge_applied) { - if (pledge("stdio unix proc exec tty", NULL) != 0) + if (pledge( + "stdio rpath wpath cpath unix proc exec tty", + NULL) != 0) fatal("pledge failed"); pledge_applied = 1; - }; + } data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; @@ -586,38 +821,16 @@ client_dispatch_wait(struct imsg *imsg) memcpy(&retval, data, sizeof retval); client_exitval = retval; } - proc_exit(client_proc); + client_exitflag = 1; + client_exit(); break; case MSG_READY: if (datalen != 0) fatalx("bad MSG_READY size"); - event_del(&client_stdin); client_attached = 1; proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); break; - case MSG_STDIN: - if (datalen != 0) - fatalx("bad MSG_STDIN size"); - - event_add(&client_stdin, NULL); - break; - case MSG_STDOUT: - if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT size"); - memcpy(&stdoutdata, data, sizeof stdoutdata); - - client_write(STDOUT_FILENO, stdoutdata.data, - stdoutdata.size); - break; - case MSG_STDERR: - if (datalen != sizeof stderrdata) - fatalx("bad MSG_STDERR size"); - memcpy(&stderrdata, data, sizeof stderrdata); - - client_write(STDERR_FILENO, stderrdata.data, - stderrdata.size); - break; case MSG_VERSION: if (datalen != 0) fatalx("bad MSG_VERSION size"); @@ -641,6 +854,24 @@ client_dispatch_wait(struct imsg *imsg) case MSG_EXITED: proc_exit(client_proc); break; + case MSG_READ_OPEN: + client_read_open(data, datalen); + break; + case MSG_WRITE_OPEN: + client_write_open(data, datalen); + break; + case MSG_WRITE: + client_write_data(data, datalen); + break; + case MSG_WRITE_CLOSE: + client_write_close(data, datalen); + break; + case MSG_OLDSTDERR: + case MSG_OLDSTDIN: + case MSG_OLDSTDOUT: + fprintf(stderr, "server version is too old for client\n"); + proc_exit(client_proc); + break; } } diff --git a/external/bsd/tmux/dist/cmd-attach-session.c b/external/bsd/tmux/dist/cmd-attach-session.c index f914966f14f4..f0174ac6274b 100644 --- a/external/bsd/tmux/dist/cmd-attach-session.c +++ b/external/bsd/tmux/dist/cmd-attach-session.c @@ -127,6 +127,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); @@ -159,6 +160,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); diff --git a/external/bsd/tmux/dist/cmd-capture-pane.c b/external/bsd/tmux/dist/cmd-capture-pane.c index febfc7ee429c..7af3b113e3ba 100644 --- a/external/bsd/tmux/dist/cmd-capture-pane.c +++ b/external/bsd/tmux/dist/cmd-capture-pane.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", - .args = { "ab:CeE:JpPqS:t:", 0, 0 }, - .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " + .args = { "ab:CeE:JNpPqS:t:", 0, 0 }, + .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct grid *gd; const struct grid_line *gl; struct grid_cell *gc = NULL; - int n, with_codes, escape_c0, join_lines; + int n, with_codes, escape_c0, join_lines, no_trim; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; const char *Sflag, *Eflag; @@ -170,11 +170,12 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, with_codes = args_has(args, 'e'); escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); + no_trim = args_has(args, 'N'); buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, - escape_c0, !join_lines); + escape_c0, !join_lines && !no_trim); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); @@ -192,7 +193,7 @@ static enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; - struct client *c; + struct client *c = item->client; struct window_pane *wp = item->target.wp; char *buf, *cause; const char *bufname; @@ -213,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); if (args_has(args, 'p')) { - c = item->client; - if (c == NULL || - (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { - cmdq_error(item, "can't write to stdout"); + if (!file_can_print(c)) { + cmdq_error(item, "can't write output to client"); free(buf); return (CMD_RETURN_ERROR); } - evbuffer_add(c->stdout_data, buf, len); - free(buf); + file_print_buffer(c, buf, len); if (args_has(args, 'P') && len > 0) - evbuffer_add(c->stdout_data, "\n", 1); - server_client_push_stdout(c); + file_print(c, "\n"); + free(buf); } else { bufname = NULL; if (args_has(args, 'b')) diff --git a/external/bsd/tmux/dist/cmd-load-buffer.c b/external/bsd/tmux/dist/cmd-load-buffer.c index cdf44bf7e146..5e9301269a2a 100644 --- a/external/bsd/tmux/dist/cmd-load-buffer.c +++ b/external/bsd/tmux/dist/cmd-load-buffer.c @@ -33,8 +33,6 @@ static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *); -static void cmd_load_buffer_callback(struct client *, int, void *); - const struct cmd_entry cmd_load_buffer_entry = { .name = "load-buffer", .alias = "loadb", @@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = { struct cmd_load_buffer_data { struct cmdq_item *item; - char *bufname; + char *name; }; +static void +cmd_load_buffer_done(__unused struct client *c, const char *path, int error, + int closed, struct evbuffer *buffer, void *data) +{ + struct cmd_load_buffer_data *cdata = data; + struct cmdq_item *item = cdata->item; + void *bdata = EVBUFFER_DATA(buffer); + size_t bsize = EVBUFFER_LENGTH(buffer); + void *copy; + char *cause; + + if (!closed) + return; + + if (error != 0) + cmdq_error(item, "%s: %s", path, strerror(error)); + else if (bsize != 0) { + copy = xmalloc(bsize); + memcpy(copy, bdata, bsize); + if (paste_set(copy, bsize, cdata->name, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + free(copy); + } + } + cmdq_continue(item); + + free(cdata->name); + free(cdata); +} + static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { @@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; - FILE *f; - const char *bufname; - char *pdata = NULL, *new_pdata, *cause; - char *path, *file; - size_t psize; - int ch, error; + const char *bufname = args_get(args, 'b'); + char *path; - bufname = NULL; - if (args_has(args, 'b')) - bufname = args_get(args, 'b'); + cdata = xmalloc(sizeof *cdata); + cdata->item = item; + if (bufname != NULL) + cdata->name = xstrdup(bufname); + else + cdata->name = NULL; path = format_single(item, args->argv[0], c, s, wl, wp); - if (strcmp(path, "-") == 0) { - free(path); - c = item->client; - - cdata = xcalloc(1, sizeof *cdata); - cdata->item = item; - - if (bufname != NULL) - cdata->bufname = xstrdup(bufname); - - error = server_set_stdin_callback(c, cmd_load_buffer_callback, - cdata, &cause); - if (error != 0) { - cmdq_error(item, "-: %s", cause); - free(cause); - free(cdata); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_WAIT); - } - - file = server_client_get_path(item->client, path); + file_read(item->client, path, cmd_load_buffer_done, cdata); free(path); - f = fopen(file, "rb"); - if (f == NULL) { - cmdq_error(item, "%s: %s", file, strerror(errno)); - goto error; - } - - pdata = NULL; - psize = 0; - while ((ch = getc(f)) != EOF) { - /* Do not let the server die due to memory exhaustion. */ - if ((new_pdata = realloc(pdata, psize + 2)) == NULL) { - cmdq_error(item, "realloc error: %s", strerror(errno)); - goto error; - } - pdata = new_pdata; - pdata[psize++] = ch; - } - if (ferror(f)) { - cmdq_error(item, "%s: read error", file); - goto error; - } - if (pdata != NULL) - pdata[psize] = '\0'; - - fclose(f); - free(file); - - if (paste_set(pdata, psize, bufname, &cause) != 0) { - cmdq_error(item, "%s", cause); - free(pdata); - free(cause); - return (CMD_RETURN_ERROR); - } - - return (CMD_RETURN_NORMAL); - -error: - free(pdata); - if (f != NULL) - fclose(f); - free(file); - return (CMD_RETURN_ERROR); -} - -static void -cmd_load_buffer_callback(struct client *c, int closed, void *data) -{ - struct cmd_load_buffer_data *cdata = data; - char *pdata, *cause, *saved; - size_t psize; - - if (!closed) - return; - c->stdin_callback = NULL; - - server_client_unref(c); - if (c->flags & CLIENT_DEAD) - goto out; - - psize = EVBUFFER_LENGTH(c->stdin_data); - if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) - goto out; - - memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); - pdata[psize] = '\0'; - evbuffer_drain(c->stdin_data, psize); - - if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) { - /* No context so can't use server_client_msg_error. */ - if (~c->flags & CLIENT_UTF8) { - saved = cause; - cause = utf8_sanitize(saved); - free(saved); - } - evbuffer_add_printf(c->stderr_data, "%s", cause); - server_client_push_stderr(c); - free(pdata); - free(cause); - } -out: - cmdq_continue(cdata->item); - - free(cdata->bufname); - free(cdata); + return (CMD_RETURN_WAIT); } diff --git a/external/bsd/tmux/dist/cmd-new-session.c b/external/bsd/tmux/dist/cmd-new-session.c index be6a8565d90b..99b9551dc726 100644 --- a/external/bsd/tmux/dist/cmd-new-session.c +++ b/external/bsd/tmux/dist/cmd-new-session.c @@ -94,26 +94,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - if (args_has(args, 's')) { - newname = format_single(item, args_get(args, 's'), c, NULL, - NULL, NULL); + tmp = args_get(args, 's'); + if (tmp != NULL) { + newname = format_single(item, tmp, c, NULL, NULL, NULL); if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); goto fail; } - if ((as = session_find(newname)) != NULL) { - if (args_has(args, 'A')) { - retval = cmd_attach_session(item, - newname, args_has(args, 'D'), - args_has(args, 'X'), 0, NULL, - args_has(args, 'E')); - free(newname); - return (retval); - } - cmdq_error(item, "duplicate session: %s", newname); - goto fail; + } + if (args_has(args, 'A')) { + if (newname != NULL) + as = session_find(newname); + else + as = item->target.s; + if (as != NULL) { + retval = cmd_attach_session(item, as->name, + args_has(args, 'D'), args_has(args, 'X'), 0, NULL, + args_has(args, 'E')); + free(newname); + return (retval); } } + if (newname != NULL && session_find(newname) != NULL) { + cmdq_error(item, "duplicate session: %s", newname); + goto fail; + } /* Is this going to be part of a session group? */ group = args_get(args, 't'); @@ -259,6 +264,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; @@ -328,7 +334,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - cp = format_single(item, template, c, s, NULL, NULL); + cp = format_single(item, template, c, s, s->curw, NULL); cmdq_print(item, "%s", cp); free(cp); } diff --git a/external/bsd/tmux/dist/cmd-new-window.c b/external/bsd/tmux/dist/cmd-new-window.c index 6cb33dd962c1..2fb92830bc5f 100644 --- a/external/bsd/tmux/dist/cmd-new-window.c +++ b/external/bsd/tmux/dist/cmd-new-window.c @@ -72,6 +72,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; @@ -107,7 +108,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, new_wl, NULL); + cp = format_single(item, template, c, s, new_wl, + new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); } diff --git a/external/bsd/tmux/dist/cmd-parse.y b/external/bsd/tmux/dist/cmd-parse.y index 5070b477771b..a980816c6774 100644 --- a/external/bsd/tmux/dist/cmd-parse.y +++ b/external/bsd/tmux/dist/cmd-parse.y @@ -133,7 +133,12 @@ statements : statement '\n' free($2); } -statement : condition +statement : /* empty */ + { + $$ = xmalloc (sizeof *$$); + TAILQ_INIT($$); + } + | condition { struct cmd_parse_state *ps = &parse_state; @@ -144,11 +149,6 @@ statement : condition cmd_parse_free_commands($1); } } - | assignment - { - $$ = xmalloc (sizeof *$$); - TAILQ_INIT($$); - } | commands { struct cmd_parse_state *ps = &parse_state; @@ -176,26 +176,28 @@ expanded : format struct cmd_parse_input *pi = ps->input; struct format_tree *ft; struct client *c = pi->c; - struct cmd_find_state *fs; + struct cmd_find_state *fsp; + struct cmd_find_state fs; int flags = FORMAT_NOJOBS; if (cmd_find_valid_state(&pi->fs)) - fs = &pi->fs; - else - fs = NULL; + fsp = &pi->fs; + else { + cmd_find_from_client(&fs, c, 0); + fsp = &fs; + } ft = format_create(NULL, pi->item, FORMAT_NONE, flags); - if (fs != NULL) - format_defaults(ft, c, fs->s, fs->wl, fs->wp); - else - format_defaults(ft, c, NULL, NULL, NULL); + format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp); $$ = format_expand(ft, $1); format_free(ft); free($1); } -assignment : /* empty */ - | EQUALS +optional_assignment : /* empty */ + | assignment + +assignment : EQUALS { struct cmd_parse_state *ps = &parse_state; int flags = ps->input->flags; @@ -339,7 +341,8 @@ commands : command struct cmd_parse_state *ps = &parse_state; $$ = cmd_parse_new_commands(); - if (ps->scope == NULL || ps->scope->flag) + if ($1->name != NULL && + (ps->scope == NULL || ps->scope->flag)) TAILQ_INSERT_TAIL($$, $1, entry); else cmd_parse_free_command($1); @@ -358,7 +361,8 @@ commands : command { struct cmd_parse_state *ps = &parse_state; - if (ps->scope == NULL || ps->scope->flag) { + if ($3->name != NULL && + (ps->scope == NULL || ps->scope->flag)) { $$ = $1; TAILQ_INSERT_TAIL($$, $3, entry); } else { @@ -372,7 +376,15 @@ commands : command $$ = $1; } -command : assignment TOKEN +command : assignment + { + struct cmd_parse_state *ps = &parse_state; + + $$ = xcalloc(1, sizeof *$$); + $$->name = NULL; + $$->line = ps->input->line; + } + | optional_assignment TOKEN { struct cmd_parse_state *ps = &parse_state; @@ -381,7 +393,7 @@ command : assignment TOKEN $$->line = ps->input->line; } - | assignment TOKEN arguments + | optional_assignment TOKEN arguments { struct cmd_parse_state *ps = &parse_state; @@ -696,6 +708,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, pr.status = CMD_PARSE_ERROR; pr.error = cmd_parse_get_error(pi->file, line, cause); free(cause); + cmd_list_free(cmdlist); goto out; } cmd_list_append(cmdlist, add); @@ -744,6 +757,12 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) struct cmd_parse_result * cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) +{ + return (cmd_parse_from_buffer(s, strlen(s), pi)); +} + +struct cmd_parse_result * +cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_input input; @@ -756,14 +775,14 @@ cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) } memset(&pr, 0, sizeof pr); - if (*s == '\0') { + if (len == 0) { pr.status = CMD_PARSE_EMPTY; pr.cmdlist = NULL; pr.error = NULL; return (&pr); } - cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause); + cmds = cmd_parse_do_buffer(buf, len, pi, &cause); if (cmds == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; diff --git a/external/bsd/tmux/dist/cmd-queue.c b/external/bsd/tmux/dist/cmd-queue.c index ec1e70841497..dac353515840 100644 --- a/external/bsd/tmux/dist/cmd-queue.c +++ b/external/bsd/tmux/dist/cmd-queue.c @@ -53,7 +53,7 @@ cmdq_get(struct client *c) } /* Append an item. */ -void +struct cmdq_item * cmdq_append(struct client *c, struct cmdq_item *item) { struct cmdq_list *queue = cmdq_get(c); @@ -73,10 +73,11 @@ cmdq_append(struct client *c, struct cmdq_item *item) item = next; } while (item != NULL); + return (TAILQ_LAST(queue, cmdq_list)); } /* Insert an item. */ -void +struct cmdq_item * cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) { struct client *c = after->client; @@ -100,9 +101,9 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) after = item; item = next; } while (item != NULL); + return (after); } - /* Insert a hook. */ void cmdq_insert_hook(struct session *s, struct cmdq_item *item, @@ -144,11 +145,10 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); cmdq_format(new_item, "hook", "%s", name); - if (item != NULL) { - cmdq_insert_after(item, new_item); - item = new_item; - } else - cmdq_append(NULL, new_item); + if (item != NULL) + item = cmdq_insert_after(item, new_item); + else + item = cmdq_append(NULL, new_item); a = options_array_next(a); } @@ -476,13 +476,11 @@ void cmdq_guard(struct cmdq_item *item, const char *guard, int flags) { struct client *c = item->client; + long t = item->time; + u_int number = item->number; - if (c == NULL || !(c->flags & CLIENT_CONTROL)) - return; - - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, - (long)item->time, item->number, flags); - server_client_push_stdout(c); + if (c != NULL && (c->flags & CLIENT_CONTROL)) + file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags); } /* Show message from command. */ @@ -496,29 +494,29 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) char *tmp, *msg; va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + log_debug("%s: %s", __func__, msg); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { - xvasprintf(&tmp, fmt, ap); + tmp = msg; msg = utf8_sanitize(tmp); free(tmp); - evbuffer_add(c->stdout_data, msg, strlen(msg)); - free(msg); - } else - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - evbuffer_add(c->stdout_data, "\n", 1); - server_client_push_stdout(c); + } + file_print(c, "%s\n", msg); } else { wp = c->session->curw->window->active; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, &window_view_mode, NULL, NULL); - window_copy_vadd(wp, fmt, ap); + window_copy_add(wp, "%s", msg); } - va_end(ap); + free(msg); } /* Show error from command. */ @@ -529,11 +527,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) struct cmd *cmd = item->cmd; va_list ap; char *msg; - size_t msglen; char *tmp; va_start(ap, fmt); - msglen = xvasprintf(&msg, fmt, ap); + xvasprintf(&msg, fmt, ap); va_end(ap); log_debug("%s: %s", __func__, msg); @@ -545,11 +542,11 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) tmp = msg; msg = utf8_sanitize(tmp); free(tmp); - msglen = strlen(msg); } - evbuffer_add(c->stderr_data, msg, msglen); - evbuffer_add(c->stderr_data, "\n", 1); - server_client_push_stderr(c); + if (c->flags & CLIENT_CONTROL) + file_print(c, "%s\n", msg); + else + file_error(c, "%s\n", msg); c->retval = 1; } else { *msg = toupper((u_char) *msg); diff --git a/external/bsd/tmux/dist/cmd-resize-window.c b/external/bsd/tmux/dist/cmd-resize-window.c index 9cc74e829fd8..f60e2b8ee8f8 100644 --- a/external/bsd/tmux/dist/cmd-resize-window.c +++ b/external/bsd/tmux/dist/cmd-resize-window.c @@ -53,7 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) const char *errstr; char *cause; u_int adjust, sx, sy; - int xpixel = -1, ypixel = -1; + u_int xpixel = ~0, ypixel = ~0; if (args->argc == 0) adjust = 1; diff --git a/external/bsd/tmux/dist/cmd-send-keys.c b/external/bsd/tmux/dist/cmd-send-keys.c index fe20233515b8..cc04a73ff047 100644 --- a/external/bsd/tmux/dist/cmd-send-keys.c +++ b/external/bsd/tmux/dist/cmd-send-keys.c @@ -33,8 +33,9 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "HlXRMN:t:", 0, -1 }, - .usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", + .args = { "FHlMN:Rt:X", 0, -1 }, + .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE + " key ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -59,6 +60,9 @@ static struct cmdq_item * cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, key_code key) { + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; @@ -67,7 +71,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, if (wme == NULL || wme->mode->key_table == NULL) { if (options_get_number(fs->wp->window->options, "xterm-keys")) key |= KEYC_XTERM; - window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL); + if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0) + return (NULL); return (item); } table = key_bindings_get_table(wme->mode->key_table(wme), 1); @@ -86,6 +91,7 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, struct args *args, int i) { const char *s = args->argv[i]; + struct cmdq_item *new_item; struct utf8_data *ud, *uc; wchar_t wc; key_code key; @@ -103,8 +109,11 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); - if (key != KEYC_NONE && key != KEYC_UNKNOWN) - return (cmd_send_keys_inject_key(c, fs, item, key)); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) { + new_item = cmd_send_keys_inject_key(c, fs, item, key); + if (new_item != NULL) + return (new_item); + } literal = 1; } if (literal) { diff --git a/external/bsd/tmux/dist/cmd-split-window.c b/external/bsd/tmux/dist/cmd-split-window.c index 8d63473ff935..a5fa3accc53e 100644 --- a/external/bsd/tmux/dist/cmd-split-window.c +++ b/external/bsd/tmux/dist/cmd-split-window.c @@ -41,8 +41,7 @@ const struct cmd_entry cmd_split_window_entry = { .args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, .usage = "[-bdefhIPv] [-c start-directory] [-e environment] " - "[-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE - " [command]", + "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -64,20 +63,37 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; struct cmd_find_state fs; int size, percentage, flags, input; - const char *template, *add; - char *cause, *cp; + const char *template, *add, *errstr, *p; + char *cause, *cp, *copy; + size_t plen; struct args_value *value; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; else type = LAYOUT_TOPBOTTOM; - if (args_has(args, 'l')) { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "create pane failed: -l %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if ((p = args_get(args, 'l')) != NULL) { + plen = strlen(p); + if (p[plen - 1] == '%') { + copy = xstrdup(p); + copy[plen - 1] = '\0'; + percentage = strtonum(copy, 0, INT_MAX, &errstr); + free(copy); + if (errstr != NULL) { + cmdq_error(item, "percentage %s", errstr); + return (CMD_RETURN_ERROR); + } + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } else { + size = args_strtonum(args, 'l', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "lines %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); @@ -137,7 +153,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.flags |= SPAWN_DETACHED; if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { - layout_close_pane(new_wp); cmdq_error(item, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); diff --git a/external/bsd/tmux/dist/compat.h b/external/bsd/tmux/dist/compat.h index b7ec5a690495..bb25edc45714 100644 --- a/external/bsd/tmux/dist/compat.h +++ b/external/bsd/tmux/dist/compat.h @@ -371,6 +371,7 @@ int utf8proc_wctomb(char *, wchar_t); #endif /* getopt.c */ +#ifndef HAVE_BSD_GETOPT extern int BSDopterr; extern int BSDoptind; extern int BSDoptopt; @@ -383,5 +384,6 @@ int BSDgetopt(int, char *const *, const char *); #define optopt BSDoptopt #define optreset BSDoptreset #define optarg BSDoptarg +#endif #endif /* COMPAT_H */ diff --git a/external/bsd/tmux/dist/format.c b/external/bsd/tmux/dist/format.c index 7da726dde7c1..c3835f9e961a 100644 --- a/external/bsd/tmux/dist/format.c +++ b/external/bsd/tmux/dist/format.c @@ -456,6 +456,35 @@ format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%ld", (long)getpid()); } +/* Callback for session_attached_list. */ +static void +format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe) +{ + struct session *s = ft->s; + struct client *loop; + struct evbuffer *buffer; + int size; + + if (s == NULL) + return; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session == s) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Callback for session_alerts. */ static void format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) @@ -528,6 +557,128 @@ format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) fe->value = xstrdup("0"); } +/* Callback for window_linked_sessions_list. */ +static void +format_cb_window_linked_sessions_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", wl->session->name); + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for window_active_sessions. */ +static void +format_cb_window_active_sessions(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + u_int n = 0; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->curw == wl) + n++; + } + + xasprintf(&fe->value, "%u", n); +} + +/* Callback for window_active_sessions_list. */ +static void +format_cb_window_active_sessions_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->curw == wl) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", wl->session->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for window_active_clients. */ +static void +format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct client *loop; + struct session *client_session; + u_int n = 0; + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + + if (w == client_session->curw->window) + n++; + } + + xasprintf(&fe->value, "%u", n); +} + +/* Callback for window_active_clients_list. */ +static void +format_cb_window_active_clients_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct client *loop; + struct session *client_session; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + + if (w == client_session->curw->window) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Callback for window_layout. */ static void format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) @@ -574,7 +725,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) struct window_pane *wp = ft->wp; char *cmd; - if (wp == NULL) + if (wp == NULL || wp->shell == NULL) return; cmd = osdep_get_name(wp->fd, wp->tty); @@ -677,11 +828,52 @@ format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); + TAILQ_FOREACH(loop, &sg->sessions, gentry) { if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); evbuffer_add_printf(buffer, "%s", loop->name); } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for session_group_attached_list. */ +static void +format_cb_session_group_attached_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct session *s = ft->s, *client_session, *session_loop; + struct session_group *sg; + struct client *loop; + struct evbuffer *buffer; + int size; + + if (s == NULL) + return; + sg = session_group_contains(s); + if (sg == NULL) + return; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + TAILQ_FOREACH(session_loop, &sg->sessions, gentry) { + if (session_loop == client_session){ + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + } + if ((size = EVBUFFER_LENGTH(buffer)) != 0) xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); @@ -703,6 +895,44 @@ format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%u", n); } +/* Callback for pane_at_top. */ +static void +format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct window *w = wp->window; + int status, flag; + + if (wp == NULL) + return; + + status = options_get_number(w->options, "pane-border-status"); + if (status == PANE_STATUS_TOP) + flag = (wp->yoff == 1); + else + flag = (wp->yoff == 0); + xasprintf(&fe->value, "%d", flag); +} + +/* Callback for pane_at_bottom. */ +static void +format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct window *w = wp->window; + int status, flag; + + if (wp == NULL) + return; + + status = options_get_number(w->options, "pane-border-status"); + if (status == PANE_STATUS_BOTTOM) + flag = (wp->yoff + wp->sy == w->sy - 1); + else + flag = (wp->yoff + wp->sy == w->sy); + xasprintf(&fe->value, "%d", flag); +} + /* Callback for cursor_character. */ static void format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) @@ -718,30 +948,19 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data); } -/* Callback for mouse_word. */ -static void -format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) +/* Return word at given coordinates. Caller frees. */ +char * +format_grid_word(struct grid *gd, u_int x, u_int y) { - struct window_pane *wp; - u_int x, y, end; - struct grid *gd; struct grid_line *gl; struct grid_cell gc; const char *ws; struct utf8_data *ud = NULL; + u_int end; size_t size = 0; int found = 0; + char *s = NULL; - if (!ft->m.valid) - return; - wp = cmd_mouse_pane(&ft->m, NULL, NULL); - if (wp == NULL) - return; - if (!TAILQ_EMPTY (&wp->modes)) - return; - if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) - return; - gd = wp->base.grid; ws = options_get_string(global_s_options, "word-separators"); y = gd->hsize + y; @@ -794,21 +1013,19 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) } if (size != 0) { ud[size].size = 0; - fe->value = utf8_tocstr(ud); + s = utf8_tocstr(ud); free(ud); } + return (s); } -/* Callback for mouse_line. */ +/* Callback for mouse_word. */ static void -format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) +format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; u_int x, y; - struct grid *gd; - struct grid_cell gc; - struct utf8_data *ud = NULL; - size_t size = 0; + char *s; if (!ft->m.valid) return; @@ -819,7 +1036,21 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - gd = wp->base.grid; + + s = format_grid_word(wp->base.grid, x, y); + if (s != NULL) + fe->value = s; +} + +/* Return line at given coordinates. Caller frees. */ +char * +format_grid_line(struct grid *gd, u_int y) +{ + struct grid_cell gc; + struct utf8_data *ud = NULL; + u_int x; + size_t size = 0; + char *s = NULL; y = gd->hsize + y; for (x = 0; x < grid_line_length(gd, y); x++) { @@ -832,9 +1063,33 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) } if (size != 0) { ud[size].size = 0; - fe->value = utf8_tocstr(ud); + s = utf8_tocstr(ud); free(ud); } + return (s); +} + +/* Callback for mouse_line. */ +static void +format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp; + u_int x, y; + char *s; + + if (!ft->m.valid) + return; + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp == NULL) + return; + if (!TAILQ_EMPTY (&wp->modes)) + return; + if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) + return; + + s = format_grid_line(wp->base.grid, y); + if (s != NULL) + fe->value = s; } /* Merge a format tree. */ @@ -904,7 +1159,7 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) ft->flags = flags; ft->time = time(NULL); - format_add(ft, "version", "%s", VERSION); + format_add(ft, "version", "%s", getversion()); format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "pid", format_cb_pid); @@ -949,12 +1204,12 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, void *), void *arg) { struct format_entry *fe; - static char s[64]; + char s[64]; RB_FOREACH(fe, format_entry_tree, &ft->tree) { if (fe->t != 0) { xsnprintf(s, sizeof s, "%lld", (long long)fe->t); - cb(fe->key, fe->value, s); + cb(fe->key, s, arg); } else { if (fe->value == NULL && fe->cb != NULL) { fe->cb(ft, fe); @@ -966,7 +1221,6 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, } } - /* Add a key-value pair. */ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) @@ -998,8 +1252,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) static void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { - struct format_entry *fe; - struct format_entry *fe_now; + struct format_entry *fe, *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); @@ -1290,7 +1543,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=", cp[0]) == NULL) + if (strchr("mCs=p", cp[0]) == NULL) break; c = cp[0]; @@ -1551,15 +1804,15 @@ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; - char *copy0, *condition, *found, *new; - char *value, *left, *right; - size_t valuelen; - int modifiers = 0, limit = 0, j; - struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier *sub = NULL; - u_int i, count; + struct window_pane *wp = ft->wp; + const char *errptr, *copy, *cp, *marker = NULL; + char *copy0, *condition, *found, *new; + char *value, *left, *right; + size_t valuelen; + int modifiers = 0, limit = 0, width = 0, j; + struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; + struct format_modifier **sub = NULL; + u_int i, count, nsub = 0; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); @@ -1588,7 +1841,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 's': if (fm->argc < 2) break; - sub = fm; + sub = xreallocarray (sub, nsub + 1, + sizeof *sub); + sub[nsub++] = fm; break; case '=': if (fm->argc < 1) @@ -1600,6 +1855,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; break; + case 'p': + if (fm->argc < 1) + break; + width = strtonum(fm->argv[0], INT_MIN, INT_MAX, + &errptr); + if (errptr != NULL) + width = 0; + break; case 'l': modifiers |= FORMAT_LITERAL; break; @@ -1800,10 +2063,10 @@ done: } /* Perform substitution if any. */ - if (sub != NULL) { - left = format_expand(ft, sub->argv[0]); - right = format_expand(ft, sub->argv[1]); - new = format_sub(sub, value, left, right); + for (i = 0; i < nsub; i++) { + left = format_expand(ft, sub[i]->argv[0]); + right = format_expand(ft, sub[i]->argv[1]); + new = format_sub(sub[i], value, left, right); format_log(ft, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; @@ -1834,6 +2097,19 @@ done: format_log(ft, "applied length limit %d: %s", limit, value); } + /* Pad the value if needed. */ + if (width > 0) { + new = utf8_padcstr(value, width); + free(value); + value = new; + format_log(ft, "applied padding width %d: %s", width, value); + } else if (width < 0) { + new = utf8_rpadcstr(value, -width); + free(value); + value = new; + format_log(ft, "applied padding width %d: %s", width, value); + } + /* Expand the buffer and copy in the value. */ valuelen = strlen(value); while (*len - *off < valuelen + 1) { @@ -1846,12 +2122,15 @@ done: format_log(ft, "replaced '%s' with '%s'", copy0, value); free(value); + free(sub); format_free_modifiers(list, count); free(copy0); return (0); fail: format_log(ft, "failed %s", copy0); + + free(sub); format_free_modifiers(list, count); free(copy0); return (-1); @@ -2091,8 +2370,14 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_group", "%s", sg->name); format_add(ft, "session_group_size", "%u", session_group_count (sg)); + format_add(ft, "session_group_attached", "%u", + session_group_attached_count (sg)); + format_add(ft, "session_group_many_attached", "%u", + session_group_attached_count (sg) > 1); format_add_cb(ft, "session_group_list", format_cb_session_group_list); + format_add_cb(ft, "session_group_attached_list", + format_cb_session_group_attached_list); } format_add_tv(ft, "session_created", &s->creation_time); @@ -2101,6 +2386,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); + format_add_cb(ft, "session_attached_list", + format_cb_session_attached_list); format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_stack", format_cb_session_stack); @@ -2113,7 +2400,6 @@ format_defaults_client(struct format_tree *ft, struct client *c) struct session *s; const char *name; struct tty *tty = &c->tty; - const char *types[] = TTY_TYPES; if (ft->s == NULL) ft->s = c->session; @@ -2123,14 +2409,14 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", tty->sy); format_add(ft, "client_width", "%u", tty->sx); + format_add(ft, "client_cell_width", "%u", tty->xpixel); + format_add(ft, "client_cell_height", "%u", tty->ypixel); format_add(ft, "client_tty", "%s", c->ttyname); format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); if (tty->term_name != NULL) format_add(ft, "client_termname", "%s", tty->term_name); - if (tty->term_name != NULL) - format_add(ft, "client_termtype", "%s", types[tty->term_type]); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); @@ -2174,6 +2460,8 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); + format_add(ft, "window_cell_width", "%u", w->xpixel); + format_add(ft, "window_cell_height", "%u", w->ypixel); format_add_cb(ft, "window_layout", format_cb_window_layout); format_add_cb(ft, "window_visible_layout", format_cb_window_visible_layout); @@ -2211,12 +2499,25 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); format_add(ft, "window_flags", "%s", window_printable_flags(wl)); format_add(ft, "window_active", "%d", wl == s->curw); + format_add_cb(ft, "window_active_sessions", + format_cb_window_active_sessions); + format_add_cb(ft, "window_active_sessions_list", + format_cb_window_active_sessions_list); + format_add_cb(ft, "window_active_clients", + format_cb_window_active_clients); + format_add_cb(ft, "window_active_clients_list", + format_cb_window_active_clients_list); format_add(ft, "window_start_flag", "%d", !!(wl == RB_MIN(winlinks, &s->windows))); format_add(ft, "window_end_flag", "%d", !!(wl == RB_MAX(winlinks, &s->windows))); + if (server_check_marked() && marked_pane.wl == wl) + format_add(ft, "window_marked_flag", "1"); + else + format_add(ft, "window_marked_flag", "0"); + format_add(ft, "window_bell_flag", "%d", !!(wl->flags & WINLINK_BELL)); format_add(ft, "window_activity_flag", "%d", @@ -2226,6 +2527,11 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); + + format_add_cb(ft, "window_linked_sessions_list", + format_cb_window_linked_sessions_list); + format_add(ft, "window_linked_sessions", "%u", + wl->window->references); } /* Set default format keys for a window pane. */ @@ -2253,6 +2559,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); + if (wp->base.path != NULL) + format_add(ft, "pane_path", "%s", wp->base.path); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == w->active); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); @@ -2276,9 +2584,9 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); format_add(ft, "pane_at_left", "%d", wp->xoff == 0); - format_add(ft, "pane_at_top", "%d", wp->yoff == 0); + format_add_cb(ft, "pane_at_top", format_cb_pane_at_top); format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); - format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == w->sy); + format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { diff --git a/external/bsd/tmux/dist/grid.c b/external/bsd/tmux/dist/grid.c index 6ea65df4bcef..1289104057ef 100644 --- a/external/bsd/tmux/dist/grid.c +++ b/external/bsd/tmux/dist/grid.c @@ -186,23 +186,25 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) struct grid_cell *gc; memcpy(gce, &grid_cleared_entry, sizeof *gce); - if (bg & COLOUR_FLAG_RGB) { - grid_get_extended_cell(gl, gce, gce->flags); - gl->flags |= GRID_LINE_EXTENDED; + if (bg != 8) { + if (bg & COLOUR_FLAG_RGB) { + grid_get_extended_cell(gl, gce, gce->flags); + gl->flags |= GRID_LINE_EXTENDED; - gc = &gl->extddata[gce->offset]; - memcpy(gc, &grid_cleared_cell, sizeof *gc); - gc->bg = bg; - } else { - if (bg & COLOUR_FLAG_256) - gce->flags |= GRID_FLAG_BG256; - gce->data.bg = bg; + gc = &gl->extddata[gce->offset]; + memcpy(gc, &grid_cleared_cell, sizeof *gc); + gc->bg = bg; + } else { + if (bg & COLOUR_FLAG_256) + gce->flags |= GRID_FLAG_BG256; + gce->data.bg = bg; + } } } /* Check grid y position. */ static int -grid_check_y(struct grid *gd, const char* from, u_int py) +grid_check_y(struct grid *gd, const char *from, u_int py) { if (py >= gd->hsize + gd->sy) { log_debug("%s: y out of range: %u", from, py); diff --git a/external/bsd/tmux/dist/input-keys.c b/external/bsd/tmux/dist/input-keys.c index 6fbde6b5b43b..f6b2789d9e89 100644 --- a/external/bsd/tmux/dist/input-keys.c +++ b/external/bsd/tmux/dist/input-keys.c @@ -42,9 +42,6 @@ struct input_key_ent { }; static const struct input_key_ent input_keys[] = { - /* Backspace key. */ - { KEYC_BSPACE, "\177", 0 }, - /* Paste keys. */ { KEYC_PASTE_START, "\033[200~", 0 }, { KEYC_PASTE_END, "\033[201~", 0 }, @@ -152,14 +149,14 @@ input_split2(u_int c, u_char *dst) } /* Translate a key code into an output key sequence. */ -void +int input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { const struct input_key_ent *ike; u_int i; size_t dlen; char *out; - key_code justkey; + key_code justkey, newkey; struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, @@ -169,14 +166,22 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); - return; + return (0); } /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; bufferevent_write(wp->event, &ud.data[0], 1); - return; + return (0); + } + + /* Is this backspace? */ + if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) { + newkey = options_get_number(global_options, "backspace"); + if (newkey >= 0x7f) + newkey = '\177'; + key = newkey|(key & KEYC_MASK_MOD); } /* @@ -189,15 +194,15 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); - return; + return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) - return; + return (-1); if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ud.data, ud.size); - return; + return (0); } /* @@ -208,7 +213,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); - return; + return (0); } } key &= ~KEYC_XTERM; @@ -231,7 +236,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) } if (i == nitems(input_keys)) { log_debug("key 0x%llx missing", key); - return; + return (-1); } dlen = strlen(ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data); @@ -240,6 +245,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ike->data, dlen); + return (0); } /* Translate mouse and output. */ diff --git a/external/bsd/tmux/dist/input.c b/external/bsd/tmux/dist/input.c index e8858100966f..ca25cae9f586 100644 --- a/external/bsd/tmux/dist/input.c +++ b/external/bsd/tmux/dist/input.c @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -740,7 +741,7 @@ input_timer_callback(__unused int fd, __unused short events, void *arg) static void input_start_timer(struct input_ctx *ictx) { - struct timeval tv = { .tv_usec = 100000 }; + struct timeval tv = { .tv_sec = 5, .tv_usec = 0 }; event_del(&ictx->timer); event_add(&ictx->timer, &tv); @@ -772,6 +773,7 @@ input_save_state(struct input_ctx *ictx) ictx->old_mode = s->mode; } +/* Restore screen state. */ static void input_restore_state(struct input_ctx *ictx) { @@ -876,7 +878,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) void input_parse(struct window_pane *wp) { - struct evbuffer *evb = wp->event->input; + struct evbuffer *evb = wp->event->input; input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); @@ -888,7 +890,8 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) { struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; - const struct input_transition *itr; + const struct input_state *state = NULL; + const struct input_transition *itr = NULL; size_t off = 0; if (len == 0) @@ -916,16 +919,23 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) ictx->ch = buf[off++]; /* Find the transition. */ - itr = ictx->state->transitions; - while (itr->first != -1 && itr->last != -1) { - if (ictx->ch >= itr->first && ictx->ch <= itr->last) - break; - itr++; - } - if (itr->first == -1 || itr->last == -1) { - /* No transition? Eh? */ - fatalx("no transition from state"); + if (ictx->state != state || + itr == NULL || + ictx->ch < itr->first || + ictx->ch > itr->last) { + itr = ictx->state->transitions; + while (itr->first != -1 && itr->last != -1) { + if (ictx->ch >= itr->first && + ictx->ch <= itr->last) + break; + itr++; + } + if (itr->first == -1 || itr->last == -1) { + /* No transition? Eh? */ + fatalx("no transition from state"); + } } + state = ictx->state; /* * Any state except print stops the current collection. This is @@ -1293,6 +1303,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; + char *copy, *cp; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1424,6 +1435,13 @@ input_csi_dispatch(struct input_ctx *ictx) case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; + case 1337: /* Terminal version, from iTerm2. */ + copy = xstrdup(getversion()); + for (cp = copy; *cp != '\0'; cp++) + *cp = toupper((u_char)*cp); + input_reply(ictx, "\033[TMUX %sn", copy); + free(copy); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1911,8 +1929,13 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) free(copy); return; } - } else + } else { n++; + if (n == nitems(p)) { + free(copy); + return; + } + } log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]); } free(copy); @@ -2195,14 +2218,18 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - if (utf8_isvalid(p)) { - screen_set_title(sctx->s, p); + if (screen_set_title(sctx->s, p)) server_status_window(ictx->wp->window); - } break; case 4: input_osc_4(ictx, p); break; + case 7: + if (utf8_isvalid(p)) { + screen_set_path(sctx->s, p); + server_status_window(ictx->wp->window); + } + break; case 10: input_osc_10(ictx, p); break; @@ -2251,10 +2278,8 @@ input_exit_apc(struct input_ctx *ictx) return; log_debug("%s: \"%s\"", __func__, p); - if (!utf8_isvalid(p)) - return; - screen_set_title(sctx->s, p); - server_status_window(ictx->wp->window); + if (screen_set_title(sctx->s, p)) + server_status_window(ictx->wp->window); } /* Rename string started. */ @@ -2272,7 +2297,10 @@ input_enter_rename(struct input_ctx *ictx) static void input_exit_rename(struct input_ctx *ictx) { + struct window_pane *wp = ictx->wp; + struct options_entry *oe; char *p = (char *)ictx->input_buf; + if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(ictx->wp->options, "allow-rename")) @@ -2281,6 +2309,13 @@ input_exit_rename(struct input_ctx *ictx) if (!utf8_isvalid(p)) return; + + if (ictx->input_len == 0) { + oe = options_get_only(wp->window->options, "automatic-rename"); + if (oe != NULL) + options_remove(oe); + return; + } window_set_name(ictx->wp->window, p); options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); @@ -2322,6 +2357,54 @@ input_top_bit_set(struct input_ctx *ictx) return (0); } +/* Parse colour from OSC. */ +static int +input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) +{ + u_int rsize, gsize, bsize; + const char *cp, *s = p; + + if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3) + return (0); + p += 4; + + cp = strchr(p, '/'); + rsize = cp - p; + if (rsize == 1) + (*r) = (*r) | ((*r) << 4); + else if (rsize == 3) + (*r) >>= 4; + else if (rsize == 4) + (*r) >>= 8; + else if (rsize != 2) + return (0); + + p = cp + 1; + cp = strchr(p, '/'); + gsize = cp - p; + if (gsize == 1) + (*g) = (*g) | ((*g) << 4); + else if (gsize == 3) + (*g) >>= 4; + else if (gsize == 4) + (*g) >>= 8; + else if (gsize != 2) + return (0); + + bsize = strlen(cp + 1); + if (bsize == 1) + (*b) = (*b) | ((*b) << 4); + else if (bsize == 3) + (*b) >>= 4; + else if (bsize == 4) + (*b) >>= 8; + else if (bsize != 2) + return (0); + + log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b); + return (1); +} + /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void input_osc_4(struct input_ctx *ictx, const char *p) @@ -2340,7 +2423,7 @@ input_osc_4(struct input_ctx *ictx, const char *p) goto bad; s = strsep(&next, ";"); - if (sscanf(s, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) { + if (!input_osc_parse_colour(s, &r, &g, &b)) { s = next; continue; } @@ -2365,8 +2448,11 @@ input_osc_10(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; - if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) - goto bad; + if (strcmp(p, "?") == 0) + return; + + if (!input_osc_parse_colour(p, &r, &g, &b)) + goto bad; xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); options_set_style(wp->options, "window-style", 1, tmp); options_set_style(wp->options, "window-active-style", 1, tmp); @@ -2386,7 +2472,10 @@ input_osc_11(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; - if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) + if (strcmp(p, "?") == 0) + return; + + if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); options_set_style(wp->options, "window-style", 1, tmp); diff --git a/external/bsd/tmux/dist/key-bindings.c b/external/bsd/tmux/dist/key-bindings.c index 43d4f7825722..790d91ab8dfa 100644 --- a/external/bsd/tmux/dist/key-bindings.c +++ b/external/bsd/tmux/dist/key-bindings.c @@ -24,12 +24,6 @@ #include "tmux.h" -#define DEFAULT_CLIENT_MENU \ - " 'Detach' 'd' {detach-client}" \ - " 'Detach & Kill' 'X' {detach-client -P}" \ - " 'Detach Others' 'o' {detach-client -a}" \ - " ''" \ - " 'Lock' 'l' {lock-client}" #define DEFAULT_SESSION_MENU \ " 'Next' 'n' {switch-client -n}" \ " 'Previous' 'p' {switch-client -p}" \ @@ -69,7 +63,6 @@ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" - static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); static int key_table_cmp(struct key_table *, struct key_table *); @@ -92,6 +85,15 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) return (0); } +static void +key_bindings_free(struct key_table *table, struct key_binding *bd) +{ + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(__UNCONST(bd->note)); + free(bd); +} + struct key_table * key_bindings_get_table(const char *name, int create) { @@ -133,11 +135,8 @@ key_bindings_unref_table(struct key_table *table) if (--table->references != 0) return; - RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) + key_bindings_free(table, bd); free(__UNCONST(table->name)); free(table); @@ -165,24 +164,22 @@ key_bindings_next(__unused struct key_table *table, struct key_binding *bd) } void -key_bindings_add(const char *name, key_code key, int repeat, +key_bindings_add(const char *name, key_code key, const char *note, int repeat, struct cmd_list *cmdlist) { struct key_table *table; - struct key_binding bd_find, *bd; + struct key_binding *bd; table = key_bindings_get_table(name, 1); - bd_find.key = (key & ~KEYC_XTERM); - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); - if (bd != NULL) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + bd = key_bindings_get(table, key & ~KEYC_XTERM); + if (bd != NULL) + key_bindings_free(table, bd); bd = xcalloc(1, sizeof *bd); bd->key = key; + if (note != NULL) + bd->note = xstrdup(note); RB_INSERT(key_bindings, &table->key_bindings, bd); if (repeat) @@ -194,20 +191,16 @@ void key_bindings_remove(const char *name, key_code key) { struct key_table *table; - struct key_binding bd_find, *bd; + struct key_binding *bd; table = key_bindings_get_table(name, 0); if (table == NULL) return; - bd_find.key = (key & ~KEYC_XTERM); - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd == NULL) return; - - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); + key_bindings_free(table, bd); if (RB_EMPTY(&table->key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); @@ -236,87 +229,88 @@ void key_bindings_init(void) { static const char *defaults[] = { - "bind C-b send-prefix", - "bind C-o rotate-window", - "bind C-z suspend-client", - "bind Space next-layout", - "bind ! break-pane", - "bind '\"' split-window", - "bind '#' list-buffers", - "bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"", - "bind % split-window -h", - "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", - "bind ( switch-client -p", - "bind ) switch-client -n", - "bind , command-prompt -I'#W' \"rename-window -- '%%'\"", - "bind - delete-buffer", - "bind . command-prompt \"move-window -t '%%'\"", - "bind 0 select-window -t:=0", - "bind 1 select-window -t:=1", - "bind 2 select-window -t:=2", - "bind 3 select-window -t:=3", - "bind 4 select-window -t:=4", - "bind 5 select-window -t:=5", - "bind 6 select-window -t:=6", - "bind 7 select-window -t:=7", - "bind 8 select-window -t:=8", - "bind 9 select-window -t:=9", - "bind : command-prompt", - "bind \\; last-pane", - "bind = choose-buffer -Z", - "bind ? list-keys", - "bind D choose-client -Z", - "bind E select-layout -E", - "bind L switch-client -l", - "bind M select-pane -M", - "bind [ copy-mode", - "bind ] paste-buffer", - "bind c new-window", - "bind d detach-client", - "bind f command-prompt \"find-window -Z -- '%%'\"", - "bind i display-message", - "bind l last-window", - "bind m select-pane -m", - "bind n next-window", - "bind o select-pane -t:.+", - "bind p previous-window", - "bind q display-panes", - "bind r refresh-client", - "bind s choose-tree -Zs", - "bind t clock-mode", - "bind w choose-tree -Zw", - "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", - "bind z resize-pane -Z", - "bind '{' swap-pane -U", - "bind '}' swap-pane -D", - "bind '~' show-messages", - "bind PPage copy-mode -u", - "bind -r Up select-pane -U", - "bind -r Down select-pane -D", - "bind -r Left select-pane -L", - "bind -r Right select-pane -R", - "bind M-1 select-layout even-horizontal", - "bind M-2 select-layout even-vertical", - "bind M-3 select-layout main-horizontal", - "bind M-4 select-layout main-vertical", - "bind M-5 select-layout tiled", - "bind M-n next-window -a", - "bind M-o rotate-window -D", - "bind M-p previous-window -a", - "bind -r S-Up refresh-client -U 10", - "bind -r S-Down refresh-client -D 10", - "bind -r S-Left refresh-client -L 10", - "bind -r S-Right refresh-client -R 10", - "bind -r DC refresh-client -c", - "bind -r M-Up resize-pane -U 5", - "bind -r M-Down resize-pane -D 5", - "bind -r M-Left resize-pane -L 5", - "bind -r M-Right resize-pane -R 5", - "bind -r C-Up resize-pane -U", - "bind -r C-Down resize-pane -D", - "bind -r C-Left resize-pane -L", - "bind -r C-Right resize-pane -R", + "bind -N 'Send the prefix key' C-b send-prefix", + "bind -N 'Rotate through the panes' C-o rotate-window", + "bind -N 'Suspend the current client' C-z suspend-client", + "bind -N 'Select next layout' Space next-layout", + "bind -N 'Break pane to a new window' ! break-pane", + "bind -N 'Split window vertically' '\"' split-window", + "bind -N 'List all paste buffers' '#' list-buffers", + "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", + "bind -N 'Split window horizontally' % split-window -h", + "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"", + "bind -N 'Switch to previous client' ( switch-client -p", + "bind -N 'Switch to next client' ) switch-client -n", + "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", + "bind -N 'Delete the most recent paste buffer' - delete-buffer", + "bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"", + "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", + "bind -N 'Select window 0' 0 select-window -t:=0", + "bind -N 'Select window 1' 1 select-window -t:=1", + "bind -N 'Select window 2' 2 select-window -t:=2", + "bind -N 'Select window 3' 3 select-window -t:=3", + "bind -N 'Select window 4' 4 select-window -t:=4", + "bind -N 'Select window 5' 5 select-window -t:=5", + "bind -N 'Select window 6' 6 select-window -t:=6", + "bind -N 'Select window 7' 7 select-window -t:=7", + "bind -N 'Select window 8' 8 select-window -t:=8", + "bind -N 'Select window 9' 9 select-window -t:=9", + "bind -N 'Prompt for a command' : command-prompt", + "bind -N 'Move to the previously active pane' \\; last-pane", + "bind -N 'Choose a paste buffer from a list' = choose-buffer -Z", + "bind -N 'List key bindings' ? list-keys -N", + "bind -N 'Choose a client from a list' D choose-client -Z", + "bind -N 'Spread panes out evenly' E select-layout -E", + "bind -N 'Switch to the last client' L switch-client -l", + "bind -N 'Clear the marked pane' M select-pane -M", + "bind -N 'Enter copy mode' [ copy-mode", + "bind -N 'Paste the most recent paste buffer' ] paste-buffer", + "bind -N 'Create a new window' c new-window", + "bind -N 'Detach the current client' d detach-client", + "bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"", + "bind -N 'Display window information' i display-message", + "bind -N 'Select the previously current window' l last-window", + "bind -N 'Toggle the marked pane' m select-pane -m", + "bind -N 'Select the next window' n next-window", + "bind -N 'Select the next pane' o select-pane -t:.+", + "bind -N 'Select the previous pane' p previous-window", + "bind -N 'Display pane numbers' q display-panes", + "bind -N 'Redraw the current client' r refresh-client", + "bind -N 'Choose a session from a list' s choose-tree -Zs", + "bind -N 'Show a clock' t clock-mode", + "bind -N 'Choose a window from a list' w choose-tree -Zw", + "bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", + "bind -N 'Zoom the active pane' z resize-pane -Z", + "bind -N 'Swap the active pane with the pane above' '{' swap-pane -U", + "bind -N 'Swap the active pane with the pane below' '}' swap-pane -D", + "bind -N 'Show messages' '~' show-messages", + "bind -N 'Enter copy mode and scroll up' PPage copy-mode -u", + "bind -N 'Select the pane above the active pane' -r Up select-pane -U", + "bind -N 'Select the pane below the active pane' -r Down select-pane -D", + "bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L", + "bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R", + "bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal", + "bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical", + "bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal", + "bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical", + "bind -N 'Select the tiled layout' M-5 select-layout tiled", + "bind -N 'Select the next window with an alert' M-n next-window -a", + "bind -N 'Rotate through the panes in reverse' M-o rotate-window -D", + "bind -N 'Select the previous window with an alert' M-p previous-window -a", + "bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10", + "bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10", + "bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10", + "bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10", + "bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c", + "bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5", + "bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5", + "bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5", + "bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5", + "bind -N 'Resize the pane up' -r C-Up resize-pane -U", + "bind -N 'Resize the pane down' -r C-Down resize-pane -D", + "bind -N 'Resize the pane left' -r C-Left resize-pane -L", + "bind -N 'Resize the pane right' -r C-Right resize-pane -R", "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", @@ -326,7 +320,6 @@ key_bindings_init(void) "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", - "bind -n MouseDown3StatusRight display-menu -t= -xM -yS -T \"#[align=centre]#{client_name}\" " DEFAULT_CLIENT_MENU, "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU, "bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, "bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, @@ -403,6 +396,8 @@ key_bindings_init(void) "bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Down send -X scroll-down", + "bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'", + "bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi C-c send -X cancel", "bind -Tcopy-mode-vi C-d send -X halfpage-down", "bind -Tcopy-mode-vi C-e send -X scroll-down", @@ -509,12 +504,16 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, struct cmdq_item *new_item; int readonly; - readonly = 1; - TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { - if (!(cmd->entry->flags & CMD_READONLY)) - readonly = 0; + if (c == NULL || (~c->flags & CLIENT_READONLY)) + readonly = 1; + else { + readonly = 1; + TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { + if (~cmd->entry->flags & CMD_READONLY) + readonly = 0; + } } - if (!readonly && (c->flags & CLIENT_READONLY)) + if (!readonly) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); @@ -522,8 +521,8 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, new_item->shared->flags |= CMDQ_SHARED_REPEAT; } if (item != NULL) - cmdq_insert_after(item, new_item); + new_item = cmdq_insert_after(item, new_item); else - cmdq_append(c, new_item); + new_item = cmdq_append(c, new_item); return (new_item); } diff --git a/external/bsd/tmux/dist/log.c b/external/bsd/tmux/dist/log.c index 41821835cef3..c281d8c9d025 100644 --- a/external/bsd/tmux/dist/log.c +++ b/external/bsd/tmux/dist/log.c @@ -136,6 +136,9 @@ log_debug(const char *msg, ...) { va_list ap; + if (log_file == NULL) + return; + va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); diff --git a/external/bsd/tmux/dist/mode-tree.c b/external/bsd/tmux/dist/mode-tree.c index 12df83051f20..5bf5211809bf 100644 --- a/external/bsd/tmux/dist/mode-tree.c +++ b/external/bsd/tmux/dist/mode-tree.c @@ -39,7 +39,7 @@ struct mode_tree_data { const char **sort_list; u_int sort_size; - u_int sort_type; + struct mode_tree_sort_criteria sort_crit; mode_tree_build_cb buildcb; mode_tree_draw_cb drawcb; @@ -334,7 +334,6 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->sort_list = sort_list; mtd->sort_size = sort_size; - mtd->sort_type = 0; mtd->preview = !args_has(args, 'N'); @@ -342,9 +341,10 @@ mode_tree_start(struct window_pane *wp, struct args *args, if (sort != NULL) { for (i = 0; i < sort_size; i++) { if (strcasecmp(sort, sort_list[i]) == 0) - mtd->sort_type = i; + mtd->sort_crit.field = i; } } + mtd->sort_crit.reversed = args_has(args, 'r'); if (args_has(args, 'f')) mtd->filter = xstrdup(args_get(args, 'f')); @@ -392,10 +392,10 @@ mode_tree_build(struct mode_tree_data *mtd) TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_INIT(&mtd->children); - mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); + mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter); mtd->no_matches = TAILQ_EMPTY(&mtd->children); if (mtd->no_matches) - mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); + mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL); mode_tree_free_items(&mtd->saved); TAILQ_INIT(&mtd->saved); @@ -598,6 +598,8 @@ mode_tree_draw(struct mode_tree_data *mtd) xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name, tag); width = utf8_cstrwidth(text); + if (width > w) + width = w; free(start); if (mti->tagged) { @@ -607,11 +609,11 @@ mode_tree_draw(struct mode_tree_data *mtd) if (i != mtd->current) { screen_write_clearendofline(&ctx, 8); - screen_write_puts(&ctx, &gc0, "%s", text); + screen_write_nputs(&ctx, w, &gc0, "%s", text); format_draw(&ctx, &gc0, w - width, mti->text, NULL); } else { screen_write_clearendofline(&ctx, gc.bg); - screen_write_puts(&ctx, &gc, "%s", text); + screen_write_nputs(&ctx, w, &gc, "%s", text); format_draw(&ctx, &gc, w - width, mti->text, NULL); } free(text); @@ -634,8 +636,9 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); - xasprintf(&text, " %s (sort: %s)", mti->name, - mtd->sort_list[mtd->sort_type]); + xasprintf(&text, " %s (sort: %s%s)", mti->name, + mtd->sort_list[mtd->sort_crit.field], + mtd->sort_crit.reversed ? ", reversed" : ""); if (w - 2 >= strlen(text)) { screen_write_cursormove(&ctx, 1, h, 0); screen_write_puts(&ctx, &gc0, "%s", text); @@ -993,9 +996,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, } break; case 'O': - mtd->sort_type++; - if (mtd->sort_type == mtd->sort_size) - mtd->sort_type = 0; + mtd->sort_crit.field++; + if (mtd->sort_crit.field == mtd->sort_size) + mtd->sort_crit.field = 0; + mode_tree_build(mtd); + break; + case 'r': + mtd->sort_crit.reversed = !mtd->sort_crit.reversed; mode_tree_build(mtd); break; case KEYC_LEFT: diff --git a/external/bsd/tmux/dist/notify.c b/external/bsd/tmux/dist/notify.c index 66c56f7a9305..afbebc0e58db 100644 --- a/external/bsd/tmux/dist/notify.c +++ b/external/bsd/tmux/dist/notify.c @@ -89,9 +89,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); cmdq_format(new_item, "hook", "%s", ne->name); notify_hook_formats(new_item, s, w, ne->pane); - - cmdq_insert_after(item, new_item); - item = new_item; + item = cmdq_insert_after(item, new_item); a = options_array_next(a); } diff --git a/external/bsd/tmux/dist/options.c b/external/bsd/tmux/dist/options.c index e9e08f4a2cdc..a5782df3de33 100644 --- a/external/bsd/tmux/dist/options.c +++ b/external/bsd/tmux/dist/options.c @@ -296,6 +296,7 @@ options_remove(struct options_entry *o) else options_value_free(o, &o->value); RB_REMOVE(options_tree, &oo->tree, o); + free(__UNCONST(o->name)); free(o); } @@ -320,6 +321,17 @@ options_array_item(struct options_entry *o, u_int idx) return (RB_FIND(options_array, &o->value.array, &a)); } +static struct options_array_item * +options_array_new(struct options_entry *o, u_int idx) +{ + struct options_array_item *a; + + a = xcalloc(1, sizeof *a); + a->index = idx; + RB_INSERT(options_array, &o->value.array, a); + return (a); +} + static void options_array_free(struct options_entry *o, struct options_array_item *a) { @@ -367,7 +379,14 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (-1); } - if (OPTIONS_IS_COMMAND(o) && value != NULL) { + if (value == NULL) { + a = options_array_item(o, idx); + if (a != NULL) + options_array_free(o, a); + return (0); + } + + if (OPTIONS_IS_COMMAND(o)) { pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: @@ -383,34 +402,33 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, case CMD_PARSE_SUCCESS: break; } - } - a = options_array_item(o, idx); - if (value == NULL) { - if (a != NULL) - options_array_free(o, a); + a = options_array_item(o, idx); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.cmdlist = pr->cmdlist; return (0); } if (OPTIONS_IS_STRING(o)) { + a = options_array_item(o, idx); if (a != NULL && append) xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.string = new; + return (0); } - if (a == NULL) { - a = xcalloc(1, sizeof *a); - a->index = idx; - RB_INSERT(options_array, &o->value.array, a); - } else - options_value_free(o, &a->value); - - if (OPTIONS_IS_STRING(o)) - a->value.string = new; - else if (OPTIONS_IS_COMMAND(o)) - a->value.cmdlist = pr->cmdlist; - return (0); + if (cause != NULL) + *cause = xstrdup("wrong array type"); + return (-1); } int @@ -592,7 +610,7 @@ options_match(const char *s, int *idx, int *ambiguous) struct options_entry * options_match_get(struct options *oo, const char *s, int *idx, int only, - int* ambiguous) + int *ambiguous) { char *name; struct options_entry *o; diff --git a/external/bsd/tmux/dist/proc.c b/external/bsd/tmux/dist/proc.c index a851d9a10bd7..b64ab7717e77 100644 --- a/external/bsd/tmux/dist/proc.c +++ b/external/bsd/tmux/dist/proc.c @@ -181,7 +181,7 @@ proc_start(const char *name) memset(&u, 0, sizeof u); log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, - (long)getpid(), VERSION, socket_path, PROTOCOL_VERSION); + (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, u.version, event_get_version(), event_get_method()); diff --git a/external/bsd/tmux/dist/regsub.c b/external/bsd/tmux/dist/regsub.c index 966f27524ae8..372bbdcbb1a7 100644 --- a/external/bsd/tmux/dist/regsub.c +++ b/external/bsd/tmux/dist/regsub.c @@ -107,6 +107,12 @@ regsub(const char *pattern, const char *with, const char *text, int flags) start += m[0].rm_eo + 1; empty = 1; } + + /* Stop now if anchored to start. */ + if (*pattern == '^') { + regsub_copy(&buf, &len, text, start, end); + break; + } } buf[len] = '\0'; diff --git a/external/bsd/tmux/dist/resize.c b/external/bsd/tmux/dist/resize.c index 96d733f05b2e..baede8188e71 100644 --- a/external/bsd/tmux/dist/resize.c +++ b/external/bsd/tmux/dist/resize.c @@ -230,7 +230,7 @@ recalculate_size(struct window *w) { struct session *s; struct client *c; - u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n; + u_int sx = 0, sy = 0, cx, cy, xpixel = 0, ypixel = 0, n; int type, current, has, changed; if (w->active == NULL) diff --git a/external/bsd/tmux/dist/screen-write.c b/external/bsd/tmux/dist/screen-write.c index a5813c66ec0f..6ac959f02914 100644 --- a/external/bsd/tmux/dist/screen-write.c +++ b/external/bsd/tmux/dist/screen-write.c @@ -100,7 +100,6 @@ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - char tmp[32]; u_int y; memset(ctx, 0, sizeof *ctx); @@ -119,12 +118,17 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, ctx->scrolled = 0; ctx->bg = 8; - if (wp != NULL) { - snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, - wp->xoff, wp->yoff); + if (log_get_level() != 0) { + if (wp != NULL) { + log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", + __func__, screen_size_x(ctx->s), + screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff); + } else { + log_debug("%s: size %ux%u, no pane", + __func__, screen_size_x(ctx->s), + screen_size_y(ctx->s)); + } } - log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); } /* Finish writing. */ @@ -1234,7 +1238,6 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->list[y + 1]; TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); - TAILQ_INIT(&cl->items); } } @@ -1323,8 +1326,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } - memcpy(&gc, &ci->gc, sizeof gc); - grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); + grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { @@ -1348,8 +1350,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx, /* * Don't need to check that the attributes and whatnot are still the * same - input_parse will end the collection when anything that isn't - * a plain character is encountered. Also nothing should make it here - * that isn't a single ASCII character. + * a plain character is encountered. */ collect = 1; @@ -1635,7 +1636,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, grid_view_get_cell(gd, xx, s->cy, &tmp_gc); if (~tmp_gc.flags & GRID_FLAG_PADDING) break; - log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); + log_debug("%s: overwrite at %u,%u", __func__, xx, + s->cy); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); done = 1; } diff --git a/external/bsd/tmux/dist/screen.c b/external/bsd/tmux/dist/screen.c index 9b67e7e889fc..fafb3456f241 100644 --- a/external/bsd/tmux/dist/screen.c +++ b/external/bsd/tmux/dist/screen.c @@ -151,11 +151,22 @@ screen_set_cursor_colour(struct screen *s, const char *colour) } /* Set screen title. */ -void +int screen_set_title(struct screen *s, const char *title) { + if (!utf8_isvalid(title)) + return (0); free(s->title); - utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + s->title = xstrdup(title); + return (1); +} + +/* Set screen path. */ +void +screen_set_path(struct screen *s, const char *path) +{ + free(s->path); + utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); } /* Push the current title onto the stack. */ diff --git a/external/bsd/tmux/dist/server-client.c b/external/bsd/tmux/dist/server-client.c index 8e329699c77d..b648acbdecc9 100644 --- a/external/bsd/tmux/dist/server-client.c +++ b/external/bsd/tmux/dist/server-client.c @@ -42,11 +42,18 @@ static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); static void server_client_clear_overlay(struct client *); +static void server_client_resize_event(int, short, void *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_shell(struct client *); +static void server_client_dispatch_write_ready(struct client *, + struct imsg *); +static void server_client_dispatch_read_data(struct client *, + struct imsg *); +static void server_client_dispatch_read_done(struct client *, + struct imsg *); /* Number of attached clients. */ u_int @@ -194,16 +201,6 @@ server_client_create(int fd) TAILQ_INIT(&c->queue); - c->stdin_data = evbuffer_new(); - if (c->stdin_data == NULL) - fatalx("out of memory"); - c->stdout_data = evbuffer_new(); - if (c->stdout_data == NULL) - fatalx("out of memory"); - c->stderr_data = evbuffer_new(); - if (c->stderr_data == NULL) - fatalx("out of memory"); - c->tty.fd = -1; c->title = NULL; @@ -222,6 +219,8 @@ server_client_create(int fd) c->prompt_buffer = NULL; c->prompt_index = 0; + RB_INIT(&c->files); + c->flags |= CLIENT_FOCUSED; c->keytable = key_bindings_get_table("root", 1); @@ -263,6 +262,7 @@ void server_client_lost(struct client *c) { struct message_entry *msg, *msg1; + struct client_file *cf; c->flags |= CLIENT_DEAD; @@ -270,8 +270,10 @@ server_client_lost(struct client *c) status_prompt_clear(c); status_message_clear(c); - if (c->stdin_callback != NULL) - c->stdin_callback(c, 1, c->stdin_callback_data); + RB_FOREACH(cf, client_files, &c->files) { + cf->error = EINTR; + file_fire_done(cf); + } TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -285,11 +287,6 @@ server_client_lost(struct client *c) free(c->ttyname); free(c->term); - evbuffer_free(c->stdin_data); - evbuffer_free(c->stdout_data); - if (c->stderr_data != c->stdout_data) - evbuffer_free(c->stderr_data); - status_free(c); free(c->title); @@ -540,7 +537,8 @@ have_event: where = STATUS_RIGHT; break; case STYLE_RANGE_WINDOW: - wl = winlink_find_by_index(&s->windows, sr->argument); + wl = winlink_find_by_index(&s->windows, + sr->argument); if (wl == NULL) return (KEYC_UNKNOWN); m->w = wl->window->id; @@ -662,8 +660,7 @@ have_event: break; } c->tty.mouse_drag_flag = 0; - - return (key); + goto out; } /* Convert to a key binding. */ @@ -958,6 +955,7 @@ have_event: if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); +out: /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) key |= KEYC_ESCAPE; @@ -966,6 +964,8 @@ have_event: if (b & MOUSE_MASK_SHIFT) key |= KEYC_SHIFT; + if (log_get_level() != 0) + log_debug("mouse key is %s", key_string_lookup_key (key)); return (key); } @@ -993,6 +993,24 @@ server_client_assume_paste(struct session *s) return (0); } +/* Has the latest client changed? */ +static void +server_client_update_latest(struct client *c) +{ + struct window *w; + + if (c->session == NULL) + return; + w = c->session->curw->window; + + if (w->latest == c) + return; + w->latest = c; + + if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) + recalculate_size(w); +} + /* * Handle data key input from client. This owns and can modify the key event it * is given and is responsible for freeing it. @@ -1016,7 +1034,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) key_code key0; /* Check the client is good to accept input. */ - if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) goto out; wl = s->curw; @@ -1041,7 +1059,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) * Mouse drag is in progress, so fire the callback (now that * the mouse event is valid). */ - if (key == KEYC_DRAGGING) { + if ((key & KEYC_MASK_KEY) == KEYC_DRAGGING) { c->tty.mouse_drag_update(c, m); goto out; } @@ -1189,6 +1207,8 @@ forward_key: window_pane_key(wp, c, s, wl, key, m); out: + if (s != NULL) + server_client_update_latest(c); free(event); return (CMD_RETURN_NORMAL); } @@ -1201,7 +1221,7 @@ server_client_handle_key(struct client *c, struct key_event *event) struct cmdq_item *item; /* Check the client is good to accept input. */ - if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return (0); /* @@ -1245,7 +1265,7 @@ server_client_loop(void) struct window_pane *wp; struct winlink *wl; struct session *s; - int focus; + int focus, attached, resize; TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); @@ -1258,19 +1278,33 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. + * + * As an optimization, panes in windows that are in an attached session + * but not the current window are not resized (this reduces the amount + * of work needed when, for example, resizing an X terminal a + * lot). Windows in no attached session are resized immediately since + * that is likely to have come from a command like split-window and be + * what the user wanted. */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { + attached = resize = 0; TAILQ_FOREACH(wl, &w->winlinks, wentry) { s = wl->session; - if (s->attached != 0 && s->curw == wl) + if (s->attached != 0) + attached = 1; + if (s->attached != 0 && s->curw == wl) { + resize = 1; break; + } } + if (!attached) + resize = 1; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) server_client_check_focus(wp); - if (wl != NULL) + if (resize) server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; @@ -1284,7 +1318,6 @@ static int server_client_resize_force(struct window_pane *wp) { struct timeval tv = { .tv_usec = 100000 }; - struct winsize ws; /* * If we are resizing to the same size as when we entered the loop @@ -1305,50 +1338,20 @@ server_client_resize_force(struct window_pane *wp) wp->sy <= 1) return (0); - memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; - ws.ws_row = wp->sy - 1; - if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) -#ifdef __sun - if (errno != EINVAL && errno != ENXIO) -#endif - fatal("ioctl failed"); log_debug("%s: %%%u forcing resize", __func__, wp->id); + window_pane_send_resize(wp, -1); evtimer_add(&wp->resize_timer, &tv); wp->flags |= PANE_RESIZEFORCE; return (1); } -/* Resize timer event. */ +/* Resize a pane. */ static void -server_client_resize_event(__unused int fd, __unused short events, void *data) +server_client_resize_pane(struct window_pane *wp) { - struct window_pane *wp = data; - struct winsize ws; - - evtimer_del(&wp->resize_timer); - - if (!(wp->flags & PANE_RESIZE)) - return; - if (server_client_resize_force(wp)) - return; - - memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; - ws.ws_row = wp->sy; - if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) -#ifdef __sun - /* - * Some versions of Solaris apparently can return an error when - * resizing; don't know why this happens, can't reproduce on - * other platforms and ignoring it doesn't seem to cause any - * issues. - */ - if (errno != EINVAL && errno != ENXIO) -#endif - fatal("ioctl failed"); log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); + window_pane_send_resize(wp, 0); wp->flags &= ~PANE_RESIZE; @@ -1356,35 +1359,55 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) wp->osy = wp->sy; } +/* Start the resize timer. */ +static void +server_client_start_resize_timer(struct window_pane *wp) +{ + struct timeval tv = { .tv_usec = 250000 }; + + if (!evtimer_pending(&wp->resize_timer, NULL)) + evtimer_add(&wp->resize_timer, &tv); +} + +/* Resize timer event. */ +static void +server_client_resize_event(__unused int fd, __unused short events, void *data) +{ + struct window_pane *wp = data; + + evtimer_del(&wp->resize_timer); + + if (~wp->flags & PANE_RESIZE) + return; + log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id, + (wp->flags & PANE_RESIZED) ? "" : " not"); + + if (wp->saved_grid == NULL && (wp->flags & PANE_RESIZED)) { + log_debug("%s: %%%u deferring timer", __func__, wp->id); + server_client_start_resize_timer(wp); + } else if (!server_client_resize_force(wp)) { + log_debug("%s: %%%u resizing pane", __func__, wp->id); + server_client_resize_pane(wp); + } + wp->flags &= ~PANE_RESIZED; +} + /* Check if pane should be resized. */ static void server_client_check_resize(struct window_pane *wp) { - struct timeval tv = { .tv_usec = 250000 }; - - if (!(wp->flags & PANE_RESIZE)) + if (~wp->flags & PANE_RESIZE) return; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_event, wp); - /* - * The first resize should happen immediately, so if the timer is not - * running, do it now. - */ - if (!evtimer_pending(&wp->resize_timer, NULL)) - server_client_resize_event(-1, 0, wp); - - /* - * If the pane is in the alternate screen, let the timer expire and - * resize to give the application a chance to redraw. If not, keep - * pushing the timer back. - */ - if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL)) - return; - evtimer_del(&wp->resize_timer); - evtimer_add(&wp->resize_timer, &tv); + if (!evtimer_pending(&wp->resize_timer, NULL)) { + log_debug("%s: %%%u starting timer", __func__, wp->id); + server_client_resize_pane(wp); + server_client_start_resize_timer(wp); + } else + log_debug("%s: %%%u timer running", __func__, wp->id); } /* Check whether pane should be focused. */ @@ -1533,17 +1556,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data) static void server_client_check_exit(struct client *c) { + struct client_file *cf; + if (~c->flags & CLIENT_EXIT) return; if (c->flags & CLIENT_EXITED) return; - if (EVBUFFER_LENGTH(c->stdin_data) != 0) - return; - if (EVBUFFER_LENGTH(c->stdout_data) != 0) - return; - if (EVBUFFER_LENGTH(c->stderr_data) != 0) - return; + RB_FOREACH(cf, client_files, &c->files) { + if (EVBUFFER_LENGTH(cf->buffer) != 0) + return; + } if (c->flags & CLIENT_ATTACHED) notify_client("client-detached", c); @@ -1554,7 +1577,7 @@ server_client_check_exit(struct client *c) /* Redraw timer callback. */ static void server_client_redraw_timer(__unused int fd, __unused short events, - __unused void* data) + __unused void *data) { log_debug("redraw timer fired"); } @@ -1682,11 +1705,9 @@ server_client_set_title(struct client *c) static void server_client_dispatch(struct imsg *imsg, void *arg) { - struct client *c = arg; - struct msg_stdin_data stdindata; - const char *data; - ssize_t datalen; - struct session *s; + struct client *c = arg; + ssize_t datalen; + struct session *s; if (c->flags & CLIENT_DEAD) return; @@ -1696,7 +1717,6 @@ server_client_dispatch(struct imsg *imsg, void *arg) return; } - data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { @@ -1713,27 +1733,13 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_COMMAND: server_client_dispatch_command(c, imsg); break; - case MSG_STDIN: - if (datalen != sizeof stdindata) - fatalx("bad MSG_STDIN size"); - memcpy(&stdindata, data, sizeof stdindata); - - if (c->stdin_callback == NULL) - break; - if (stdindata.size <= 0) - c->stdin_closed = 1; - else { - evbuffer_add(c->stdin_data, stdindata.data, - stdindata.size); - } - c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data); - break; case MSG_RESIZE: if (datalen != 0) fatalx("bad MSG_RESIZE size"); if (c->flags & CLIENT_CONTROL) break; + server_client_update_latest(c); server_client_clear_overlay(c); tty_resize(&c->tty); recalculate_sizes(); @@ -1778,6 +1784,15 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_client_dispatch_shell(c); break; + case MSG_WRITE_READY: + server_client_dispatch_write_ready(c, imsg); + break; + case MSG_READ: + server_client_dispatch_read_data(c, imsg); + break; + case MSG_READ_DONE: + server_client_dispatch_read_done(c, imsg); + break; } } @@ -1796,7 +1811,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data) static void server_client_dispatch_command(struct client *c, struct imsg *imsg) { - struct msg_command_data data; + struct msg_command data; char *buf; size_t len; int argc; @@ -1940,19 +1955,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) #endif if (c->flags & CLIENT_CONTROL) { - c->stdin_callback = control_callback; - - evbuffer_free(c->stderr_data); - c->stderr_data = c->stdout_data; - - if (c->flags & CLIENT_CONTROLCONTROL) - evbuffer_add_printf(c->stdout_data, "\033P1000p"); - proc_send(c->peer, MSG_STDIN, -1, NULL, 0); - - c->tty.fd = -1; - close(c->fd); c->fd = -1; + + control_start(c); + c->tty.fd = -1; } else if (c->fd != -1) { if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); @@ -1992,93 +1999,71 @@ server_client_dispatch_shell(struct client *c) proc_kill_peer(c->peer); } -/* Event callback to push more stdout data if any left. */ +/* Handle write ready message. */ static void -server_client_stdout_cb(__unused int fd, __unused short events, void *arg) +server_client_dispatch_write_ready(struct client *c, struct imsg *imsg) { - struct client *c = arg; + struct msg_write_ready *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; - if (~c->flags & CLIENT_DEAD) - server_client_push_stdout(c); - server_client_unref(c); -} - -/* Push stdout to client if possible. */ -void -server_client_push_stdout(struct client *c) -{ - struct msg_stdout_data data; - size_t sent, left; - - left = EVBUFFER_LENGTH(c->stdout_data); - while (left != 0) { - sent = left; - if (sent > sizeof data.data) - sent = sizeof data.data; - memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent); - data.size = sent; - - if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0) - break; - evbuffer_drain(c->stdout_data, sent); - - left = EVBUFFER_LENGTH(c->stdout_data); - log_debug("%s: client %p, sent %zu, left %zu", __func__, c, - sent, left); - } - if (left != 0) { - c->references++; - event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL); - log_debug("%s: client %p, queued", __func__, c); - } -} - -/* Event callback to push more stderr data if any left. */ -static void -server_client_stderr_cb(__unused int fd, __unused short events, void *arg) -{ - struct client *c = arg; - - if (~c->flags & CLIENT_DEAD) - server_client_push_stderr(c); - server_client_unref(c); -} - -/* Push stderr to client if possible. */ -void -server_client_push_stderr(struct client *c) -{ - struct msg_stderr_data data; - size_t sent, left; - - if (c->stderr_data == c->stdout_data) { - server_client_push_stdout(c); + if (msglen != sizeof *msg) + fatalx("bad MSG_WRITE_READY size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) return; - } + if (msg->error != 0) { + cf->error = msg->error; + file_fire_done(cf); + } else + file_push(cf); +} - left = EVBUFFER_LENGTH(c->stderr_data); - while (left != 0) { - sent = left; - if (sent > sizeof data.data) - sent = sizeof data.data; - memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); - data.size = sent; +/* Handle read data message. */ +static void +server_client_dispatch_read_data(struct client *c, struct imsg *imsg) +{ + struct msg_read_data *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + void *bdata = msg + 1; + size_t bsize = msglen - sizeof *msg; - if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) - break; - evbuffer_drain(c->stderr_data, sent); + if (msglen < sizeof *msg) + fatalx("bad MSG_READ_DATA size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) + return; - left = EVBUFFER_LENGTH(c->stderr_data); - log_debug("%s: client %p, sent %zu, left %zu", __func__, c, - sent, left); - } - if (left != 0) { - c->references++; - event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); - log_debug("%s: client %p, queued", __func__, c); + log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize); + if (cf->error == 0) { + if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { + cf->error = ENOMEM; + file_fire_done(cf); + } else + file_fire_read(cf); } } +/* Handle read done message. */ +static void +server_client_dispatch_read_done(struct client *c, struct imsg *imsg) +{ + struct msg_read_done *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + fatalx("bad MSG_READ_DONE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) + return; + + log_debug("%s: file %d read done", c->name, cf->stream); + cf->error = msg->error; + file_fire_done(cf); +} + /* Add to client message log. */ void server_client_add_message(struct client *c, const char *fmt, ...) @@ -2128,19 +2113,3 @@ server_client_get_cwd(struct client *c, struct session *s) return (home); return ("/"); } - -/* Resolve an absolute path or relative to client working directory. */ -char * -server_client_get_path(struct client *c, const char *file) -{ - char *path, resolved[PATH_MAX]; - - if (*file == '/') - path = xstrdup(file); - else - xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file); - if (realpath(path, resolved) == NULL) - return (path); - free(path); - return (xstrdup(resolved)); -} diff --git a/external/bsd/tmux/dist/server-fn.c b/external/bsd/tmux/dist/server-fn.c index cfafef21bcc3..2247f1c5bc11 100644 --- a/external/bsd/tmux/dist/server-fn.c +++ b/external/bsd/tmux/dist/server-fn.c @@ -440,36 +440,6 @@ server_check_unattached(void) } } -int -server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, - void *), void *cb_data, char **cause) -{ - if (c == NULL || c->session != NULL) { - *cause = xstrdup("no client with stdin"); - return (-1); - } - if (c->flags & CLIENT_TERMINAL) { - *cause = xstrdup("stdin is a tty"); - return (-1); - } - if (c->stdin_callback != NULL) { - *cause = xstrdup("stdin is in use"); - return (-1); - } - - c->stdin_callback_data = cb_data; - c->stdin_callback = cb; - - c->references++; - - if (c->stdin_closed) - c->stdin_callback(c, 1, c->stdin_callback_data); - - proc_send(c->peer, MSG_STDIN, -1, NULL, 0); - - return (0); -} - void server_unzoom_window(struct window *w) { diff --git a/external/bsd/tmux/dist/session.c b/external/bsd/tmux/dist/session.c index 4f130fda0e03..16d83934a67a 100644 --- a/external/bsd/tmux/dist/session.c +++ b/external/bsd/tmux/dist/session.c @@ -568,7 +568,20 @@ session_group_count(struct session_group *sg) n = 0; TAILQ_FOREACH(s, &sg->sessions, gentry) - n++; + n++; + return (n); +} + +/* Count number of clients attached to sessions in session group. */ +u_int +session_group_attached_count(struct session_group *sg) +{ + struct session *s; + u_int n; + + n = 0; + TAILQ_FOREACH(s, &sg->sessions, gentry) + n += s->attached; return (n); } diff --git a/external/bsd/tmux/dist/spawn.c b/external/bsd/tmux/dist/spawn.c index 7349d29bf93c..b3a4ada27dc8 100644 --- a/external/bsd/tmux/dist/spawn.c +++ b/external/bsd/tmux/dist/spawn.c @@ -78,12 +78,14 @@ spawn_log(const char *from, struct spawn_context *sc) struct winlink * spawn_window(struct spawn_context *sc, char **cause) { + struct cmdq_item *item = sc->item; + struct client *c = item->client; struct session *s = sc->s; struct window *w; struct window_pane *wp; struct winlink *wl; int idx = sc->idx; - u_int sx, sy; + u_int sx, sy, xpixel, ypixel; spawn_log(__func__, sc); @@ -153,8 +155,9 @@ spawn_window(struct spawn_context *sc, char **cause) xasprintf(cause, "couldn't add window %d", idx); return (NULL); } - default_window_size(s, NULL, &sx, &sy, -1); - if ((w = window_create(sx, sy)) == NULL) { + default_window_size(sc->c, s, NULL, &sx, &sy, &xpixel, &ypixel, + -1); + if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) { winlink_remove(&s->windows, sc->wl); xasprintf(cause, "couldn't create window %d", idx); return (NULL); @@ -162,6 +165,7 @@ spawn_window(struct spawn_context *sc, char **cause) if (s->curw == NULL) s->curw = sc->wl; sc->wl->session = s; + w->latest = sc->c; winlink_set_window(sc->wl, w); } else w = NULL; @@ -178,7 +182,8 @@ spawn_window(struct spawn_context *sc, char **cause) /* Set the name of the new window. */ if (~sc->flags & SPAWN_RESPAWN) { if (sc->name != NULL) { - w->name = xstrdup(sc->name); + w->name = format_single(item, sc->name, c, s, NULL, + NULL); options_set_number(w->options, "automatic-rename", 0); } else w->name = xstrdup(default_window_name(w)); @@ -214,9 +219,21 @@ spawn_pane(struct spawn_context *sc, char **cause) u_int hlimit; struct winsize ws; sigset_t set, oldset; + key_code key; spawn_log(__func__, sc); + /* + * Work out the current working directory. If respawning, use + * the pane's stored one unless specified. + */ + if (sc->cwd != NULL) + cwd = format_single(item, sc->cwd, c, item->target.s, NULL, NULL); + else if (~sc->flags & SPAWN_RESPAWN) + cwd = xstrdup(server_client_get_cwd(c, item->target.s)); + else + cwd = NULL; + /* * If we are respawning then get rid of the old process. Otherwise * either create a new cell or assign to the one we are given. @@ -227,6 +244,7 @@ spawn_pane(struct spawn_context *sc, char **cause) window_pane_index(sc->wp0, &idx); xasprintf(cause, "pane %s:%d.%u still active", s->name, sc->wl->idx, idx); + free(cwd); return (NULL); } if (sc->wp0->fd != -1) { @@ -247,8 +265,8 @@ spawn_pane(struct spawn_context *sc, char **cause) } /* - * Now we have a pane with nothing running in it ready for the new - * process. Work out the command and arguments. + * Now we have a pane with nothing running in it ready for the new process. + * Work out the command and arguments and store the working directory. */ if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { cmd = options_get_string(s->options, "default-command"); @@ -263,6 +281,10 @@ spawn_pane(struct spawn_context *sc, char **cause) argc = sc->argc; argv = sc->argv; } + if (cwd != NULL) { + free(new_wp->cwd); + new_wp->cwd = cwd; + } /* * Replace the stored arguments if there are new ones. If not, the @@ -274,21 +296,6 @@ spawn_pane(struct spawn_context *sc, char **cause) new_wp->argv = cmd_copy_argv(argc, argv); } - /* - * Work out the current working directory. If respawning, use - * the pane's stored one unless specified. - */ - if (sc->cwd != NULL) - cwd = format_single(item, sc->cwd, c, s, NULL, NULL); - else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, s)); - else - cwd = NULL; - if (cwd != NULL) { - free(new_wp->cwd); - new_wp->cwd = cwd; - } - /* Create an environment for this pane. */ child = environ_for_session(s, 0); if (sc->environ != NULL) @@ -334,6 +341,8 @@ spawn_pane(struct spawn_context *sc, char **cause) memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&new_wp->base); ws.ws_row = screen_size_y(&new_wp->base); + ws.ws_xpixel = w->xpixel * ws.ws_col; + ws.ws_ypixel = w->ypixel * ws.ws_row; /* Block signals until fork has completed. */ sigfillset(&set); @@ -375,13 +384,17 @@ spawn_pane(struct spawn_context *sc, char **cause) /* * Update terminal escape characters from the session if available and - * force VERASE to tmux's \177. + * force VERASE to tmux's backspace. */ if (tcgetattr(STDIN_FILENO, &now) != 0) _exit(1); if (s->tio != NULL) memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); - now.c_cc[VERASE] = '\177'; + key = options_get_number(global_options, "backspace"); + if (key >= 0x7f) + now.c_cc[VERASE] = '\177'; + else + now.c_cc[VERASE] = key; if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) _exit(1); @@ -424,6 +437,15 @@ spawn_pane(struct spawn_context *sc, char **cause) _exit(1); complete: +#ifdef HAVE_UTEMPTER + if (~new_wp->flags & PANE_EMPTY) { + xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), new_wp->id); + utempter_add_record(new_wp->fd, cp); + kill(getpid(), SIGCHLD); + free(cp); + } +#endif + new_wp->pipe_off = 0; new_wp->flags &= ~PANE_EXITED; diff --git a/external/bsd/tmux/dist/status.c b/external/bsd/tmux/dist/status.c index 06ee4fd590f2..280ecffa2967 100644 --- a/external/bsd/tmux/dist/status.c +++ b/external/bsd/tmux/dist/status.c @@ -915,11 +915,17 @@ status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, word[64], prefix = '='; - const char *histstr, *ws = NULL; + const char *histstr, *ws = NULL, *keystring; size_t size, n, off, idx, used; struct utf8_data tmp, *first, *last, *ud; int keys; + if (c->prompt_flags & PROMPT_KEY) { + keystring = key_string_lookup_key(key); + c->prompt_inputcb(c, c->prompt_data, keystring, 1); + status_prompt_clear(c); + return (0); + } size = utf8_strlen(c->prompt_buffer); if (c->prompt_flags & PROMPT_NUMERIC) { diff --git a/external/bsd/tmux/dist/style.c b/external/bsd/tmux/dist/style.c index 2a66a5b0d8f4..82664dda0977 100644 --- a/external/bsd/tmux/dist/style.c +++ b/external/bsd/tmux/dist/style.c @@ -36,13 +36,15 @@ static struct style style_default = { STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, - STYLE_RANGE_NONE, 0 + STYLE_RANGE_NONE, 0, + + STYLE_DEFAULT_BASE }; /* - * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". - * Note that this adds onto the given style, so it must have been initialized - * alredy. + * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note + * that this adds onto the given style, so it must have been initialized + * already. */ int style_parse(struct style *sy, const struct grid_cell *base, const char *in) @@ -74,7 +76,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.bg = base->bg; sy->gc.attr = base->attr; sy->gc.flags = base->flags; - } else if (strcasecmp(tmp, "nolist") == 0) + } else if (strcasecmp(tmp, "push-default") == 0) + sy->default_type = STYLE_DEFAULT_PUSH; + else if (strcasecmp(tmp, "pop-default") == 0) + sy->default_type = STYLE_DEFAULT_POP; + else if (strcasecmp(tmp, "nolist") == 0) sy->list = STYLE_LIST_OFF; else if (strncasecmp(tmp, "list=", 5) == 0) { if (strcasecmp(tmp + 5, "on") == 0) @@ -223,6 +229,14 @@ style_tostring(struct style *sy) tmp); comma = ","; } + if (sy->default_type != STYLE_DEFAULT_BASE) { + if (sy->default_type == STYLE_DEFAULT_PUSH) + tmp = "push-default"; + else if (sy->default_type == STYLE_DEFAULT_POP) + tmp = "pop-default"; + off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp); + comma = ","; + } if (sy->fill != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, colour_tostring(sy->fill)); @@ -262,21 +276,6 @@ style_apply(struct grid_cell *gc, struct options *oo, const char *name) gc->attr |= sy->gc.attr; } -/* Apply a style, updating if default. */ -void -style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) -{ - struct style *sy; - - sy = options_get_style(oo, name); - if (sy->gc.fg != 8) - gc->fg = sy->gc.fg; - if (sy->gc.bg != 8) - gc->bg = sy->gc.bg; - if (sy->gc.attr != 0) - gc->attr |= sy->gc.attr; -} - /* Initialize style from cell. */ void style_set(struct style *sy, const struct grid_cell *gc) diff --git a/external/bsd/tmux/dist/tmux.1 b/external/bsd/tmux/dist/tmux.1 index 43971bddc530..a23e1b38446e 100644 --- a/external/bsd/tmux/dist/tmux.1 +++ b/external/bsd/tmux/dist/tmux.1 @@ -214,7 +214,6 @@ was given) and off. Report the .Nm version. -.Pp .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , @@ -296,6 +295,12 @@ Prompt to search for text in open windows. Display some information about the current window. .It l Move to the previously selected window. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. .It n Change to the next window. .It o @@ -306,12 +311,6 @@ Change to the previous window. Briefly display pane indexes. .It r Force redraw of the attached client. -.It m -Mark the current pane (see -.Ic select-pane -.Fl m ) . -.It M -Clear the marked pane. .It s Select a new session for the attached client interactively. .It t @@ -556,7 +555,7 @@ Braces may be enclosed inside braces, for example: .Bd -literal -offset indent bind x if-shell "true" { if-shell "true" { - display "true!" + display "true!" } } .Ed @@ -938,7 +937,9 @@ If is specified, any other clients attached to the session are detached. If .Fl x -is given, send SIGHUP to the parent process of the client as well as +is given, send +.Dv SIGHUP +to the parent process of the client as well as detaching the client, typically causing it to exit. .Fl r signifies the client is read-only (only keys bound to the @@ -989,7 +990,9 @@ option kills all but the client given with .Fl t . If .Fl P -is given, send SIGHUP to the parent process of the client, typically causing it +is given, send +.Dv SIGHUP +to the parent process of the client, typically causing it to exit. With .Fl E , @@ -1033,9 +1036,12 @@ If is specified, list only clients connected to that session. .It Xo Ic list-commands .Op Fl F Ar format +.Op Ar command .Xc .D1 (alias: Ic lscm ) -List the syntax of all commands supported by +List the syntax of +.Ar command +or - if omitted - of all commands supported by .Nm . .It Ic list-sessions Op Fl F Ar format .D1 (alias: Ic ls ) @@ -1156,7 +1162,7 @@ The .Fl P option prints information about the new session after it has been created. By default, it uses the format -.Ql #{session_name}: +.Ql #{session_name}:\& but a different format may be specified with .Fl F . .Pp @@ -1288,6 +1294,17 @@ shows the parsed commands and line numbers if possible. Start the .Nm server, if not already running, without creating any sessions. +.Pp +Note that as by default the +.Nm +server will exit with no sessions, this is only useful if a session is created in +.Pa ~/.tmux.conf , +.Ic exit-empty +is turned off, or another command is run as part of the same command sequence. +For example: +.Bd -literal -offset indent +$ tmux start \\; show -g +.Ed .It Xo Ic suspend-client .Op Fl t Ar target-client .Xc @@ -1296,7 +1313,7 @@ Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client -.Op Fl Elnpr +.Op Fl ElnprZ .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table @@ -1313,7 +1330,10 @@ may refer to a pane (a target that contains .Ql \&. or .Ql % ) , -in which case the session, window and pane are all changed. +to change session, window and pane. +In that case, +.Fl Z +keeps the window zoomed if it was zoomed. If .Fl l , .Fl n @@ -1333,7 +1353,8 @@ is used, option will not be applied. .Pp .Fl T -sets the client's key table; the next key from the client will be interpreted from +sets the client's key table; the next key from the client will be interpreted +from .Ar key-table . This may be used to configure multiple prefix keys, or to bind commands to sequences of keys. @@ -1349,11 +1370,41 @@ bind-key -Troot a switch-client -Ttable1 .Ed .El .Sh WINDOWS AND PANES -A +Each window displayed by .Nm -window may be in one of two modes. -The default permits direct access to the terminal attached to the window. -The other is copy mode, which permits a section of a window or its +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +Windows may be split horizontally (with the +.Fl h +flag) or vertically. +Panes may be resized with the +.Ic resize-pane +command (bound to +.Ql C-Up , +.Ql C-Down +.Ql C-Left +and +.Ql C-Right +by default), the current pane may be changed with the +.Ic select-pane +command and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing their position. +Panes are numbered beginning from zero in the order they are created. +.Pp +By default, a +.Nm +pane permits direct access to the terminal contained in the pane. +A pane may also be put into one of several modes: +.Bl -dash -offset indent +.It +Copy mode, which permits a section of a window or its history to be copied to a .Em paste buffer for later insertion into another window. @@ -1362,9 +1413,21 @@ This mode is entered with the command, bound to .Ql \&[ by default. -It is also entered when a command that produces output, such as +.It +View mode, which is like copy mode but is entered when a command that produces +output, such as .Ic list-keys , is executed from a key binding. +.It +Choose mode, which allows an item to be chosen from a list. +This may be a client, a session or window or pane, or a buffer. +This mode is entered with the +.Ic choose-buffer , +.Ic choose-client +and +.Ic choose-tree +commands. +.El .Pp In copy mode an indicator is displayed in the top-right corner of the pane with the current position and the number of lines in the history. @@ -1405,6 +1468,7 @@ The following commands are supported in copy mode: .It Li "copy-selection-no-clear []" Ta "" Ta "" .It Li "copy-selection-and-cancel []" Ta "Enter" Ta "M-w" .It Li "cursor-down" Ta "j" Ta "Down" +.It Li "cursor-down-and-cancel" Ta "" Ta "" .It Li "cursor-left" Ta "h" Ta "Left" .It Li "cursor-right" Ta "l" Ta "Right" .It Li "cursor-up" Ta "k" Ta "Up" @@ -1442,9 +1506,11 @@ The following commands are supported in copy mode: .It Li "scroll-up" Ta "C-y" Ta "C-Up" .It Li "search-again" Ta "n" Ta "n" .It Li "search-backward " Ta "?" Ta "" -.It Li "search-forward " Ta "/" Ta "" .It Li "search-backward-incremental " Ta "" Ta "C-r" +.It Li "search-backward-text " Ta "" Ta "" +.It Li "search-forward " Ta "/" Ta "" .It Li "search-forward-incremental " Ta "" Ta "C-s" +.It Li "search-forward-text " Ta "" Ta "" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" @@ -1453,6 +1519,26 @@ The following commands are supported in copy mode: .It Li "top-line" Ta "H" Ta "M-R" .El .Pp +The search commands come in several varieties: +.Ql search-forward +and +.Ql search-backward +search for a regular expression; +the +.Ql -text +variants search for a plain text string rather than a regular expression; +.Ql -incremental +perform an incremental search and expect to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.Ql search-again +repeats the last search and +.Ql search-reverse +does the same but reverses the direction (forward becomes backward and backward +becomes forward). +.Pp Copy commands may take an optional buffer prefix argument which is used to generate the buffer name (the default is .Ql buffer @@ -1504,7 +1590,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl Meu +.Op Fl eHMqu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1514,6 +1600,11 @@ option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Fl H +hides the position indicator in the top right. +.Fl q +cancels copy mode and any other modes. +.Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) should exit copy mode. @@ -1526,37 +1617,7 @@ bind PageUp copy-mode -eu .Ed .El .Pp -Each window displayed by -.Nm -may be split into one or more -.Em panes ; -each pane takes up a certain area of the display and is a separate terminal. -A window may be split into panes using the -.Ic split-window -command. -Windows may be split horizontally (with the -.Fl h -flag) or vertically. -Panes may be resized with the -.Ic resize-pane -command (bound to -.Ql C-Up , -.Ql C-Down -.Ql C-Left -and -.Ql C-Right -by default), the current pane may be changed with the -.Ic select-pane -command and the -.Ic rotate-window -and -.Ic swap-pane -commands may be used to swap panes without changing their position. -Panes are numbered beginning from zero in the order they are created. -.Pp -A number of preset -.Em layouts -are available. +A number of preset arrangements of panes are available, these are called layouts. These may be selected with the .Ic select-layout command or cycled with @@ -1635,7 +1696,7 @@ By default, it uses the format but a different format may be specified with .Fl F . .It Xo Ic capture-pane -.Op Fl aepPqCJ +.Op Fl aepPqCJN .Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line @@ -1660,8 +1721,10 @@ is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. +.Fl N +preserves trailing spaces at each line's end and .Fl J -joins wrapped lines and preserves trailing spaces at each line's end. +preserves trailing spaces and joins any wrapped lines. .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. @@ -1680,7 +1743,7 @@ the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client -.Op Fl NZ +.Op Fl NrZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -1709,7 +1772,8 @@ The following keys may be used in client mode: .It Li "z" Ta "Suspend selected client" .It Li "Z" Ta "Suspend tagged clients" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -1724,12 +1788,14 @@ If is not given, "detach-client -t '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql name , .Ql size , .Ql creation , or .Ql activity . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -1741,7 +1807,7 @@ starts without the preview. This command works only if at least one client is attached. .It Xo .Ic choose-tree -.Op Fl GNswZ +.Op Fl GNrswZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -1773,7 +1839,8 @@ The following keys may be used in tree mode: .It Li "C-t" Ta "Tag all items" .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -1788,11 +1855,13 @@ If is not given, "switch-client -t '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql index , .Ql name , or .Ql time . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -1871,10 +1940,8 @@ zooms the pane. .Pp This command works only if at least one client is attached. .It Xo Ic join-pane -.Op Fl bdhv -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl bdfhv +.Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -1925,11 +1992,13 @@ The option kills all but the window given with .Fl t . .It Xo Ic last-pane -.Op Fl de +.Op Fl deZ .Op Fl t Ar target-window .Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. +.Fl Z +keeps the window zoomed if it was zoomed. .Fl e enables or .Fl d @@ -2009,9 +2078,7 @@ flag, see the section. .It Xo Ic move-pane .Op Fl bdhv -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -2220,8 +2287,14 @@ or .Fl y . The .Ar adjustment -is given in lines or cells (the default is 1). -.Pp +is given in lines or columns (the default is 1); +.Fl x +and +.Fl y +may be a given as a number of lines or columns or followed by +.Ql % +for a percentage of the window size (for example +.Ql -x 10% ) . With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) @@ -2311,7 +2384,7 @@ option has the same meaning as for the .Ic new-window command. .It Xo Ic rotate-window -.Op Fl DU +.Op Fl DUZ .Op Fl t Ar target-window .Xc .D1 (alias: Ic rotatew ) @@ -2319,6 +2392,8 @@ Rotate the positions of the panes within a window, either upward (numerically lower) with .Fl U or downward (numerically higher). +.Fl Z +keeps the window zoomed if it was zoomed. .It Xo Ic select-layout .Op Fl Enop .Op Fl t Ar target-pane @@ -2342,7 +2417,7 @@ applies the last set layout if possible (undoes the most recent layout change). .Fl E spreads the current pane and any panes next to it out evenly. .It Xo Ic select-pane -.Op Fl DdeLlMmRU +.Op Fl DdeLlMmRUZ .Op Fl T Ar title .Op Fl t Ar target-pane .Xc @@ -2359,6 +2434,8 @@ or .Fl U is used, respectively the pane below, to the left, to the right, or above the target pane is used. +.Fl Z +keeps the window zoomed if it was zoomed. .Fl l is the same as using the .Ic last-pane @@ -2409,9 +2486,7 @@ the command behaves like .Op Fl bdfhIvP .Op Fl c Ar start-directory .Op Fl e Ar environment -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl l Ar size .Op Fl t Ar target-pane .Op Ar shell-command .Op Fl F Ar format @@ -2427,10 +2502,12 @@ a vertical split; if neither is specified, is assumed. The .Fl l -and -.Fl p -options specify the size of the new pane in lines (for vertical split) or in -cells (for horizontal split), or as a percentage, respectively. +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. The .Fl b option causes the new pane to be created to the left of or above @@ -2464,7 +2541,7 @@ All other options have the same meaning as for the .Ic new-window command. .It Xo Ic swap-pane -.Op Fl dDU +.Op Fl dDUZ .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -2481,7 +2558,9 @@ swaps with the next pane (after it numerically). .Fl d instructs .Nm -not to change the active pane. +not to change the active pane and +.Fl Z +keeps the window zoomed if it was zoomed. .Pp If .Fl s @@ -2500,10 +2579,11 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +If +.Fl d +is given, the new window does not become the current window. .Pp -Like -.Ic swap-pane , -if +If .Fl s is omitted and a marked pane is present (see .Ic select-pane @@ -2571,10 +2651,15 @@ bind-key '"' split-window bind-key "'" new-window .Ed .Pp +A command bound to the +.Em Any +key will execute for all keys which do not have a more specific binding. +.Pp Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl nr +.Op Fl N Ar note .Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc @@ -2622,24 +2707,51 @@ The flag indicates this key may repeat, see the .Ic repeat-time option. +.Fl N +attaches a note to the key (shown with +.Ic list-keys +.Fl N ) . .Pp To view the default bindings and possible commands, see the .Ic list-keys command. .It Xo Ic list-keys -.Op Fl T Ar key-table +.Op Fl 1aN +.Op Fl P Ar prefix-string Fl T Ar key-table +.Op Ar key .Xc .D1 (alias: Ic lsk ) -List all key bindings. -Without +List key bindings. +There are two forms: the default lists keys as +.Ic bind-key +commands; +.Fl N +lists only keys with attached notes and shows only the key and note for each +key. +.Pp +With the default form, all key tables are listed by default. .Fl T -all key tables are printed. -With -.Fl T -only +lists only keys in .Ar key-table . +.Pp +With the +.Fl N +form, only keys in the +.Em root +and +.Em prefix +key tables are listed by default; +.Fl T +also lists only keys in +.Ar key-table . +.Fl P +specifies a prefix to print before each key and +.Fl 1 +lists only the first matching key. +.Fl a +lists the command for keys that do have a note rather than skipping them. .It Xo Ic send-keys -.Op Fl HlMRX +.Op Fl FHlMRX .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... @@ -2678,7 +2790,9 @@ the .Sx WINDOWS AND PANES section. .Fl N -specifies a repeat count. +specifies a repeat count and +.Fl F +expands formats in arguments where appropriate. .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -2716,7 +2830,7 @@ and .Pp The .Nm -server has a set of global options which do not apply to any particular +server has a set of global server options which do not apply to any particular window or session or pane. These are altered with the .Ic set-option @@ -2890,6 +3004,10 @@ omitted to toggle). .Pp Available server options are: .Bl -tag -width Ds +.It Ic backspace Ar key +Set the key sent by +.Nm +for backspace. .It Ic buffer-limit Ar number Set the number of buffers; as new buffers are added to the top of the stack, old ones are removed from the bottom if necessary to maintain this maximum @@ -3632,7 +3750,7 @@ see the section. .Pp .It Xo Ic window-size -.Ar largest | Ar smallest | Ar manual +.Ar largest | Ar smallest | Ar manual | Ar latest .Xc Configure how .Nm @@ -3647,6 +3765,10 @@ If the size of a new window is set from the .Ic default-size option and windows are resized automatically. +With +.Ar latest , +.Nm +uses the size of the client that had the most recent activity. See also the .Ic resize-window command and the @@ -4058,9 +4180,15 @@ appended or prepended to the string if the length has been trimmed, for example will append .Ql ... if the pane title is more than five characters. +Similarly, +.Ql p +pads the string to a given width, for example +.Ql #{p10:pane_title} +will result in a width of at least 10 characters. +A positive width pads on the left, a negative on the right. .Pp Prefixing a time variable with -.Ql t: +.Ql t:\& will convert it to a string, so if .Ql #{window_activity} gives @@ -4069,34 +4197,34 @@ gives gives .Ql Sun Oct 25 09:25:02 2015 . The -.Ql b: +.Ql b:\& and -.Ql d: +.Ql d:\& prefixes are .Xr basename 3 and .Xr dirname 3 of the variable respectively. -.Ql q: +.Ql q:\& will escape .Xr sh 1 special characters. -.Ql E: +.Ql E:\& will expand the format twice, for example .Ql #{E:status-left} is the result of expanding the content of the .Ic status-left option rather than the option itself. -.Ql T: +.Ql T:\& is like -.Ql E: +.Ql E:\& but also expands .Xr strftime 3 specifiers. -.Ql S: , -.Ql W: +.Ql S:\& , +.Ql W:\& or -.Ql P: +.Ql P:\& will loop over each session, window or pane and insert the format once for each. For windows and panes, two comma-separated formats may be given: @@ -4107,7 +4235,7 @@ For example, to get a list of windows formatted like the status line: .Ed .Pp A prefix of the form -.Ql s/foo/bar/: +.Ql s/foo/bar/:\& will substitute .Ql foo with @@ -4116,7 +4244,7 @@ throughout. The first argument may be an extended regular expression and a final argument may be .Ql i to ignore case, for example -.Ql s/a(.)/\e1x/i: +.Ql s/a(.)/\e1x/i:\& would change .Ql abABab into @@ -4160,6 +4288,8 @@ The following variables are available, where appropriate: .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "" Ta "Time client last had activity" +.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" +.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_created" Ta "" Ta "Time client created" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" @@ -4172,15 +4302,18 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" -.It Li "client_termtype" Ta "" Ta "Terminal type of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" -.It Li "client_utf8" Ta "" Ta "1 if client supports utf8" +.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" .It Li "command" Ta "" Ta "Name of command in use, if any" .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" +.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" +.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" +.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" +.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" @@ -4221,7 +4354,7 @@ The following variables are available, where appropriate: .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" -.It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" +.It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" @@ -4231,6 +4364,7 @@ The following variables are available, where appropriate: .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" .It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" +.It Li "pane_path" Ta "#T" Ta "Path of pane (can be set by application)" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" @@ -4238,7 +4372,7 @@ The following variables are available, where appropriate: .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" -.It Li "pane_title" Ta "#T" Ta "Title of pane" +.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" @@ -4247,14 +4381,23 @@ The following variables are available, where appropriate: .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" +.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" +.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" .It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" +.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection" +.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection" .It Li "session_activity" Ta "" Ta "Time of session last activity" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_attached_list" Ta "" Ta "List of clients session is attached to" .It Li "session_created" Ta "" Ta "Time session created" -.It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)" +.It Li "session_format" Ta "" Ta "1 if format is for a session" .It Li "session_group" Ta "" Ta "Name of session group" +.It Li "session_group_attached" Ta "" Ta "Number of clients sessions in group are attached to" +.It Li "session_group_attached_list" Ta "" Ta "List of clients sessions in group are attached to" .It Li "session_group_list" Ta "" Ta "List of sessions in group" +.It Li "session_group_many_attached" Ta "" Ta "1 if multiple clients attached to sessions in group" .It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_id" Ta "" Ta "Unique session ID" @@ -4267,19 +4410,28 @@ The following variables are available, where appropriate: .It Li "start_time" Ta "" Ta "Server start time" .It Li "version" Ta "" Ta "Server version" .It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" +.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window" +.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active" +.It Li "window_active_sessions_list" Ta "" Ta "List of sessions on which this window is active" .It Li "window_activity" Ta "" Ta "Time of window last activity" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_bigger" Ta "" Ta "1 if window is larger than client" +.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" +.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags" -.It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)" +.It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to" +.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to" +.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" @@ -4299,7 +4451,7 @@ interface, for example .Ic status-style for the status line. In addition, embedded styles may be specified in format options, such as -.Ic status-left-format , +.Ic status-left , by enclosing them in .Ql #[ and @@ -4307,7 +4459,9 @@ and .Pp A style may be the single term .Ql default -to specify the default style (which may inherit from another option) or a space +to specify the default style (which may come from an option, for example +.Ic status-style +in the status line) or a space or comma separated list of the following: .Bl -tag -width Ds .It Ic fg=colour @@ -4386,6 +4540,20 @@ and .Ic list=right-marker mark the text to be used to mark that text has been trimmed from the left or right of the list if there is not enough space. +.It Xo Ic push-default , +.Ic pop-default +.Xc +Store the current colours and attributes as the default or reset to the previous +default. +A +.Ic push-default +affects any subsequent use of the +.Ic default +term until a +.Ic pop-default . +Only one default may be pushed (each +.Ic push-default +replaces the previous saved default). .It Xo Ic range=left , .Ic range=right , .Ic range=window|X , @@ -4602,7 +4770,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1Ni +.Op Fl 1ikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -4652,6 +4820,10 @@ but any quotation marks are escaped. .Fl 1 makes the prompt only accept one key press, in this case the resulting input is a single character. +.Fl k +is like +.Fl 1 +but the key press is translated to a key name. .Fl N makes the prompt only accept numeric key presses. .Fl i @@ -4815,7 +4987,7 @@ When the option is reached, the oldest automatically named buffer is deleted. Explicitly named buffers are not subject to .Ic buffer-limit -and may be deleted with +and may be deleted with the .Ic delete-buffer command. .Pp @@ -4842,7 +5014,7 @@ The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose-buffer -.Op Fl NZ +.Op Fl NZr .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -4869,7 +5041,8 @@ The following keys may be used in buffer mode: .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -4884,11 +5057,13 @@ If is not given, "paste-buffer -b '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql time , .Ql name or .Ql size . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -5058,6 +5233,37 @@ channel are made to wait until the channel is unlocked with .Ic wait-for .Fl U . .El +.Sh EXIT MESSAGES +When a +.Nm +client detaches, it prints a message. +This may be one of: +.Bl -tag -width Ds +.It [detached (from session ...)] +The client was detached normally. +.It [detached and SIGHUP] +The client was detached and its parent sent the +.Dv SIGHUP +signal (for example with +.Ic detach-client +.Fl P ) . +.It [lost tty] +The client's +.Xr tty 4 +or +.Xr pty 4 +was unexpectedly destroyed. +.It [terminated] +The client was killed with +.Dv SIGTERM . +.It [exited] +The server exited when it had no sessions. +.It [server exited] +The server exited when it received +.Dv SIGTERM . +.It [server exited unexpectedly] +The server crashed or otherwise exited without telling the client the reason. +.El .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to diff --git a/external/bsd/tmux/dist/tmux.c b/external/bsd/tmux/dist/tmux.c index f9de920c6bb6..3c1feccc4e93 100644 --- a/external/bsd/tmux/dist/tmux.c +++ b/external/bsd/tmux/dist/tmux.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -127,6 +128,7 @@ make_label(const char *label, char **cause) free(base); goto fail; } + free(base); if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) goto fail; @@ -208,6 +210,12 @@ find_home(void) return (home); } +const char * +getversion(void) +{ + return TMUX_VERSION; +} + int main(int argc, char **argv) { @@ -234,7 +242,7 @@ main(int argc, char **argv) flags = 0; label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -248,12 +256,12 @@ main(int argc, char **argv) else flags |= CLIENT_CONTROL; break; - case 'V': - printf("%s %s\n", getprogname(), VERSION); - exit(0); case 'f': set_cfg_file(optarg); break; + case 'V': + printf("%s %s\n", getprogname(), getversion()); + exit(0); case 'l': flags |= CLIENT_LOGIN; break; diff --git a/external/bsd/tmux/dist/tmux.h b/external/bsd/tmux/dist/tmux.h index 432875e221e0..4a98a6ff3ed5 100644 --- a/external/bsd/tmux/dist/tmux.h +++ b/external/bsd/tmux/dist/tmux.h @@ -62,9 +62,9 @@ struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 -/* Default global configuration file. */ +/* Default configuration files. */ #ifndef TMUX_CONF -#define TMUX_CONF "/etc/tmux.conf" +#define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" #endif /* Minimum layout cell size, NOT including border lines. */ @@ -80,6 +80,10 @@ struct winlink; /* Maximum size of data to hold from a pane. */ #define READ_SIZE 4096 +/* Default pixel cell sizes. */ +#define DEFAULT_XPIXEL 16 +#define DEFAULT_YPIXEL 32 + /* Attribute to make GCC check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) @@ -476,13 +480,21 @@ enum msgtype { MSG_RESIZE, MSG_SHELL, MSG_SHUTDOWN, - MSG_STDERR, - MSG_STDIN, - MSG_STDOUT, + MSG_OLDSTDERR, /* unused */ + MSG_OLDSTDIN, /* unused */ + MSG_OLDSTDOUT, /* unused */ MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, MSG_EXEC, + + MSG_READ_OPEN = 300, + MSG_READ, + MSG_READ_DONE, + MSG_WRITE_OPEN, + MSG_WRITE, + MSG_WRITE_READY, + MSG_WRITE_CLOSE }; /* @@ -490,23 +502,41 @@ enum msgtype { * * Don't forget to bump PROTOCOL_VERSION if any of these change! */ -struct msg_command_data { +struct msg_command { int argc; }; /* followed by packed argv */ -struct msg_stdin_data { - ssize_t size; - char data[BUFSIZ]; +struct msg_read_open { + int stream; + int fd; +}; /* followed by path */ + +struct msg_read_data { + int stream; }; -struct msg_stdout_data { - ssize_t size; - char data[BUFSIZ]; +struct msg_read_done { + int stream; + int error; }; -struct msg_stderr_data { - ssize_t size; - char data[BUFSIZ]; +struct msg_write_open { + int stream; + int fd; + int flags; +}; /* followed by path */ + +struct msg_write_data { + int stream; +}; /* followed by data */ + +struct msg_write_ready { + int stream; + int error; +}; + +struct msg_write_close { + int stream; }; /* Mode keys. */ @@ -682,6 +712,13 @@ struct style_range { }; TAILQ_HEAD(style_ranges, style_range); +/* Style default. */ +enum style_default_type { + STYLE_DEFAULT_BASE, + STYLE_DEFAULT_PUSH, + STYLE_DEFAULT_POP +}; + /* Style option. */ struct style { struct grid_cell gc; @@ -692,6 +729,8 @@ struct style { enum style_range_type range_type; u_int range_argument; + + enum style_default_type default_type; }; /* Virtual screen. */ @@ -699,6 +738,7 @@ struct screen_sel; struct screen_titles; struct screen { char *title; + char *path; struct screen_titles *titles; struct grid *grid; /* grid data */ @@ -845,6 +885,7 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 +#define PANE_RESIZED 0x2000 int argc; char **argv; @@ -886,7 +927,9 @@ struct window_pane { TAILQ_HEAD (, window_mode_entry) modes; struct event modetimer; time_t modelast; + char *searchstr; + int searchregex; TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; @@ -897,6 +940,7 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { u_int id; + void *latest; char *name; struct event name_event; @@ -918,12 +962,15 @@ struct window { u_int sx; u_int sy; + u_int xpixel; + u_int ypixel; int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 +#define WINDOW_WASZOOMED 0x10 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -961,6 +1008,7 @@ TAILQ_HEAD(winlink_stack, winlink); #define WINDOW_SIZE_LARGEST 0 #define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_MANUAL 2 +#define WINDOW_SIZE_LATEST 3 /* Pane border status option. */ #define PANE_STATUS_OFF 0 @@ -1124,7 +1172,10 @@ struct tty_term { struct tty_code *codes; #define TERM_256COLOURS 0x1 -#define TERM_EARLYWRAP 0x2 +#define TERM_NOXENL 0x2 +#define TERM_DECSLRM 0x4 +#define TERM_DECFRA 0x8 +#define TERM_RGBCOLOURS 0x10 int flags; LIST_ENTRY(tty_term) entry; @@ -1133,9 +1184,12 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; + struct event start_timer; u_int sx; u_int sy; + u_int xpixel; + u_int ypixel; u_int cx; u_int cy; @@ -1179,20 +1233,13 @@ struct tty { #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 +#define TTY_HAVEDA 0x100 +#define TTY_HAVEDSR 0x200 int flags; struct tty_term *term; char *term_name; int term_flags; - enum { - TTY_VT100, - TTY_VT101, - TTY_VT102, - TTY_VT220, - TTY_VT320, - TTY_VT420, - TTY_UNKNOWN - } term_type; u_int mouse_last_x; u_int mouse_last_y; @@ -1206,8 +1253,6 @@ struct tty { struct event key_timer; struct tty_key *key_tree; }; -#define TTY_TYPES \ - { "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "Unknown" } /* TTY command context. */ struct tty_ctx { @@ -1216,8 +1261,8 @@ struct tty_ctx { const struct grid_cell *cell; int wrapped; - u_int num; - void *ptr; + u_int num; + void *ptr; /* * Cursor and region position before the screen was updated - this is @@ -1446,6 +1491,29 @@ struct status_line { struct status_line_entry entries[STATUS_LINES_LIMIT]; }; +/* File in client. */ +typedef void (*client_file_cb) (struct client *, const char *, int, int, + struct evbuffer *, void *); +struct client_file { + struct client *c; + int references; + int stream; + + char *path; + struct evbuffer *buffer; + struct bufferevent *event; + + int fd; + int error; + int closed; + + client_file_cb cb; + void *data; + + RB_ENTRY (client_file) entry; +}; +RB_HEAD(client_files, client_file); + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1479,13 +1547,6 @@ struct client { size_t discarded; size_t redraw; - void (*stdin_callback)(struct client *, int, void *); - void *stdin_callback_data; - struct evbuffer *stdin_data; - int stdin_closed; - struct evbuffer *stdout_data; - struct evbuffer *stderr_data; - struct event repeat_timer; struct event click_timer; @@ -1526,6 +1587,10 @@ struct client { CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ CLIENT_REDRAWOVERLAY) +#define CLIENT_UNATTACHEDFLAGS \ + (CLIENT_DEAD| \ + CLIENT_SUSPENDED| \ + CLIENT_DETACHING) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ @@ -1552,6 +1617,7 @@ struct client { #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 +#define PROMPT_KEY 0x10 int prompt_flags; struct session *session; @@ -1569,6 +1635,8 @@ struct client { void *overlay_data; struct event overlay_timer; + struct client_files files; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -1577,6 +1645,7 @@ TAILQ_HEAD(clients, client); struct key_binding { key_code key; struct cmd_list *cmdlist; + const char *note; int flags; #define KEY_BINDING_REPEAT 0x1 @@ -1661,6 +1730,7 @@ struct spawn_context { struct session *s; struct winlink *wl; + struct client *c; struct window_pane *wp0; struct layout_cell *lc; @@ -1683,6 +1753,12 @@ struct spawn_context { #define SPAWN_EMPTY 0x40 }; +/* Mode tree sort order. */ +struct mode_tree_sort_criteria { + u_int field; + int reversed; +}; + /* tmux.c */ extern struct options *global_options; extern struct options *global_s_options; @@ -1697,6 +1773,7 @@ int areshell(const char *); void setblocking(int, int); const char *find_cwd(void); const char *find_home(void); +const char *getversion(void); /* proc.c */ struct imsg; @@ -1718,6 +1795,8 @@ extern struct client *cfg_client; void start_cfg(void); int load_cfg(const char *, struct client *, struct cmdq_item *, int, struct cmdq_item **); +int load_cfg_from_buffer(const void *, size_t, const char *, + struct client *, struct cmdq_item *, int, struct cmdq_item **); void set_cfg_file(const char *); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmdq_item *); @@ -1769,6 +1848,8 @@ void format_defaults_pane(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); void format_lost_client(struct client *); +char *format_grid_word(struct grid *, u_int, u_int); +char *format_grid_line(struct grid *, u_int); /* format-draw.c */ void format_draw(struct screen_write_ctx *, @@ -1898,7 +1979,7 @@ void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); int tty_init(struct tty *, struct client *, int, char *); void tty_resize(struct tty *); -void tty_set_size(struct tty *, u_int, u_int); +void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); @@ -1908,7 +1989,7 @@ void tty_draw_line(struct tty *, struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_set_type(struct tty *, int); +void tty_set_flags(struct tty *, int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -2033,6 +2114,8 @@ void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); +struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t, + struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); @@ -2049,8 +2132,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); -void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); -void cmdq_append(struct client *, struct cmdq_item *); +struct cmdq_item *cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); +struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *); void printflike(4, 5) cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); @@ -2075,7 +2158,8 @@ void key_bindings_unref_table(struct key_table *); struct key_binding *key_bindings_get(struct key_table *, key_code); struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); -void key_bindings_add(const char *, key_code, int, struct cmd_list *); +void key_bindings_add(const char *, key_code, const char *, int, + struct cmd_list *); void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); @@ -2092,6 +2176,23 @@ void alerts_reset_all(void); void alerts_queue(struct window *, int); void alerts_check_session(struct session *); +/* file.c */ +int file_cmp(struct client_file *, struct client_file *); +RB_PROTOTYPE(client_files, client_file, entry, file_cmp); +struct client_file *file_create(struct client *, int, client_file_cb, void *); +void file_free(struct client_file *); +void file_fire_done(struct client_file *); +void file_fire_read(struct client_file *); +int file_can_print(struct client *); +void printflike(2, 3) file_print(struct client *, const char *, ...); +void file_vprint(struct client *, const char *, va_list); +void file_print_buffer(struct client *, void *, size_t); +void printflike(2, 3) file_error(struct client *, const char *, ...); +void file_write(struct client *, const char *, int, const void *, size_t, + client_file_cb, void *); +void file_read(struct client *, const char *, client_file_cb, void *); +void file_push(struct client_file *); + /* server.c */ extern struct tmuxproc *server_proc; extern struct clients clients; @@ -2126,7 +2227,6 @@ void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); void printflike(2, 3) server_client_add_message(struct client *, const char *, ...); -char *server_client_get_path(struct client *, const char *); const char *server_client_get_cwd(struct client *, struct session *); /* server-fn.c */ @@ -2150,8 +2250,6 @@ void server_unlink_window(struct session *, struct winlink *); void server_destroy_pane(struct window_pane *, int); void server_destroy_session(struct session *); void server_check_unattached(void); -int server_set_stdin_callback(struct client *, void (*)(struct client *, - int, void *), void *, char **); void server_unzoom_window(struct window *); /* status.c */ @@ -2177,9 +2275,10 @@ void status_prompt_load_history(void); void status_prompt_save_history(void); /* resize.c */ -void resize_window(struct window *, u_int, u_int); -void default_window_size(struct session *, struct window *, u_int *, - u_int *, int); +void resize_window(struct window *, u_int, u_int, int, int); +void default_window_size(struct client *, struct session *, struct window *, + u_int *, u_int *, u_int *, u_int *, int); +void recalculate_size(struct window *); void recalculate_sizes(void); /* input.c */ @@ -2191,7 +2290,7 @@ void input_parse(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); /* input-key.c */ -void input_key(struct window_pane *, key_code, struct mouse_event *); +int input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); @@ -2329,7 +2428,8 @@ void screen_free(struct screen *); void screen_reset_tabs(struct screen *); void screen_set_cursor_style(struct screen *, u_int); void screen_set_cursor_colour(struct screen *, const char *); -void screen_set_title(struct screen *, const char *); +int screen_set_title(struct screen *, const char *); +void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); @@ -2369,7 +2469,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); -struct window *window_create(u_int, u_int); +struct window *window_create(u_int, u_int, u_int, u_int); void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); @@ -2380,9 +2480,12 @@ void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); -void window_resize(struct window *, u_int, u_int); +void window_resize(struct window *, u_int, u_int, int, int); +void window_pane_send_resize(struct window_pane *, int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); +int window_push_zoom(struct window *, int); +int window_pop_zoom(struct window *); void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); @@ -2410,7 +2513,7 @@ int window_pane_set_mode(struct window_pane *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); -void window_pane_key(struct window_pane *, struct client *, +int window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); @@ -2472,7 +2575,8 @@ u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); /* mode-tree.c */ -typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *); +typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *, + uint64_t *, const char *); typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); typedef int (*mode_tree_search_cb)(void *, void *, const char *); @@ -2530,9 +2634,8 @@ char *default_window_name(struct window *); char *parse_window_name(const char *); /* control.c */ -void control_callback(struct client *, int, void *); +void control_start(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); -void control_write_buffer(struct client *, struct evbuffer *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, @@ -2585,6 +2688,7 @@ void session_group_add(struct session_group *, struct session *); void session_group_synchronize_to(struct session *); void session_group_synchronize_from(struct session *); u_int session_group_count(struct session_group *); +u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ @@ -2604,6 +2708,7 @@ struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_padcstr(const char *, u_int); +char *utf8_rpadcstr(const char *, u_int); int utf8_cstrhas(const char *, const struct utf8_data *); /* osdep-*.c */ @@ -2641,8 +2746,6 @@ int style_parse(struct style *,const struct grid_cell *, const char *style_tostring(struct style *); void style_apply(struct grid_cell *, struct options *, const char *); -void style_apply_update(struct grid_cell *, struct options *, - const char *); int style_equal(struct style *, struct style *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); diff --git a/external/bsd/tmux/dist/tty-keys.c b/external/bsd/tmux/dist/tty-keys.c index 0bffd4083975..c85ba388ee23 100644 --- a/external/bsd/tmux/dist/tty-keys.c +++ b/external/bsd/tmux/dist/tty-keys.c @@ -52,6 +52,8 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t, size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); +static int tty_keys_device_status_report(struct tty *, const char *, + size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -607,6 +609,17 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this a device status report response? */ + switch (tty_keys_device_status_report(tty, buf, len, &size)) { + case 0: /* yes */ + key = KEYC_UNKNOWN; + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size, &m)) { case 0: /* yes */ @@ -1002,13 +1015,14 @@ static int tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { - struct client *c = tty->client; - u_int i, a, b; - char tmp[64], *endptr; - static const char *types[] = TTY_TYPES; - int type; + struct client *c = tty->client; + u_int i, n = 0; + char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; + int flags = 0; *size = 0; + if (tty->flags & TTY_HAVEDA) + return (-1); /* First three bytes are always \033[?. */ if (buf[0] != '\033') @@ -1036,39 +1050,81 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, *size = 4 + i; /* Convert version numbers. */ - a = strtoul(tmp, &endptr, 10); - if (*endptr == ';') { - b = strtoul(endptr + 1, &endptr, 10); - if (*endptr != '\0' && *endptr != ';') - b = 0; - } else - a = b = 0; + cp = tmp; + while ((next = strsep(&cp, ";")) != NULL) { + p[n] = strtoul(next, &endptr, 10); + if (*endptr != '\0') + p[n] = 0; + n++; + } - /* Store terminal type. */ - type = TTY_UNKNOWN; - switch (a) { - case 1: - if (b == 2) - type = TTY_VT100; - else if (b == 0) - type = TTY_VT101; - break; - case 6: - type = TTY_VT102; - break; - case 62: - type = TTY_VT220; - break; - case 63: - type = TTY_VT320; - break; - case 64: - type = TTY_VT420; + /* Set terminal flags. */ + switch (p[0]) { + case 64: /* VT420 */ + flags |= (TERM_DECFRA|TERM_DECSLRM); break; } - tty_set_type(tty, type); + for (i = 1; i < n; i++) + log_debug("%s: DA feature: %d", c->name, p[i]); + log_debug("%s: received DA %.*s", c->name, (int)*size, buf); + + tty_set_flags(tty, flags); + tty->flags |= TTY_HAVEDA; + + return (0); +} + +/* + * Handle device status report input. Returns 0 for success, -1 for failure, 1 + * for partial. + */ +static int +tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, + size_t *size) +{ + struct client *c = tty->client; + u_int i; + char tmp[64]; + int flags = 0; + + *size = 0; + if (tty->flags & TTY_HAVEDSR) + return (-1); + + /* First three bytes are always \033[. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + if (buf[2] != 'I' && buf[2] != 'T') + return (-1); + if (len == 3) + return (1); + + /* Copy the rest up to a 'n'. */ + for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { + if (2 + i == len) + return (1); + tmp[i] = buf[2 + i]; + } + if (i == (sizeof tmp) - 1) + return (-1); + tmp[i] = '\0'; + *size = 3 + i; + + /* Set terminal flags. */ + if (strncmp(tmp, "ITERM2 ", 7) == 0) + flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS); + if (strncmp(tmp, "TMUX ", 5) == 0) + flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); + log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); + + tty_set_flags(tty, flags); + tty->flags |= TTY_HAVEDSR; - log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf, - types[type]); return (0); } diff --git a/external/bsd/tmux/dist/tty-term.c b/external/bsd/tmux/dist/tty-term.c index 0e814241284a..443feb3971bb 100644 --- a/external/bsd/tmux/dist/tty-term.c +++ b/external/bsd/tmux/dist/tty-term.c @@ -528,11 +528,16 @@ tty_term_find(char *name, int fd, char **cause) goto error; } - /* Figure out if we have 256 colours (or more). */ - if (tty_term_number(term, TTYC_COLORS) >= 256 || - tty_term_has(term, TTYC_RGB)) + /* Set flag if terminal has 256 colours. */ + if (tty_term_number(term, TTYC_COLORS) >= 256) term->flags |= TERM_256COLOURS; + /* Set flag if terminal has RGB colours. */ + if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) || + (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB))) + term->flags |= TERM_RGBCOLOURS; + /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). @@ -544,7 +549,7 @@ tty_term_find(char *name, int fd, char **cause) * do the best possible. */ if (!tty_term_flag(term, TTYC_XENL)) - term->flags |= TERM_EARLYWRAP; + term->flags |= TERM_NOXENL; /* Generate ACS table. If none is present, use nearest ASCII. */ memset(term->acs, 0, sizeof term->acs); @@ -567,22 +572,7 @@ tty_term_find(char *name, int fd, char **cause) code->type = TTYCODE_STRING; } - /* - * On terminals with RGB colour (Tc or RGB), fill in setrgbf and - * setrgbb if they are missing. - */ - if ((tty_term_flag(term, TTYC_TC) || tty_term_flag(term, TTYC_RGB)) && - !tty_term_has(term, TTYC_SETRGBF) && - !tty_term_has(term, TTYC_SETRGBB)) { - code = &term->codes[TTYC_SETRGBF]; - code->value.string = xstrdup("\033[38;2;%p1%d;%p2%d;%p3%dm"); - code->type = TTYCODE_STRING; - code = &term->codes[TTYC_SETRGBB]; - code->value.string = xstrdup("\033[48;2;%p1%d;%p2%d;%p3%dm"); - code->type = TTYCODE_STRING; - } - - /* Log it. */ + /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) log_debug("%s%s", name, tty_term_describe(term, i)); @@ -642,7 +632,8 @@ tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) } const char * -tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, int c) +tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, + int c) { return (tparm(tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0)); } diff --git a/external/bsd/tmux/dist/tty.c b/external/bsd/tmux/dist/tty.c index 48997f35210e..88e9167be19f 100644 --- a/external/bsd/tmux/dist/tty.c +++ b/external/bsd/tmux/dist/tty.c @@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ - ((tty)->term_type == TTY_VT420) + ((tty->term->flags|tty->term_flags) & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -115,9 +115,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->ccolour = xstrdup(""); tty->flags = 0; - tty->term_flags = 0; - tty->term_type = TTY_UNKNOWN; return (0); } @@ -127,29 +125,40 @@ tty_resize(struct tty *tty) { struct client *c = tty->client; struct winsize ws; - u_int sx, sy; + u_int sx, sy, xpixel, ypixel; if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { sx = ws.ws_col; - if (sx == 0) + if (sx == 0) { sx = 80; + xpixel = 0; + } else + xpixel = ws.ws_xpixel / sx; sy = ws.ws_row; - if (sy == 0) + if (sy == 0) { sy = 24; + ypixel = 0; + } else + ypixel = ws.ws_ypixel / sy; } else { sx = 80; sy = 24; + xpixel = 0; + ypixel = 0; } - log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); - tty_set_size(tty, sx, sy); + log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy, + xpixel, ypixel); + tty_set_size(tty, sx, sy, xpixel, ypixel); tty_invalidate(tty); } void -tty_set_size(struct tty *tty, u_int sx, u_int sy) +tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel) { tty->sx = sx; tty->sy = sy; + tty->xpixel = xpixel; + tty->ypixel = ypixel; } static void @@ -276,11 +285,22 @@ tty_open(struct tty *tty, char **cause) return (0); } +static void +tty_start_timer_callback(__unused int fd, __unused short events, void *data) +{ + struct tty *tty = data; + struct client *c = tty->client; + + log_debug("%s: start timer fired", c->name); + tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); +} + void tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; + struct timeval tv = { .tv_sec = 1 }; if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { setblocking(tty->fd, 0); @@ -310,21 +330,31 @@ tty_start_tty(struct tty *tty) log_debug("%s: using UTF-8 for ACS", c->name); tty_putcode(tty, TTYC_CNORM); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_puts(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c"); - } + if (~tty->flags & TTY_HAVEDA) + tty_puts(tty, "\033[c"); + if (~tty->flags & TTY_HAVEDSR) + tty_puts(tty, "\033[1337n"); + } else + tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + + evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); + evtimer_add(&tty->start_timer, &tv); tty->flags |= TTY_STARTED; tty_invalidate(tty); - tty_force_cursor_colour(tty, ""); + if (*tty->ccolour != '\0') + tty_force_cursor_colour(tty, ""); tty->mouse_drag_flag = 0; tty->mouse_drag_update = NULL; @@ -340,6 +370,8 @@ tty_stop_tty(struct tty *tty) return; tty->flags &= ~TTY_STARTED; + evtimer_del(&tty->start_timer); + event_del(&tty->timer); tty->flags &= ~TTY_BLOCK; @@ -370,11 +402,14 @@ tty_stop_tty(struct tty *tty) } if (tty->mode & MODE_BRACKETPASTE) tty_raw(tty, "\033[?2004l"); - tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); + if (*tty->ccolour != '\0') + tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_raw(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { @@ -425,9 +460,9 @@ tty_free(struct tty *tty) } void -tty_set_type(struct tty *tty, int type) +tty_set_flags(struct tty *tty, int flags) { - tty->term_type = type; + tty->term_flags |= flags; if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ @@ -530,7 +565,7 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) @@ -556,7 +591,7 @@ tty_putc(struct tty *tty, u_char ch) * where we think it should be after a line wrap - this * means it works on sensible terminals as well. */ - if (tty->term->flags & TERM_EARLYWRAP) + if (tty->term->flags & TERM_NOXENL) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; @@ -566,7 +601,7 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; @@ -1116,7 +1151,8 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if (tty->term_type == TTY_VT420 && !COLOUR_DEFAULT(bg)) { + if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) && + !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); @@ -1811,7 +1847,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_EARLYWRAP) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || @@ -1860,7 +1896,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) const struct grid_cell *gcp; /* Skip last character if terminal is stupid. */ - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; @@ -2019,7 +2055,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_EARLYWRAP) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || @@ -2106,7 +2142,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { tty_putcode1(tty, TTYC_HPA, cx); goto out; - } else if (change > 0 && tty_term_has(term, TTYC_CUB)) { + } else if (change > 0 && + tty_term_has(term, TTYC_CUB) && + !tty_use_margin(tty)) { if (change == 2 && tty_term_has(term, TTYC_CUB1)) { tty_putcode(tty, TTYC_CUB1); tty_putcode(tty, TTYC_CUB1); @@ -2114,7 +2152,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) } tty_putcode1(tty, TTYC_CUB, change); goto out; - } else if (change < 0 && tty_term_has(term, TTYC_CUF)) { + } else if (change < 0 && + tty_term_has(term, TTYC_CUF) && + !tty_use_margin(tty)) { tty_putcode1(tty, TTYC_CUF, -change); goto out; } @@ -2352,11 +2392,10 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (!tty_term_has(tty->term, TTYC_SETRGBF)) { - colour_split_rgb(gc->fg, &r, &g, &b); - gc->fg = colour_find_rgb(r, g, b); - } else + if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) return; + colour_split_rgb(gc->fg, &r, &g, &b); + gc->fg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ @@ -2374,10 +2413,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) gc->fg &= 7; if (colours >= 16) gc->fg += 90; - else - gc->attr |= GRID_ATTR_BRIGHT; - } else - gc->attr &= ~GRID_ATTR_BRIGHT; + } } return; } @@ -2405,11 +2441,10 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (!tty_term_has(tty->term, TTYC_SETRGBB)) { - colour_split_rgb(gc->bg, &r, &g, &b); - gc->bg = colour_find_rgb(r, g, b); - } else + if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) return; + colour_split_rgb(gc->bg, &r, &g, &b); + gc->bg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ @@ -2442,7 +2477,8 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void -tty_check_us(__unused struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +tty_check_us(__unused struct tty *tty, struct window_pane *wp, + struct grid_cell *gc) { int c; @@ -2585,15 +2621,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type) } if (colour & COLOUR_FLAG_RGB) { + colour_split_rgb(colour & 0xffffff, &r, &g, &b); if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETRGBF)) - return (-1); - colour_split_rgb(colour & 0xffffff, &r, &g, &b); + goto fallback_rgb; tty_putcode3(tty, TTYC_SETRGBF, r, g, b); } else { if (!tty_term_has(tty->term, TTYC_SETRGBB)) - return (-1); - colour_split_rgb(colour & 0xffffff, &r, &g, &b); + goto fallback_rgb; tty_putcode3(tty, TTYC_SETRGBB, r, g, b); } return (0); @@ -2606,6 +2641,12 @@ fallback_256: log_debug("%s: 256 colour fallback: %s", tty->client->name, s); tty_puts(tty, s); return (0); + +fallback_rgb: + xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b); + log_debug("%s: RGB colour fallback: %s", tty->client->name, s); + tty_puts(tty, s); + return (0); } static void diff --git a/external/bsd/tmux/dist/utf8.c b/external/bsd/tmux/dist/utf8.c index 0dce24537e18..44a1967a7d6b 100644 --- a/external/bsd/tmux/dist/utf8.c +++ b/external/bsd/tmux/dist/utf8.c @@ -415,7 +415,7 @@ utf8_cstrwidth(const char *s) return (width); } -/* Pad UTF-8 string to width. Caller frees. */ +/* Pad UTF-8 string to width on the left. Caller frees. */ char * utf8_padcstr(const char *s, u_int width) { @@ -436,6 +436,27 @@ utf8_padcstr(const char *s, u_int width) return (out); } +/* Pad UTF-8 string to width on the right. Caller frees. */ +char * +utf8_rpadcstr(const char *s, u_int width) +{ + size_t slen; + char *out; + u_int n, i; + + n = utf8_cstrwidth(s); + if (n >= width) + return (xstrdup(s)); + + slen = strlen(s); + out = xmalloc(slen + 1 + (width - n)); + for (i = 0; i < width - n; i++) + out[i] = ' '; + memcpy(out + i, s, slen); + out[i + slen] = '\0'; + return (out); +} + int utf8_cstrhas(const char *s, const struct utf8_data *ud) { diff --git a/external/bsd/tmux/dist/window-buffer.c b/external/bsd/tmux/dist/window-buffer.c index fca06c5de6a7..bed1dfd7081d 100644 --- a/external/bsd/tmux/dist/window-buffer.c +++ b/external/bsd/tmux/dist/window-buffer.c @@ -74,6 +74,7 @@ static const char *window_buffer_sort_list[] = { "name", "size" }; +static struct mode_tree_sort_criteria *window_buffer_sort; struct window_buffer_itemdata { const char *name; @@ -112,43 +113,29 @@ window_buffer_free_item(struct window_buffer_itemdata *item) } static int -window_buffer_cmp_name(const void *a0, const void *b0) +window_buffer_cmp(const void *a0, const void *b0) { - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; + const struct window_buffer_itemdata *const *a = a0; + const struct window_buffer_itemdata *const *b = b0; + int result = 0; - return (strcmp((*a)->name, (*b)->name)); -} + if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME) + result = (*b)->order - (*a)->order; + else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE) + result = (*b)->size - (*a)->size; -static int -window_buffer_cmp_time(const void *a0, const void *b0) -{ - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; + /* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */ + if (result == 0) + result = strcmp((*a)->name, (*b)->name); - if ((*a)->order > (*b)->order) - return (-1); - if ((*a)->order < (*b)->order) - return (1); - return (strcmp((*a)->name, (*b)->name)); -} - -static int -window_buffer_cmp_size(const void *a0, const void *b0) -{ - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; - - if ((*a)->size > (*b)->size) - return (-1); - if ((*a)->size < (*b)->size) - return (1); - return (strcmp((*a)->name, (*b)->name)); + if (window_buffer_sort->reversed) + result = -result; + return (result); } static void -window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, - const char *filter) +window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + __unused uint64_t *tag, const char *filter) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item; @@ -174,20 +161,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, item->order = paste_buffer_order(pb); } - switch (sort_type) { - case WINDOW_BUFFER_BY_NAME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_name); - break; - case WINDOW_BUFFER_BY_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_time); - break; - case WINDOW_BUFFER_BY_SIZE: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_size); - break; - } + window_buffer_sort = sort_crit; + qsort(data->item_list, data->item_size, sizeof *data->item_list, + window_buffer_cmp); if (cmd_find_valid_state(&data->fs)) { s = data->fs.s; @@ -231,9 +207,9 @@ window_buffer_draw(__unused void *modedata, void *itemdata, { struct window_buffer_itemdata *item = itemdata; struct paste_buffer *pb; - char line[1024]; - const char *pdata, *end, *cp; - size_t psize, at; + const char *pdata, *start, *end; + char *buf = NULL; + size_t psize; u_int i, cx = ctx->s->cx, cy = ctx->s->cy; pb = paste_get_name(item->name); @@ -242,27 +218,22 @@ window_buffer_draw(__unused void *modedata, void *itemdata, pdata = end = paste_buffer_data(pb, &psize); for (i = 0; i < sy; i++) { - at = 0; - while (end != pdata + psize && *end != '\n') { - if ((sizeof line) - at > 5) { - cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0); - at = cp - line; - } + start = end; + while (end != pdata + psize && *end != '\n') end++; - } - if (at > sx) - at = sx; - line[at] = '\0'; - - if (*line != '\0') { + buf = xreallocarray(buf, 4, end - start + 1); + utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB); + if (*buf != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); - screen_write_puts(ctx, &grid_default_cell, "%s", line); + screen_write_nputs(ctx, sx, &grid_default_cell, "%s", + buf); } if (end == pdata + psize) break; end++; } + free(buf); } static int @@ -357,7 +328,7 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) } static void -window_buffer_do_delete(void* modedata, void *itemdata, +window_buffer_do_delete(void *modedata, void *itemdata, __unused struct client *c, __unused key_code key) { struct window_buffer_modedata *data = modedata; @@ -371,7 +342,7 @@ window_buffer_do_delete(void* modedata, void *itemdata, } static void -window_buffer_do_paste(void* modedata, void *itemdata, struct client *c, +window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, __unused key_code key) { struct window_buffer_modedata *data = modedata; diff --git a/external/bsd/tmux/dist/window-client.c b/external/bsd/tmux/dist/window-client.c index a0d8c54b3d84..144c9dc9a5be 100644 --- a/external/bsd/tmux/dist/window-client.c +++ b/external/bsd/tmux/dist/window-client.c @@ -75,6 +75,7 @@ static const char *window_client_sort_list[] = { "creation", "activity" }; +static struct mode_tree_sort_criteria *window_client_sort; struct window_client_itemdata { struct client *c; @@ -110,60 +111,48 @@ window_client_free_item(struct window_client_itemdata *item) } static int -window_client_cmp_name(const void *a0, const void *b0) +window_client_cmp(const void *a0, const void *b0) { - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; + const struct window_client_itemdata *const *a = a0; + const struct window_client_itemdata *const *b = b0; + const struct window_client_itemdata *itema = *a; + const struct window_client_itemdata *itemb = *b; + struct client *ca = itema->c; + struct client *cb = itemb->c; + int result = 0; - return (strcmp((*a)->c->name, (*b)->c->name)); -} + switch (window_client_sort->field) { + case WINDOW_CLIENT_BY_SIZE: + result = ca->tty.sx - cb->tty.sx; + if (result == 0) + result = ca->tty.sy - cb->tty.sy; + break; + case WINDOW_CLIENT_BY_CREATION_TIME: + if (timercmp(&ca->creation_time, &cb->creation_time, >)) + result = -1; + else if (timercmp(&ca->creation_time, &cb->creation_time, <)) + result = 1; + break; + case WINDOW_CLIENT_BY_ACTIVITY_TIME: + if (timercmp(&ca->activity_time, &cb->activity_time, >)) + result = -1; + else if (timercmp(&ca->activity_time, &cb->activity_time, <)) + result = 1; + break; + } -static int -window_client_cmp_size(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; + /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */ + if (result == 0) + result = strcmp(ca->name, cb->name); - if ((*a)->c->tty.sx < (*b)->c->tty.sx) - return (-1); - if ((*a)->c->tty.sx > (*b)->c->tty.sx) - return (1); - if ((*a)->c->tty.sy < (*b)->c->tty.sy) - return (-1); - if ((*a)->c->tty.sy > (*b)->c->tty.sy) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); -} - -static int -window_client_cmp_creation_time(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >)) - return (-1); - if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <)) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); -} - -static int -window_client_cmp_activity_time(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >)) - return (-1); - if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <)) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); + if (window_client_sort->reversed) + result = -result; + return (result); } static void -window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, - const char *filter) +window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + __unused uint64_t *tag, const char *filter) { struct window_client_modedata *data = modedata; struct window_client_itemdata *item; @@ -187,24 +176,9 @@ window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, c->references++; } - switch (sort_type) { - case WINDOW_CLIENT_BY_NAME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_name); - break; - case WINDOW_CLIENT_BY_SIZE: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_size); - break; - case WINDOW_CLIENT_BY_CREATION_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_creation_time); - break; - case WINDOW_CLIENT_BY_ACTIVITY_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_activity_time); - break; - } + window_client_sort = sort_crit; + qsort(data->item_list, data->item_size, sizeof *data->item_list, + window_client_cmp); for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; @@ -236,7 +210,7 @@ window_client_draw(__unused void *modedata, void *itemdata, struct window_pane *wp; u_int cx = s->cx, cy = s->cy, lines, at; - if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING))) + if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return; wp = c->session->curw->window->active; @@ -339,7 +313,7 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy) } static void -window_client_do_detach(void* modedata, void *itemdata, +window_client_do_detach(void *modedata, void *itemdata, __unused struct client *c, key_code key) { struct window_client_modedata *data = modedata; diff --git a/external/bsd/tmux/dist/window-copy.c b/external/bsd/tmux/dist/window-copy.c index e9c232681664..4d33291d40e5 100644 --- a/external/bsd/tmux/dist/window-copy.c +++ b/external/bsd/tmux/dist/window-copy.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -57,27 +58,35 @@ static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); +static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, + u_int last, u_int len, u_int *ppx, u_int *psx, + const char *buf, const regex_t *preg, int eflags); +static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, + char *, u_int *); +static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *, + const char *str); static int window_copy_search_marks(struct window_mode_entry *, - struct screen *); + struct screen *, int); static void window_copy_clear_marks(struct window_mode_entry *); -static void window_copy_move_left(struct screen *, u_int *, u_int *); -static void window_copy_move_right(struct screen *, u_int *, u_int *); +static void window_copy_move_left(struct screen *, u_int *, u_int *, int); +static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, - int); -static int window_copy_search(struct window_mode_entry *, int); -static int window_copy_search_up(struct window_mode_entry *); -static int window_copy_search_down(struct window_mode_entry *); + int, int); +static int window_copy_search(struct window_mode_entry *, int, int); +static int window_copy_search_up(struct window_mode_entry *, int); +static int window_copy_search_down(struct window_mode_entry *, int); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); static void window_copy_start_selection(struct window_mode_entry *); static int window_copy_adjust_selection(struct window_mode_entry *, u_int *, u_int *); -static int window_copy_set_selection(struct window_mode_entry *, int); -static int window_copy_update_selection(struct window_mode_entry *, int); -static void window_copy_synchronize_cursor(struct window_mode_entry *); +static int window_copy_set_selection(struct window_mode_entry *, int, int); +static int window_copy_update_selection(struct window_mode_entry *, int, + int); +static void window_copy_synchronize_cursor(struct window_mode_entry *, int); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); static void window_copy_copy_buffer(struct window_mode_entry *, const char *, void *, size_t); @@ -107,10 +116,14 @@ static void window_copy_cursor_jump_to(struct window_mode_entry *); static void window_copy_cursor_jump_to_back(struct window_mode_entry *); static void window_copy_cursor_next_word(struct window_mode_entry *, const char *); +static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, + const char *, u_int *, u_int *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, - const char *); + const char *, int); +static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, + const char *, int, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, - const char *); + const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); static void window_copy_scroll_down(struct window_mode_entry *, u_int); static void window_copy_rectangle_toggle(struct window_mode_entry *); @@ -214,6 +227,23 @@ struct window_copy_mode_data { } lineflag; /* line selection mode */ int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ + int hide_position; /* hide position marker */ + + enum { + SEL_CHAR, /* select one char at a time */ + SEL_WORD, /* select one word at a time */ + SEL_LINE, /* select one line at a time */ + } selflag; + + const char *ws; /* word separators */ + + u_int dx; /* drag start position */ + u_int dy; + + u_int selrx; /* selection reset positions */ + u_int selry; + u_int endselrx; + u_int endselry; u_int cx; u_int cy; @@ -222,6 +252,7 @@ struct window_copy_mode_data { u_int lastsx; /* size of last line w/ content */ int searchtype; + int searchregex; char *searchstr; bitstr_t *searchmark; u_int searchcount; @@ -230,6 +261,9 @@ struct window_copy_mode_data { int searchy; int searcho; + int timeout; /* search has timed out */ +#define WINDOW_COPY_SEARCH_TIMEOUT 10 + int jumptype; char jumpchar; @@ -272,16 +306,20 @@ window_copy_common_init(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = wp->searchregex; data->searchstr = xstrdup(wp->searchstr); } else { data->searchtype = WINDOW_COPY_OFF; + data->searchregex = 0; data->searchstr = NULL; } data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; + data->timeout = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -313,6 +351,7 @@ window_copy_init(struct window_mode_entry *wme, data->cy = data->backing->cy; data->scroll_exit = args_has(args, 'e'); + data->hide_position = args_has(args, 'H'); data->screen.cx = data->cx; data->screen.cy = data->cy; @@ -470,7 +509,9 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) window_copy_cursor_end_of_line(wme); } - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -518,7 +559,9 @@ window_copy_pagedown(struct window_mode_entry *wme, int half_page, if (scroll_exit && data->oy == 0) return (1); - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); return (0); } @@ -560,14 +603,63 @@ window_copy_next_paragraph(struct window_mode_entry *wme) window_copy_scroll_to(wme, ox, oy); } +#if 0 +static char * +window_copy_get_word(struct window_pane *wp, u_int x, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_word(gd, x, gd->hsize + y)); +} + +static char * +window_copy_get_line(struct window_pane *wp, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_line(gd, gd->hsize + y)); +} +#endif + static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + char *s; - format_add(ft, "selection_present", "%d", data->screen.sel != NULL); format_add(ft, "scroll_position", "%d", data->oy); format_add(ft, "rectangle_toggle", "%d", data->rectflag); + + format_add(ft, "copy_cursor_x", "%d", data->cx); + format_add(ft, "copy_cursor_y", "%d", data->cy); + + format_add(ft, "selection_present", "%d", data->screen.sel != NULL); + if (data->screen.sel != NULL) { + format_add(ft, "selection_start_x", "%d", data->selx); + format_add(ft, "selection_start_y", "%d", data->sely); + format_add(ft, "selection_end_x", "%d", data->endselx); + format_add(ft, "selection_end_y", "%d", data->endsely); + format_add(ft, "selection_active", "%d", + data->cursordrag != CURSORDRAG_NONE); + } else + format_add(ft, "selection_active", "%d", 0); + + s = format_grid_word(gd, data->cx, gd->hsize + data->cy); + if (s != NULL) { + format_add(ft, "copy_cursor_word", "%s", s); + free(s); + } + + s = format_grid_line(gd, gd->hsize + data->cy); + if (s != NULL) { + format_add(ft, "copy_cursor_line", "%s", s); + free(s); + } } static void @@ -598,8 +690,8 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); - if (search) - window_copy_search_marks(wme, NULL); + if (search && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; @@ -617,6 +709,35 @@ window_copy_key_table(struct window_mode_entry *wme) return ("copy-mode"); } +static int +window_copy_expand_search_string(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + const char *argument; + char *expanded; + + if (cs->args->argc == 2) { + argument = cs->args->argv[1]; + if (*argument != '\0') { + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, argument, NULL, + NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (0); + } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(argument); + } + } + } + return (1); +} + static enum window_copy_cmd_action window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) { @@ -664,6 +785,7 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) } data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; window_copy_start_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -676,6 +798,7 @@ window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; return (WINDOW_COPY_CMD_NOTHING); } @@ -688,7 +811,7 @@ window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = screen_size_y(&data->screen) - 1; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -745,12 +868,14 @@ window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + data->selflag = SEL_CHAR; window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) @@ -819,6 +944,21 @@ window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix, cy; + + cy = data->cy; + for (; np != 0; np--) + window_copy_cursor_down(wme, 0); + if (cy == data->cy && data->oy == 0) + return (WINDOW_COPY_CMD_CANCEL); + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) { @@ -915,7 +1055,9 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) data->cx = window_copy_find_length(wme, data->cy); data->oy = 0; - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -934,7 +1076,9 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) data->cx = 0; data->oy = screen_hsize(data->backing); - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1003,7 +1147,7 @@ window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = (screen_size_y(&data->screen) - 1) / 2; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1048,7 +1192,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_previous_word(wme, "}]) "); + window_copy_cursor_previous_word(wme, "}]) ", 1); } continue; } @@ -1093,7 +1237,6 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } - static enum window_copy_cmd_action window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) { @@ -1161,7 +1304,8 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( "); + window_copy_cursor_next_word_end(wme, "{[( ", + 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1244,7 +1388,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " "); + window_copy_cursor_next_word_end(wme, " ", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1272,7 +1416,7 @@ window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws); + window_copy_cursor_next_word_end(wme, ws, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1281,7 +1425,9 @@ window_copy_cmd_other_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; + struct window_copy_mode_data *data = wme->data; + data->selflag = SEL_CHAR; if ((np % 2) != 0) window_copy_other_end(wme); return (WINDOW_COPY_CMD_NOTHING); @@ -1343,7 +1489,7 @@ window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " "); + window_copy_cursor_previous_word(wme, " ", 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1357,7 +1503,7 @@ window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws); + window_copy_cursor_previous_word(wme, ws, 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1421,10 +1567,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1438,10 +1584,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1455,8 +1601,15 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs) data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; + data->selflag = SEL_LINE; + data->dx = data->cx; + data->dy = screen_hsize(data->backing) + data->cy - data->oy; window_copy_cursor_start_of_line(wme); + data->selrx = data->cx; + data->selry = screen_hsize(data->backing) + data->cy - data->oy; + data->endselrx = window_copy_find_length(wme, data->selry); + data->endselry = data->selry; window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); @@ -1471,15 +1624,34 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; - const char *ws; + u_int px, py; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; + data->selflag = SEL_WORD; + data->dx = data->cx; + data->dy = screen_hsize(data->backing) + data->cy - data->oy; - ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, ws); + data->ws = options_get_string(s->options, "word-separators"); + window_copy_cursor_previous_word(wme, data->ws, 0); + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + data->selrx = px; + data->selry = py; window_copy_start_selection(wme); - window_copy_cursor_next_word_end(wme, ws); + + if (px >= window_copy_find_length(wme, py) || + !window_copy_in_set(wme, px + 1, py, data->ws)) + window_copy_cursor_next_word_end(wme, data->ws, 1); + else { + window_copy_update_cursor(wme, px, data->cy); + if (window_copy_update_selection(wme, 1, 1)) + window_copy_redraw_lines(wme, data->cy, 1); + } + data->endselrx = data->cx; + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; + if (data->dx > data->endselrx) + data->dx = data->endselrx; return (WINDOW_COPY_CMD_REDRAW); } @@ -1502,7 +1674,7 @@ window_copy_cmd_top_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = 0; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1635,14 +1807,36 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; - if (*argument != '\0') { + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; - free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, 1); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; + data->timeout = 0; + for (; np != 0; np--) + window_copy_search_up(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1653,14 +1847,36 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; - if (*argument != '\0') { + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; - free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, 1); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; + data->timeout = 0; + for (; np != 0; np--) + window_copy_search_down(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1675,6 +1891,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -1694,18 +1912,20 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) case '=': case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } break; case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -1724,6 +1944,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -1743,18 +1965,20 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) case '=': case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } break; case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -1766,131 +1990,138 @@ static const struct { const char *command; int minargs; int maxargs; + int ismotion; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { - { "append-selection", 0, 0, + { "append-selection", 0, 0, 0, window_copy_cmd_append_selection }, - { "append-selection-and-cancel", 0, 0, + { "append-selection-and-cancel", 0, 0, 0, window_copy_cmd_append_selection_and_cancel }, - { "back-to-indentation", 0, 0, + { "back-to-indentation", 0, 0, 0, window_copy_cmd_back_to_indentation }, - { "begin-selection", 0, 0, + { "begin-selection", 0, 0, 0, window_copy_cmd_begin_selection }, - { "bottom-line", 0, 0, + { "bottom-line", 0, 0, 1, window_copy_cmd_bottom_line }, - { "cancel", 0, 0, + { "cancel", 0, 0, 0, window_copy_cmd_cancel }, - { "clear-selection", 0, 0, + { "clear-selection", 0, 0, 0, window_copy_cmd_clear_selection }, - { "copy-end-of-line", 0, 1, + { "copy-end-of-line", 0, 1, 0, window_copy_cmd_copy_end_of_line }, - { "copy-line", 0, 1, + { "copy-line", 0, 1, 0, window_copy_cmd_copy_line }, - { "copy-pipe-no-clear", 1, 2, + { "copy-pipe-no-clear", 1, 2, 0, window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 1, 2, + { "copy-pipe", 1, 2, 0, window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 1, 2, + { "copy-pipe-and-cancel", 1, 2, 0, window_copy_cmd_copy_pipe_and_cancel }, - { "copy-selection-no-clear", 0, 1, + { "copy-selection-no-clear", 0, 1, 0, window_copy_cmd_copy_selection_no_clear }, - { "copy-selection", 0, 1, + { "copy-selection", 0, 1, 0, window_copy_cmd_copy_selection }, - { "copy-selection-and-cancel", 0, 1, + { "copy-selection-and-cancel", 0, 1, 0, window_copy_cmd_copy_selection_and_cancel }, - { "cursor-down", 0, 0, + { "cursor-down", 0, 0, 1, window_copy_cmd_cursor_down }, - { "cursor-left", 0, 0, + { "cursor-down-and-cancel", 0, 0, 0, + window_copy_cmd_cursor_down_and_cancel }, + { "cursor-left", 0, 0, 1, window_copy_cmd_cursor_left }, - { "cursor-right", 0, 0, + { "cursor-right", 0, 0, 1, window_copy_cmd_cursor_right }, - { "cursor-up", 0, 0, + { "cursor-up", 0, 0, 1, window_copy_cmd_cursor_up }, - { "end-of-line", 0, 0, + { "end-of-line", 0, 0, 1, window_copy_cmd_end_of_line }, - { "goto-line", 1, 1, + { "goto-line", 1, 1, 1, window_copy_cmd_goto_line }, - { "halfpage-down", 0, 0, + { "halfpage-down", 0, 0, 1, window_copy_cmd_halfpage_down }, - { "halfpage-down-and-cancel", 0, 0, + { "halfpage-down-and-cancel", 0, 0, 0, window_copy_cmd_halfpage_down_and_cancel }, - { "halfpage-up", 0, 0, + { "halfpage-up", 0, 0, 1, window_copy_cmd_halfpage_up }, - { "history-bottom", 0, 0, + { "history-bottom", 0, 0, 1, window_copy_cmd_history_bottom }, - { "history-top", 0, 0, + { "history-top", 0, 0, 1, window_copy_cmd_history_top }, - { "jump-again", 0, 0, + { "jump-again", 0, 0, 1, window_copy_cmd_jump_again }, - { "jump-backward", 1, 1, + { "jump-backward", 1, 1, 1, window_copy_cmd_jump_backward }, - { "jump-forward", 1, 1, + { "jump-forward", 1, 1, 1, window_copy_cmd_jump_forward }, - { "jump-reverse", 0, 0, + { "jump-reverse", 0, 0, 1, window_copy_cmd_jump_reverse }, - { "jump-to-backward", 1, 1, + { "jump-to-backward", 1, 1, 1, window_copy_cmd_jump_to_backward }, - { "jump-to-forward", 1, 1, + { "jump-to-forward", 1, 1, 1, window_copy_cmd_jump_to_forward }, - { "middle-line", 0, 0, + { "middle-line", 0, 0, 1, window_copy_cmd_middle_line }, - { "next-matching-bracket", 0, 0, + { "next-matching-bracket", 0, 0, 0, window_copy_cmd_next_matching_bracket }, - { "next-paragraph", 0, 0, + { "next-paragraph", 0, 0, 1, window_copy_cmd_next_paragraph }, - { "next-space", 0, 0, + { "next-space", 0, 0, 1, window_copy_cmd_next_space }, - { "next-space-end", 0, 0, + { "next-space-end", 0, 0, 1, window_copy_cmd_next_space_end }, - { "next-word", 0, 0, + { "next-word", 0, 0, 1, window_copy_cmd_next_word }, - { "next-word-end", 0, 0, + { "next-word-end", 0, 0, 1, window_copy_cmd_next_word_end }, - { "other-end", 0, 0, + { "other-end", 0, 0, 1, window_copy_cmd_other_end }, - { "page-down", 0, 0, + { "page-down", 0, 0, 1, window_copy_cmd_page_down }, - { "page-down-and-cancel", 0, 0, + { "page-down-and-cancel", 0, 0, 0, window_copy_cmd_page_down_and_cancel }, - { "page-up", 0, 0, + { "page-up", 0, 0, 1, window_copy_cmd_page_up }, - { "previous-matching-bracket", 0, 0, + { "previous-matching-bracket", 0, 0, 0, window_copy_cmd_previous_matching_bracket }, - { "previous-paragraph", 0, 0, + { "previous-paragraph", 0, 0, 1, window_copy_cmd_previous_paragraph }, - { "previous-space", 0, 0, + { "previous-space", 0, 0, 1, window_copy_cmd_previous_space }, - { "previous-word", 0, 0, + { "previous-word", 0, 0, 1, window_copy_cmd_previous_word }, - { "rectangle-toggle", 0, 0, + { "rectangle-toggle", 0, 0, 0, window_copy_cmd_rectangle_toggle }, - { "scroll-down", 0, 0, + { "scroll-down", 0, 0, 1, window_copy_cmd_scroll_down }, - { "scroll-down-and-cancel", 0, 0, + { "scroll-down-and-cancel", 0, 0, 0, window_copy_cmd_scroll_down_and_cancel }, - { "scroll-up", 0, 0, + { "scroll-up", 0, 0, 1, window_copy_cmd_scroll_up }, - { "search-again", 0, 0, + { "search-again", 0, 0, 0, window_copy_cmd_search_again }, - { "search-backward", 1, 1, + { "search-backward", 0, 1, 0, window_copy_cmd_search_backward }, - { "search-backward-incremental", 1, 1, + { "search-backward-text", 0, 1, 0, + window_copy_cmd_search_backward_text }, + { "search-backward-incremental", 1, 1, 0, window_copy_cmd_search_backward_incremental }, - { "search-forward", 1, 1, + { "search-forward", 0, 1, 0, window_copy_cmd_search_forward }, - { "search-forward-incremental", 1, 1, + { "search-forward-text", 0, 1, 0, + window_copy_cmd_search_forward_text }, + { "search-forward-incremental", 1, 1, 0, window_copy_cmd_search_forward_incremental }, - { "search-reverse", 0, 0, + { "search-reverse", 0, 0, 0, window_copy_cmd_search_reverse }, - { "select-line", 0, 0, + { "select-line", 0, 0, 0, window_copy_cmd_select_line }, - { "select-word", 0, 0, + { "select-word", 0, 0, 0, window_copy_cmd_select_word }, - { "start-of-line", 0, 0, + { "start-of-line", 0, 0, 1, window_copy_cmd_start_of_line }, - { "stop-selection", 0, 0, + { "stop-selection", 0, 0, 0, window_copy_cmd_stop_selection }, - { "top-line", 0, 0, + { "top-line", 0, 0, 1, window_copy_cmd_top_line }, }; @@ -1904,6 +2135,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, enum window_copy_cmd_action action; const char *command; u_int i; + int ismotion = 0, keys; if (args->argc == 0) return; @@ -1926,16 +2158,23 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (args->argc - 1 < window_copy_cmd_table[i].minargs || args->argc - 1 > window_copy_cmd_table[i].maxargs) break; + ismotion = window_copy_cmd_table[i].ismotion; action = window_copy_cmd_table[i].f (&cs); break; } } if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { - window_copy_clear_marks(wme); + keys = options_get_number(wme->wp->window->options, "mode-keys"); + if (keys != MODEKEY_VI || !ismotion) { + window_copy_clear_marks(wme); + data->searchx = data->searchy = -1; + } else if (data->searchthis != -1) { + data->searchthis = -1; + action = WINDOW_COPY_CMD_REDRAW; + } if (action == WINDOW_COPY_CMD_NOTHING) action = WINDOW_COPY_CMD_REDRAW; - data->searchx = data->searchy = -1; } wme->prefix = 1; @@ -1971,7 +2210,9 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) data->oy = gd->hsize - offset; } - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2000,16 +2241,28 @@ static int window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { - u_int ax, bx, px; - int matched; + u_int ax, bx, px, pywrap, endline; + int matched; + struct grid_line *gl; + endline = gd->hsize + gd->sy - 1; for (ax = first; ax < last; ax++) { - if (ax + sgd->sx > gd->sx) - break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; - matched = window_copy_search_compare(gd, px, py, sgd, - bx, cis); + pywrap = py; + /* Wrap line. */ + while (px >= gd->sx && pywrap < endline) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + px -= gd->sx; + pywrap++; + } + /* We have run off the end of the grid. */ + if (px >= gd->sx) + break; + matched = window_copy_search_compare(gd, px, pywrap, + sgd, bx, cis); if (!matched) break; } @@ -2025,16 +2278,28 @@ static int window_copy_search_rl(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { - u_int ax, bx, px; - int matched; + u_int ax, bx, px, pywrap, endline; + int matched; + struct grid_line *gl; - for (ax = last + 1; ax > first; ax--) { - if (gd->sx - (ax - 1) < sgd->sx) - continue; + endline = gd->hsize + gd->sy - 1; + for (ax = last; ax > first; ax--) { for (bx = 0; bx < sgd->sx; bx++) { px = ax - 1 + bx; - matched = window_copy_search_compare(gd, px, py, sgd, - bx, cis); + pywrap = py; + /* Wrap line. */ + while (px >= gd->sx && pywrap < endline) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + px -= gd->sx; + pywrap++; + } + /* We have run off the end of the grid. */ + if (px >= gd->sx) + break; + matched = window_copy_search_compare(gd, px, pywrap, + sgd, bx, cis); if (!matched) break; } @@ -2046,12 +2311,312 @@ window_copy_search_rl(struct grid *gd, return (0); } +static int +window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) +{ + int eflags = 0; + u_int endline, foundx, foundy, len, pywrap, size = 1; + char *buf; + regmatch_t regmatch; + struct grid_line *gl; + + /* + * This can happen during search if the last match was the last + * character on a line. + */ + if (first >= last) + return (0); + + /* Set flags for regex search. */ + if (first != 0) + eflags |= REG_NOTBOL; + + /* Need to look at the entire string. */ + buf = xmalloc(size); + buf[0] = '\0'; + buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); + len = gd->sx - first; + endline = gd->hsize + gd->sy - 1; + pywrap = py; + while (buf != NULL && pywrap <= endline) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + pywrap++; + buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); + len += gd->sx; + } + + if (regexec(reg, buf, 1, ®match, eflags) == 0) { + foundx = first; + foundy = py; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + regmatch.rm_so); + if (foundy == py && foundx < last) { + *ppx = foundx; + len -= foundx - first; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + regmatch.rm_eo); + *psx = foundx; + while (foundy > py) { + *psx += gd->sx; + foundy--; + } + *psx -= *ppx; + free(buf); + return (1); + } + } + + free(buf); + *ppx = 0; + *psx = 0; + return (0); +} + +static int +window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) +{ + int eflags = 0; + u_int endline, len, pywrap, size = 1; + char *buf; + struct grid_line *gl; + + /* Set flags for regex search. */ + if (first != 0) + eflags |= REG_NOTBOL; + + /* Need to look at the entire string. */ + buf = xmalloc(size); + buf[0] = '\0'; + buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); + len = gd->sx - first; + endline = gd->hsize + gd->sy - 1; + pywrap = py; + while (buf != NULL && (pywrap <= endline)) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + pywrap++; + buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); + len += gd->sx; + } + + if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, + reg, eflags)) + { + free(buf); + return (1); + } + + free(buf); + *ppx = 0; + *psx = 0; + return (0); +} + +static const char * +window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size) +{ + struct grid_cell_entry *gce; + + if (px >= gl->cellsize) { + *size = 1; + return (" "); + } + + gce = &gl->celldata[px]; + if (~gce->flags & GRID_FLAG_EXTENDED) { + *size = 1; + return ((const char *)&gce->data.data); + } + + *size = gl->extddata[gce->offset].data.size; + return ((const char *)gl->extddata[gce->offset].data.data); +} + +/* Find last match in given range. */ +static int +window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, + u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, + int eflags) +{ + u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; + regmatch_t regmatch; + + foundx = first; + foundy = py; + oldx = first; + while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + px + regmatch.rm_so); + if (foundy > py || foundx >= last) + break; + len -= foundx - oldx; + savepx = foundx; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + px + regmatch.rm_eo); + if (foundy > py || foundx >= last) { + *ppx = savepx; + *psx = foundx; + while (foundy > py) { + *psx += gd->sx; + foundy--; + } + *psx -= *ppx; + return (1); + } else { + savesx = foundx - savepx; + len -= savesx; + oldx = foundx; + } + px += regmatch.rm_eo; + } + + if (savesx > 0) { + *ppx = savepx; + *psx = savesx; + return (1); + } else { + *ppx = 0; + *psx = 0; + return (0); + } +} + +/* Stringify line and append to input buffer. Caller frees. */ +static char * +window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, + char *buf, u_int *size) +{ + u_int ax, bx, newsize = *size; + const struct grid_line *gl; + const char *d; + size_t bufsize = 1024, dlen; + + while (bufsize < newsize) + bufsize *= 2; + buf = xrealloc(buf, bufsize); + + gl = grid_peek_line(gd, py); + bx = *size - 1; + for (ax = first; ax < last; ax++) { + d = window_copy_cellstring(gl, ax, &dlen); + newsize += dlen; + while (bufsize < newsize) { + bufsize *= 2; + buf = xrealloc(buf, bufsize); + } + if (dlen == 1) + buf[bx++] = *d; + else { + memcpy(buf + bx, d, dlen); + bx += dlen; + } + } + buf[newsize - 1] = '\0'; + + *size = newsize; + return (buf); +} + +/* Map start of C string containing UTF-8 data to grid cell position. */ static void -window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) +window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, + const char *str) +{ + u_int cell, ccell, px, pywrap, pos, len; + int match; + const struct grid_line *gl; + const char *d; + size_t dlen; + struct { + const char *d; + size_t dlen; + } *cells; + + /* Populate the array of cell data. */ + cells = xreallocarray(NULL, ncells, sizeof cells[0]); + cell = 0; + px = *ppx; + pywrap = *ppy; + gl = grid_peek_line(gd, pywrap); + while (cell < ncells) { + cells[cell].d = window_copy_cellstring(gl, px, + &cells[cell].dlen); + cell++; + px++; + if (px == gd->sx) { + px = 0; + pywrap++; + gl = grid_peek_line(gd, pywrap); + } + } + + /* Locate starting cell. */ + cell = 0; + len = strlen(str); + while (cell < ncells) { + ccell = cell; + pos = 0; + match = 1; + while (ccell < ncells) { + if (str[pos] == '\0') { + match = 0; + break; + } + d = cells[ccell].d; + dlen = cells[ccell].dlen; + if (dlen == 1) { + if (str[pos] != *d) { + match = 0; + break; + } + pos++; + } else { + if (dlen > len - pos) + dlen = len - pos; + if (memcmp(str + pos, d, dlen) != 0) { + match = 0; + break; + } + pos += dlen; + } + ccell++; + } + if (match) + break; + cell++; + } + + /* If not found this will be one past the end. */ + px = *ppx + cell; + pywrap = *ppy; + while (px >= gd->sx) { + px -= gd->sx; + pywrap++; + } + + *ppx = px; + *ppy = pywrap; + + /* Free cell data. */ + free(cells); +} + +static void +window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) { if (*fx == 0) { /* left */ - if (*fy == 0) /* top */ + if (*fy == 0) { /* top */ + if (wrapflag) { + *fx = screen_size_x(s) - 1; + *fy = screen_hsize(s) + screen_size_y(s) - 1; + } return; + } *fx = screen_size_x(s) - 1; *fy = *fy - 1; } else @@ -2059,11 +2624,16 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) } static void -window_copy_move_right(struct screen *s, u_int *fx, u_int *fy) +window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) { if (*fx == screen_size_x(s) - 1) { /* right */ - if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */ + if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ + if (wrapflag) { + *fx = 0; + *fy = 0; + } return; + } *fx = 0; *fy = *fy + 1; } else @@ -2090,31 +2660,58 @@ window_copy_is_lowercase(const char *ptr) static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, - int direction) + int direction, int regex) { - u_int i, px; - int found; + u_int i, px, sx, ssize = 1; + int found = 0, cflags = REG_EXTENDED; + char *sbuf = NULL; + regex_t reg; + + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } - found = 0; if (direction) { for (i = fy; i <= endline; i++) { - found = window_copy_search_lr(gd, sgd, &px, i, fx, - gd->sx, cis); + if (regex) { + found = window_copy_search_lr_regex(gd, + &px, &sx, i, fx, gd->sx, ®); + } else { + found = window_copy_search_lr(gd, sgd, + &px, i, fx, gd->sx, cis); + } if (found) break; fx = 0; } } else { for (i = fy + 1; endline < i; i--) { - found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, - fx, cis); + if (regex) { + found = window_copy_search_rl_regex(gd, + &px, &sx, i - 1, 0, fx + 1, ®); + } else { + found = window_copy_search_rl(gd, sgd, + &px, i - 1, 0, fx + 1, cis); + } if (found) { i--; break; } - fx = gd->sx; + fx = gd->sx - 1; } } + if (regex) { + free(sbuf); + regfree(®); + } if (found) { window_copy_scroll_to(wme, px, i); @@ -2124,7 +2721,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, - direction)); + direction, regex)); } return (0); } @@ -2134,43 +2731,50 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction) +window_copy_search(struct window_mode_entry *wme, int direction, int regex) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const char *str = data->searchstr; u_int fx, fy, endline; int wrapflag, cis, found; + if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') + regex = 0; + + if (data->timeout) + return (0); + free(wp->searchstr); - wp->searchstr = xstrdup(data->searchstr); + wp->searchstr = xstrdup(str); + wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; - screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); + screen_init(&ss, screen_write_strlen("%s", str), 1, 0); screen_write_start(&ctx, NULL, &ss); - screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); + screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); - if (direction) - window_copy_move_right(s, &fx, &fy); - else - window_copy_move_left(s, &fx, &fy); - wrapflag = options_get_number(wp->window->options, "wrap-search"); - cis = window_copy_is_lowercase(data->searchstr); + cis = window_copy_is_lowercase(str); - if (direction) + if (direction) { + window_copy_move_right(s, &fx, &fy, wrapflag); endline = gd->hsize + gd->sy - 1; - else + } else { + window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; - found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, - wrapflag, direction); + } - if (window_copy_search_marks(wme, &ss)) + found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, + wrapflag, direction, regex); + + if (window_copy_search_marks(wme, &ss, regex)) window_copy_redraw_screen(wme); screen_free(&ss); @@ -2178,14 +2782,21 @@ window_copy_search(struct window_mode_entry *wme, int direction) } static int -window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) +window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, + int regex) { struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const struct grid_line *gl; int found, cis, which = -1; + int cflags = REG_EXTENDED; u_int px, py, b, nfound = 0, width; + u_int ssize = 1; + char *sbuf = NULL; + regex_t reg; + time_t tstart, t; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2203,16 +2814,42 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) free(data->searchmark); data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); - for (py = 0; py < gd->hsize + gd->sy; py++) { + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, + sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } + time(&tstart); + for (py = gd->hsize - data->oy; py > 0; py--) { + gl = grid_peek_line(gd, py - 1); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + for (; py < gd->hsize - data->oy + gd->sy; py++) { px = 0; for (;;) { - found = window_copy_search_lr(gd, ssp->grid, &px, py, - px, gd->sx, cis); - if (!found) - break; + if (regex) { + found = window_copy_search_lr_regex(gd, + &px, &width, py, px, gd->sx, ®); + if (!found) + break; + } else { + found = window_copy_search_lr(gd, ssp->grid, + &px, py, px, gd->sx, cis); + if (!found) + break; + } nfound++; - if (px == data->cx && py == gd->hsize + data->cy - data->oy) + if (px == data->cx && + py == gd->hsize + data->cy - data->oy) which = nfound; b = (py * gd->sx) + px; @@ -2220,6 +2857,20 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) px++; } + + time(&t); + if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { + data->timeout = 1; + break; + } + } + if (regex) { + free(sbuf); + regfree(®); + } + if (data->timeout) { + window_copy_clear_marks(wme); + return (1); } if (which != -1) @@ -2230,7 +2881,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) if (ssp == &ss) screen_free(&ss); - return (nfound); + return (1); } static void @@ -2243,15 +2894,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme) +window_copy_search_up(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 0)); + return (window_copy_search(wme, 0, regex)); } static int -window_copy_search_down(struct window_mode_entry *wme) +window_copy_search_down(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 1)); + return (window_copy_search(wme, 1, regex)); } static void @@ -2268,7 +2919,7 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) lineno = screen_hsize(data->backing); data->oy = lineno; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2287,10 +2938,17 @@ window_copy_write_line(struct window_mode_entry *wme, style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; - if (py == 0 && s->rupper < s->rlower) { + if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { - size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, screen_hsize(data->backing)); + if (data->timeout) { + size = xsnprintf(hdr, sizeof hdr, + "(timed out) [%u/%u]", data->oy, + screen_hsize(data->backing)); + } else { + size = xsnprintf(hdr, sizeof hdr, + "[%u/%u]", data->oy, + screen_hsize(data->backing)); + } } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, @@ -2338,6 +2996,7 @@ static void window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; u_int new_y, start, end; new_y = data->cy; @@ -2348,6 +3007,16 @@ window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) start = new_y; end = old_y; } + + /* + * In word selection mode the first word on the line below the cursor + * might be selected, so add this line to the redraw area. + */ + if (data->selflag == SEL_WORD) { + /* Last grid line in data coordinates. */ + if (end < gd->sy + data->oy - 1) + end++; + } window_copy_redraw_lines(wme, start, end - start + 1); } @@ -2375,22 +3044,87 @@ window_copy_redraw_screen(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor(struct window_mode_entry *wme) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, + int no_reset) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; - xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; + switch (data->selflag) { + case SEL_WORD: + xx = data->cx; + if (no_reset) + break; + begin = 0; + if (data->dy > yy || (data->dy == yy && data->dx > xx)) { + /* Right to left selection. */ + window_copy_cursor_previous_word_pos(wme, data->ws, 0, + &xx, &yy); + begin = 1; + + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } else { + /* Left to right selection. */ + if (xx >= window_copy_find_length(wme, yy) || + !window_copy_in_set(wme, xx + 1, yy, data->ws)) + window_copy_cursor_next_word_end_pos(wme, + data->ws, &xx, &yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_LINE: + if (no_reset) { + xx = data->cx; + break; + } + begin = 0; + if (data->dy > yy) { + /* Right to left selection. */ + xx = 0; + begin = 1; + + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } else { + /* Left to right selection. */ + xx = window_copy_find_length(wme, yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_CHAR: + xx = data->cx; + break; + } + if (begin) { + data->selx = xx; + data->sely = yy; + } else { + data->endselx = xx; + data->endsely = yy; + } +} + +static void +window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) +{ + struct window_copy_mode_data *data = wme->data; switch (data->cursordrag) { case CURSORDRAG_ENDSEL: - data->endselx = xx; - data->endsely = yy; + window_copy_synchronize_cursor_end(wme, 0, no_reset); break; case CURSORDRAG_SEL: - data->selx = xx; - data->sely = yy; + window_copy_synchronize_cursor_end(wme, 1, no_reset); break; case CURSORDRAG_NONE: break; @@ -2432,7 +3166,7 @@ window_copy_start_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_ENDSEL; - window_copy_set_selection(wme, 1); + window_copy_set_selection(wme, 1, 0); } static int @@ -2469,18 +3203,20 @@ window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, } static int -window_copy_update_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) return (0); - return (window_copy_set_selection(wme, may_redraw)); + return (window_copy_set_selection(wme, may_redraw, no_reset)); } static int -window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -2490,7 +3226,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) u_int sx, sy, cy, endsx, endsy; int startrelpos, endrelpos; - window_copy_synchronize_cursor(wme); + window_copy_synchronize_cursor(wme, no_reset); /* Adjust the selection. */ sx = data->selx; @@ -2671,7 +3407,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, static void window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, - const char *prefix, const char *command) + const char *prefix, const char *cmd) { void *buf; size_t len; @@ -2681,7 +3417,7 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); bufferevent_write(job_get_event(job), buf, len); window_copy_copy_buffer(wme, prefix, buf, len); } @@ -2800,6 +3536,7 @@ window_copy_clear_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); @@ -2845,7 +3582,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) } } window_copy_update_cursor(wme, 0, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -2868,7 +3605,7 @@ window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -2901,7 +3638,7 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -2952,7 +3689,7 @@ window_copy_other_end(struct window_mode_entry *wme) } else data->cy = cy + sely - yy; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 1); window_copy_redraw_screen(wme); } @@ -2976,7 +3713,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) window_copy_cursor_end_of_line(wme); } else if (cx > 0) { window_copy_update_cursor(wme, cx - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3008,7 +3745,7 @@ window_copy_cursor_right(struct window_mode_entry *wme) cx++; } window_copy_update_cursor(wme, cx, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3041,7 +3778,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } else { window_copy_update_cursor(wme, data->lastcx, data->cy - 1); - if (window_copy_update_selection(wme, 1)) { + if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); else @@ -3087,7 +3824,7 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { window_copy_update_cursor(wme, data->lastcx, data->cy + 1); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } @@ -3122,7 +3859,7 @@ window_copy_cursor_jump(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3149,7 +3886,7 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3176,7 +3913,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3206,7 +3943,7 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px + 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3255,13 +3992,62 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, } while (expected == 1); window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } +static void +window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, + const char *separators, u_int *ppx, u_int *ppy) +{ + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + struct options *oo = wp->window->options; + struct screen *back_s = data->backing; + u_int px, py, xx, yy; + int keys, expected = 1; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wme, py); + yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + + keys = options_get_number(oo, "mode-keys"); + if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) + px++; + + /* + * First skip past any word characters, then any non-word characters. + * + * expected is initially set to 1 for the former and then 0 for the + * latter. + */ + do { + while (px > xx || + window_copy_in_set(wme, px, py, separators) == expected) { + /* Move down if we're past the end of the line. */ + if (px > xx) { + if (py == yy) + return; + py++; + px = 0; + xx = window_copy_find_length(wme, py); + } else + px++; + } + expected = !expected; + } while (expected == 0); + + if (keys == MODEKEY_VI && px != 0) + px--; + + *ppx = px; + *ppy = py; +} + static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, - const char *separators) + const char *separators, int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3307,14 +4093,14 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, px--; window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, no_reset)) window_copy_redraw_lines(wme, data->cy, 1); } -/* Move to the previous place where a word begins. */ +/* Compute the previous place where a word begins. */ static void -window_copy_cursor_previous_word(struct window_mode_entry *wme, - const char *separators) +window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, + const char *separators, int already, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; u_int px, py; @@ -3323,25 +4109,78 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, py = screen_hsize(data->backing) + data->cy - data->oy; /* Move back to the previous word character. */ - for (;;) { - if (px > 0) { - px--; - if (!window_copy_in_set(wme, px, py, separators)) - break; - } else { - if (data->cy == 0 && - (screen_hsize(data->backing) == 0 || - data->oy >= screen_hsize(data->backing) - 1)) - goto out; - window_copy_cursor_up(wme, 0); + if (already || window_copy_in_set(wme, px, py, separators)) { + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wme, px, py, + separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= + screen_hsize(data->backing) - 1)) + goto out; + py--; - py = screen_hsize(data->backing) + data->cy - data->oy; - px = window_copy_find_length(wme, py); + py = screen_hsize(data->backing) + data->cy - + data->oy; + px = window_copy_find_length(wme, py); - /* Stop if separator at EOL. */ - if (px > 0 && - window_copy_in_set(wme, px - 1, py, separators)) - break; + /* Stop if separator at EOL. */ + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) + break; + } + } + } + + /* Move back to the beginning of this word. */ + while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) + px--; + +out: + *ppx = px; + *ppy = py; +} + +/* Move to the previous place where a word begins. */ +static void +window_copy_cursor_previous_word(struct window_mode_entry *wme, + const char *separators, int already) +{ + struct window_copy_mode_data *data = wme->data; + u_int px, py; + + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + + /* Move back to the previous word character. */ + if (already || window_copy_in_set(wme, px, py, separators)) { + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wme, px, py, + separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= + screen_hsize(data->backing) - 1)) + goto out; + window_copy_cursor_up(wme, 0); + + py = screen_hsize(data->backing) + data->cy - + data->oy; + px = window_copy_find_length(wme, py); + + /* Stop if separator at EOL. */ + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) + break; + } } } @@ -3351,7 +4190,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, out: window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3369,7 +4208,9 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) return; data->oy -= ny; - window_copy_update_selection(wme, 0); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -3403,7 +4244,9 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) return; data->oy += ny; - window_copy_update_selection(wme, 0); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -3430,7 +4273,7 @@ window_copy_rectangle_toggle(struct window_mode_entry *wme) if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -3461,7 +4304,8 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; - u_int x, y; + struct window_copy_mode_data *data; + u_int x, y, yg; if (c == NULL) return; @@ -3481,10 +4325,30 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = window_copy_drag_release; - window_copy_update_cursor(wme, x, y); - window_copy_start_selection(wme); - window_copy_redraw_screen(wme); + data = wme->data; + yg = screen_hsize(data->backing) + y - data->oy; + if (x < data->selrx || x > data->endselrx || yg != data->selry) + data->selflag = SEL_CHAR; + switch (data->selflag) { + case SEL_WORD: + if (data->ws) { + window_copy_update_cursor(wme, x, y); + window_copy_cursor_previous_word_pos(wme, + data->ws, 0, &x, &y); + y -= screen_hsize(data->backing) - data->oy; + } + window_copy_update_cursor(wme, x, y); + break; + case SEL_LINE: + window_copy_update_cursor(wme, 0, y); + break; + case SEL_CHAR: + window_copy_update_cursor(wme, x, y); + window_copy_start_selection(wme); + break; + } + window_copy_redraw_screen(wme); window_copy_drag_update(c, m); } @@ -3520,7 +4384,7 @@ window_copy_drag_update(struct client *c, struct mouse_event *m) old_cy = data->cy; window_copy_update_cursor(wme, x, y); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_selection(wme, old_cy); if (old_cy != data->cy || old_cx == data->cx) { if (y == 0) { diff --git a/external/bsd/tmux/dist/window-tree.c b/external/bsd/tmux/dist/window-tree.c index 9bcaf5333c9e..2f3aaeb0a7d1 100644 --- a/external/bsd/tmux/dist/window-tree.c +++ b/external/bsd/tmux/dist/window-tree.c @@ -33,7 +33,7 @@ static void window_tree_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); -#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'" +#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ @@ -89,6 +89,7 @@ static const char *window_tree_sort_list[] = { "name", "time" }; +static struct mode_tree_sort_criteria *window_tree_sort; enum window_tree_type { WINDOW_TREE_NONE, @@ -184,62 +185,92 @@ window_tree_free_item(struct window_tree_itemdata *item) } static int -window_tree_cmp_session_name(const void *a0, const void *b0) +window_tree_cmp_session(const void *a0, const void *b0) { - const struct session *const *a = a0; - const struct session *const *b = b0; + const struct session *const *a = a0; + const struct session *const *b = b0; + const struct session *sa = *a; + const struct session *sb = *b; + int result = 0; - return (strcmp((*a)->name, (*b)->name)); + switch (window_tree_sort->field) { + case WINDOW_TREE_BY_INDEX: + result = sa->id - sb->id; + break; + case WINDOW_TREE_BY_TIME: + if (timercmp(&sa->activity_time, &sb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&sa->activity_time, &sb->activity_time, <)) { + result = 1; + break; + } + /* FALLTHROUGH */ + case WINDOW_TREE_BY_NAME: + result = strcmp(sa->name, sb->name); + break; + } + + if (window_tree_sort->reversed) + result = -result; + return (result); } static int -window_tree_cmp_session_time(const void *a0, const void *b0) +window_tree_cmp_window(const void *a0, const void *b0) { - const struct session *const *a = a0; - const struct session *const *b = b0; + const struct winlink *const *a = a0; + const struct winlink *const *b = b0; + const struct winlink *wla = *a; + const struct winlink *wlb = *b; + struct window *wa = wla->window; + struct window *wb = wlb->window; + int result = 0; - if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >)) - return (-1); - if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <)) - return (1); - return (strcmp((*a)->name, (*b)->name)); + switch (window_tree_sort->field) { + case WINDOW_TREE_BY_INDEX: + result = wla->idx - wlb->idx; + break; + case WINDOW_TREE_BY_TIME: + if (timercmp(&wa->activity_time, &wb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&wa->activity_time, &wb->activity_time, <)) { + result = 1; + break; + } + /* FALLTHROUGH */ + case WINDOW_TREE_BY_NAME: + result = strcmp(wa->name, wb->name); + break; + } + + if (window_tree_sort->reversed) + result = -result; + return (result); } static int -window_tree_cmp_window_name(const void *a0, const void *b0) +window_tree_cmp_pane(const void *a0, const void *b0) { - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; + const struct window_pane *const *a = a0; + const struct window_pane *const *b = b0; + int result; - return (strcmp((*a)->window->name, (*b)->window->name)); -} - -static int -window_tree_cmp_window_time(const void *a0, const void *b0) -{ - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; - - if (timercmp(&(*a)->window->activity_time, - &(*b)->window->activity_time, >)) - return (-1); - if (timercmp(&(*a)->window->activity_time, - &(*b)->window->activity_time, <)) - return (1); - return (strcmp((*a)->window->name, (*b)->window->name)); -} - -static int -window_tree_cmp_pane_time(const void *a0, const void *b0) -{ - const struct window_pane *const *a = a0; - const struct window_pane *const *b = b0; - - if ((*a)->active_point < (*b)->active_point) - return (-1); - if ((*a)->active_point > (*b)->active_point) - return (1); - return (0); + if (window_tree_sort->field == WINDOW_TREE_BY_TIME) + result = (*a)->active_point - (*b)->active_point; + else { + /* + * Panes don't have names, so use number order for any other + * sort field. + */ + result = (*a)->id - (*b)->id; + } + if (window_tree_sort->reversed) + result = -result; + return (result); } static void @@ -285,8 +316,9 @@ window_tree_filter_pane(struct session *s, struct winlink *wl, } static int -window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, - u_int sort_type, struct mode_tree_item *parent, const char *filter) +window_tree_build_window(struct session *s, struct winlink *wl, + void *modedata, struct mode_tree_sort_criteria *sort_crit, + struct mode_tree_item *parent, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; @@ -335,16 +367,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, if (n == 0) goto empty; - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - /* Panes don't have names, so leave in number order. */ - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_pane_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_pane); for (i = 0; i < n; i++) window_tree_build_pane(s, wl, l[i], modedata, mti); @@ -359,8 +383,8 @@ empty: } static void -window_tree_build_session(struct session *s, void* modedata, - u_int sort_type, const char *filter) +window_tree_build_session(struct session *s, void *modedata, + struct mode_tree_sort_criteria *sort_crit, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; @@ -392,20 +416,12 @@ window_tree_build_session(struct session *s, void* modedata, l = xreallocarray(l, n + 1, sizeof *l); l[n++] = wl; } - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - qsort(l, n, sizeof *l, window_tree_cmp_window_name); - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_window_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_window); empty = 0; for (i = 0; i < n; i++) { - if (!window_tree_build_window(s, l[i], modedata, sort_type, mti, + if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, filter)) empty++; } @@ -418,8 +434,8 @@ window_tree_build_session(struct session *s, void* modedata, } static void -window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, - const char *filter) +window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + uint64_t *tag, const char *filter) { struct window_tree_modedata *data = modedata; struct session *s, **l; @@ -446,19 +462,11 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, l = xreallocarray(l, n + 1, sizeof *l); l[n++] = s; } - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - qsort(l, n, sizeof *l, window_tree_cmp_session_name); - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_session_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_session); for (i = 0; i < n; i++) - window_tree_build_session(l[i], modedata, sort_type, filter); + window_tree_build_session(l[i], modedata, sort_crit, filter); free(l); switch (data->type) { @@ -966,7 +974,7 @@ window_tree_get_target(struct window_tree_itemdata *item, } static void -window_tree_command_each(void* modedata, void* itemdata, struct client *c, +window_tree_command_each(void *modedata, void *itemdata, struct client *c, __unused key_code key) { struct window_tree_modedata *data = modedata; @@ -1023,7 +1031,7 @@ window_tree_command_free(void *modedata) } static void -window_tree_kill_each(__unused void* modedata, void* itemdata, +window_tree_kill_each(__unused void *modedata, void *itemdata, __unused struct client *c, __unused key_code key) { struct window_tree_itemdata *item = itemdata; diff --git a/external/bsd/tmux/dist/window.c b/external/bsd/tmux/dist/window.c index 1efd09bfd7e8..4304b755e1fa 100644 --- a/external/bsd/tmux/dist/window.c +++ b/external/bsd/tmux/dist/window.c @@ -306,12 +306,17 @@ window_update_activity(struct window *w) } struct window * -window_create(u_int sx, u_int sy) +window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) { struct window *w; + if (xpixel == 0) + xpixel = DEFAULT_XPIXEL; + if (ypixel == 0) + ypixel = DEFAULT_YPIXEL; + w = xcalloc(1, sizeof *w); - w->name = NULL; + w->name = xstrdup(""); w->flags = 0; TAILQ_INIT(&w->panes); @@ -322,6 +327,8 @@ window_create(u_int sx, u_int sy) w->sx = sx; w->sy = sy; + w->xpixel = xpixel; + w->ypixel = ypixel; w->options = options_create(global_w_options); @@ -408,11 +415,49 @@ window_set_name(struct window *w, const char *new_name) } void -window_resize(struct window *w, u_int sx, u_int sy) +window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) { - log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); + if (xpixel == 0) + xpixel = DEFAULT_XPIXEL; + if (ypixel == 0) + ypixel = DEFAULT_YPIXEL; + + log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy, + xpixel == -1 ? w->xpixel : (u_int)xpixel, + ypixel == -1 ? w->ypixel : (u_int)ypixel); w->sx = sx; w->sy = sy; + if (xpixel != -1) + w->xpixel = xpixel; + if (ypixel != -1) + w->ypixel = ypixel; +} + +void +window_pane_send_resize(struct window_pane *wp, int yadjust) +{ + struct window *w = wp->window; + struct winsize ws; + + if (wp->fd == -1) + return; + + memset(&ws, 0, sizeof ws); + ws.ws_col = wp->sx; + ws.ws_row = wp->sy + yadjust; + ws.ws_xpixel = w->xpixel * ws.ws_col; + ws.ws_ypixel = w->ypixel * ws.ws_row; + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) +#ifdef __sun + /* + * Some versions of Solaris apparently can return an error when + * resizing; don't know why this happens, can't reproduce on + * other platforms and ignoring it doesn't seem to cause any + * issues. + */ + if (errno != EINVAL && errno != ENXIO) +#endif + fatal("ioctl failed"); } int @@ -503,31 +548,38 @@ window_get_active_at(struct window *w, u_int x, u_int y) struct window_pane * window_find_string(struct window *w, const char *s) { - u_int x, y; + u_int x, y, top = 0, bottom = w->sy - 1; + int status; x = w->sx / 2; y = w->sy / 2; + status = options_get_number(w->options, "pane-border-status"); + if (status == PANE_STATUS_TOP) + top++; + else if (status == PANE_STATUS_BOTTOM) + bottom--; + if (strcasecmp(s, "top") == 0) - y = 0; + y = top; else if (strcasecmp(s, "bottom") == 0) - y = w->sy - 1; + y = bottom; else if (strcasecmp(s, "left") == 0) x = 0; else if (strcasecmp(s, "right") == 0) x = w->sx - 1; else if (strcasecmp(s, "top-left") == 0) { x = 0; - y = 0; + y = top; } else if (strcasecmp(s, "top-right") == 0) { x = w->sx - 1; - y = 0; + y = top; } else if (strcasecmp(s, "bottom-left") == 0) { x = 0; - y = w->sy - 1; + y = bottom; } else if (strcasecmp(s, "bottom-right") == 0) { x = w->sx - 1; - y = w->sy - 1; + y = bottom; } else return (NULL); @@ -585,6 +637,28 @@ window_unzoom(struct window *w) return (0); } +int +window_push_zoom(struct window *w, int flag) +{ + log_debug("%s: @%u %d", __func__, w->id, + flag && (w->flags & WINDOW_ZOOMED)); + if (flag && (w->flags & WINDOW_ZOOMED)) + w->flags |= WINDOW_WASZOOMED; + else + w->flags &= ~WINDOW_WASZOOMED; + return (window_unzoom(w) == 0); +} + +int +window_pop_zoom(struct window *w) +{ + log_debug("%s: @%u %d", __func__, w->id, + !!(w->flags & WINDOW_WASZOOMED)); + if (w->flags & WINDOW_WASZOOMED) + return (window_zoom(w->active) == 0); + return (0); +} + struct window_pane * window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, int flags) @@ -933,7 +1007,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - wp->flags |= PANE_RESIZE; + wp->flags |= (PANE_RESIZE|PANE_RESIZED); } /* @@ -1173,7 +1247,7 @@ window_pane_reset_mode_all(struct window_pane *wp) window_pane_reset_mode(wp); } -void +int window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, key_code key, struct mouse_event *m) { @@ -1181,23 +1255,24 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct window_pane *wp2; if (KEYC_IS_MOUSE(key) && m == NULL) - return; + return (-1); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { wp->modelast = time(NULL); if (wme->mode->key != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); - return; + return (0); } if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) - return; + return (0); - input_key(wp, key, m); + if (input_key(wp, key, m) != 0) + return (-1); if (KEYC_IS_MOUSE(key)) - return; + return (0); if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 != wp && @@ -1208,6 +1283,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, input_key(wp2, key, NULL); } } + return (0); } int @@ -1542,31 +1618,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl) } static void -window_pane_input_callback(struct client *c, int closed, void *data) +window_pane_input_callback(struct client *c, __unused const char *path, + int error, int closed, struct evbuffer *buffer, void *data) { struct window_pane_input_data *cdata = data; struct window_pane *wp; - struct evbuffer *evb = c->stdin_data; - u_char *buf = EVBUFFER_DATA(evb); - size_t len = EVBUFFER_LENGTH(evb); + u_char *buf = EVBUFFER_DATA(buffer); + size_t len = EVBUFFER_LENGTH(buffer); wp = window_pane_find_by_id(cdata->wp); - if (wp == NULL || closed || c->flags & CLIENT_DEAD) { + if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) { if (wp == NULL) c->flags |= CLIENT_EXIT; - evbuffer_drain(evb, len); - - c->stdin_callback = NULL; - server_client_unref(c); + evbuffer_drain(buffer, len); cmdq_continue(cdata->item); - free(cdata); + server_client_unref(c); + free(cdata); return; } - input_parse_buffer(wp, buf, len); - evbuffer_drain(evb, len); + evbuffer_drain(buffer, len); } int @@ -1585,6 +1658,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, cdata->item = item; cdata->wp = wp->id; - return (server_set_stdin_callback(c, window_pane_input_callback, cdata, - cause)); + c->references++; + file_read(c, "-", window_pane_input_callback, cdata); + + return (0); } diff --git a/external/bsd/tmux/dist/xmalloc.h b/external/bsd/tmux/dist/xmalloc.h index 57d274073d05..f6454b9728e5 100644 --- a/external/bsd/tmux/dist/xmalloc.h +++ b/external/bsd/tmux/dist/xmalloc.h @@ -27,6 +27,7 @@ void *xmalloc(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); void *xreallocarray(void *, size_t, size_t); +void *xrecallocarray(void *, size_t, size_t, size_t); char *xstrdup(const char *); char *xstrndup(const char *, size_t); int xasprintf(char **, const char *, ...) diff --git a/external/bsd/tmux/usr.bin/tmux/Makefile b/external/bsd/tmux/usr.bin/tmux/Makefile index 810a5533da50..55d84e646f87 100644 --- a/external/bsd/tmux/usr.bin/tmux/Makefile +++ b/external/bsd/tmux/usr.bin/tmux/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.24 2020/01/06 21:03:24 christos Exp $ +# $NetBSD: Makefile,v 1.25 2020/11/01 15:16:05 christos Exp $ .include @@ -85,6 +85,7 @@ colour.c \ control-notify.c \ control.c \ environ.c \ +file.c \ format.c \ format-draw.c \ grid-view.c \ @@ -160,13 +161,11 @@ CPPFLAGS+= \ -DHAVE_ASPRINTF=1 \ -DHAVE_B64_NTOP=1 \ -DHAVE_BITSTRING_H=1 \ +-DHAVE_BSD_GETOPT=1 \ -DHAVE_CFMAKERAW=1 \ -DHAVE_CLOSEFROM=1 \ -DHAVE_CURSES_H=1 \ -DHAVE_DAEMON=1 \ --DHAVE_DECL_OPTARG=1 \ --DHAVE_DECL_OPTIND=1 \ --DHAVE_DECL_OPTRESET=1 \ -DHAVE_DIRENT_H=1 \ -DHAVE_FCNTL_CLOSEM=1 \ -DHAVE_FCNTL_H=1 \ @@ -209,13 +208,13 @@ CPPFLAGS+= \ -DPACKAGE=\"tmux\" \ -DPACKAGE_BUGREPORT=\"\" \ -DPACKAGE_NAME=\"tmux\" \ --DPACKAGE_STRING=\"tmux\ 3.0a\" \ +-DPACKAGE_STRING=\"tmux\ 3.1c\" \ -DPACKAGE_TARNAME=\"tmux\" \ -DPACKAGE_URL=\"\" \ --DPACKAGE_VERSION=\"3.0a\" \ +-DPACKAGE_VERSION=\"3.1c\" \ -DSTDC_HEADERS=1 \ --DTMUX_CONF="\"/etc/tmux.conf\"" \ --DVERSION=\"3.0a\" \ +-DTMUX_CONF="\"/etc/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" \ +-DTMUX_VERSION=\"3.1c\" \ -D_ALL_SOURCE=1 \ -D_GNU_SOURCE=1 \ -D_OPENBSD_SOURCE \